Skip to content

Commit c506d98

Browse files
committed
Add support for ternary simplification in Psuedo C and disable in graph view
1 parent 6eac30d commit c506d98

File tree

5 files changed

+128
-0
lines changed

5 files changed

+128
-0
lines changed

binaryninjaapi.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19745,6 +19745,10 @@ namespace BinaryNinja {
1974519745
BNBraceRequirement GetBraceRequirement() const;
1974619746
bool HasBracesAroundSwitchCases() const;
1974719747
bool GetDefaultBracesOnSameLine() const;
19748+
19749+
/*! Gets the maximum number of tokens to emit as a ternary operation. */
19750+
size_t GetMaxTernarySimplificationTokens() const;
19751+
1974819752
bool IsSimpleScopeAllowed() const;
1974919753

1975019754
/*! Gets the list of lines in the output. */

binaryninjacore.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8305,6 +8305,7 @@ extern "C"
83058305
BNHighLevelILTokenEmitter* emitter);
83068306
BINARYNINJACOREAPI bool BNHighLevelILTokenEmitterHasBracesAroundSwitchCases(BNHighLevelILTokenEmitter* emitter);
83078307
BINARYNINJACOREAPI bool BNHighLevelILTokenEmitterGetDefaultBracesOnSameLine(BNHighLevelILTokenEmitter* emitter);
8308+
BINARYNINJACOREAPI size_t BNHighLevelILTokenEmitterGetMaxTernarySimplficationTokens(BNHighLevelILTokenEmitter* emitter);
83088309
BINARYNINJACOREAPI bool BNHighLevelILTokenEmitterIsSimpleScopeAllowed(BNHighLevelILTokenEmitter* emitter);
83098310
BINARYNINJACOREAPI BNInstructionTextToken* BNHighLevelILTokenEmitterGetCurrentTokens(BNHighLevelILTokenEmitter* emitter, size_t* tokenCount);
83108311
BINARYNINJACOREAPI void BNHighLevelILTokenEmitterSetCurrentTokens(BNHighLevelILTokenEmitter* emitter, BNInstructionTextToken* tokens, size_t tokenCount);

highlevelil.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,11 @@ bool HighLevelILTokenEmitter::GetDefaultBracesOnSameLine() const
878878
return BNHighLevelILTokenEmitterGetDefaultBracesOnSameLine(m_object);
879879
}
880880

881+
size_t HighLevelILTokenEmitter::GetMaxTernarySimplificationTokens() const
882+
{
883+
return BNHighLevelILTokenEmitterGetMaxTernarySimplficationTokens(m_object);
884+
}
885+
881886

882887
bool HighLevelILTokenEmitter::IsSimpleScopeAllowed() const
883888
{

lang/c/pseudoc.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,103 @@ PseudoCFunction::FieldDisplayType PseudoCFunction::GetFieldDisplayType(
441441
return FieldDisplayNone;
442442
}
443443

444+
std::optional<PseudoCFunction::TernaryInfo> PseudoCFunction::CanSimplifyToTernary(const BinaryNinja::HighLevelILInstruction &instr) const
445+
{
446+
// Only handle if-statements
447+
if (instr.operation != HLIL_IF)
448+
return std::nullopt;
449+
450+
auto conditionExpr = instr.GetConditionExpr<HLIL_IF>();
451+
auto trueExpr = instr.GetTrueExpr<HLIL_IF>();
452+
auto falseExpr = instr.GetFalseExpr<HLIL_IF>();
453+
454+
if (GetHighLevelILFunction()->HasSideEffects(conditionExpr))
455+
return std::nullopt;
456+
// Both branches must be assignment operations
457+
if (trueExpr.operation != HLIL_ASSIGN || falseExpr.operation != HLIL_ASSIGN)
458+
return std::nullopt;
459+
460+
// Get the destination expressions of the assignments
461+
auto trueDestExpr = trueExpr.GetDestExpr<HLIL_ASSIGN>();
462+
auto falseDestExpr = falseExpr.GetDestExpr<HLIL_ASSIGN>();
463+
464+
// Verify that the destination expressions are variable references
465+
if (trueDestExpr.operation != HLIL_VAR || falseDestExpr.operation != HLIL_VAR)
466+
return std::nullopt;
467+
468+
auto trueExprDestExpr = trueExpr.GetDestExpr<HLIL_ASSIGN>();
469+
auto falseExprDestExpr = falseExpr.GetDestExpr<HLIL_ASSIGN>();
470+
if (trueExprDestExpr.operation != HLIL_VAR || falseExprDestExpr.operation != HLIL_VAR)
471+
return std::nullopt;
472+
473+
auto trueExprDestVar = trueExprDestExpr.GetVariable<HLIL_VAR>();
474+
auto falseExprDestVar = falseExprDestExpr.GetVariable<HLIL_VAR>();
475+
if (trueExprDestVar != falseExprDestVar)
476+
return std::nullopt;
477+
478+
auto trueExprSourceExpr = trueExpr.GetSourceExpr<HLIL_ASSIGN>();
479+
auto falseExprSourceExpr = falseExpr.GetSourceExpr<HLIL_ASSIGN>();
480+
if (GetHighLevelILFunction()->HasSideEffects(trueExprSourceExpr) || GetHighLevelILFunction()->HasSideEffects(falseExprSourceExpr))
481+
return std::nullopt;
482+
483+
// Avoid folding for "else if" cases
484+
for (auto parent = instr; parent.HasParent(); parent = parent.GetParent())
485+
{
486+
if (parent.operation != HLIL_IF)
487+
break;
488+
auto parentFalse = parent.GetFalseExpr<HLIL_IF>();
489+
if (parentFalse.operation == HLIL_IF)
490+
return std::nullopt;
491+
}
492+
493+
TernaryInfo info;
494+
info.conditional = conditionExpr;
495+
info.assignDest = trueDestExpr;
496+
info.trueAssign = trueExprSourceExpr;
497+
info.falseAssign = falseExprSourceExpr;
498+
return info;
499+
}
500+
501+
bool PseudoCFunction::TryEmitSimplifiedTernary(
502+
const BinaryNinja::HighLevelILInstruction &instr,
503+
DisassemblySettings* settings,
504+
BinaryNinja::HighLevelILTokenEmitter &emitter
505+
)
506+
{
507+
auto ternaryOpt = CanSimplifyToTernary(instr);
508+
if (!ternaryOpt.has_value())
509+
return false;
510+
511+
auto &[conditional, assignDest, trueAssign, falseAssign] = ternaryOpt.value();
512+
513+
std::vector<InstructionTextToken> tokens = emitter.GetCurrentTokens();
514+
size_t originalTokenCount = tokens.size();
515+
516+
// Emit the destination expression
517+
GetExprText(assignDest, emitter, settings, AssignmentOperatorPrecedence);
518+
emitter.Append(OperationToken, " = ");
519+
520+
// Emit the condition expression
521+
GetExprText(conditional, emitter, settings, TernaryOperatorPrecedence);
522+
emitter.Append(OperationToken, " ? ");
523+
524+
// Emit the true-source expression
525+
GetExprText(trueAssign, emitter, settings, TernaryOperatorPrecedence);
526+
emitter.Append(OperationToken, " : ");
527+
528+
// Emit the false-source expression
529+
GetExprText(falseAssign, emitter, settings, TernaryOperatorPrecedence);
530+
emitter.Append(KeywordToken, ";");
531+
532+
// If the ternary expression is too complex (i.e. too many tokens), revert back.
533+
if (tokens.size() - originalTokenCount > emitter.GetMaxTernarySimplificationTokens())
534+
{
535+
tokens.resize(originalTokenCount);
536+
return false;
537+
}
538+
return true;
539+
}
540+
444541

445542
void PseudoCFunction::AppendDefaultSplitExpr(const BinaryNinja::HighLevelILInstruction& instr,
446543
BinaryNinja::HighLevelILTokenEmitter& tokens, DisassemblySettings* settings, BNOperatorPrecedence precedence)
@@ -671,6 +768,9 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H
671768
case HLIL_IF:
672769
[&]()
673770
{
771+
if (instr.ast && TryEmitSimplifiedTernary(instr, settings, tokens))
772+
return;
773+
674774
const auto condExpr = instr.GetConditionExpr<HLIL_IF>();
675775
const auto trueExpr = instr.GetTrueExpr<HLIL_IF>();
676776
const auto falseExpr = instr.GetFalseExpr<HLIL_IF>();

lang/c/pseudoc.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include "binaryninjaapi.h"
4+
#include "highlevelilinstruction.h"
45

56
class PseudoCFunction: public BinaryNinja::LanguageRepresentationFunction
67
{
@@ -15,6 +16,23 @@ class PseudoCFunction: public BinaryNinja::LanguageRepresentationFunction
1516
FieldDisplayNone
1617
};
1718

19+
struct TernaryInfo
20+
{
21+
BinaryNinja::HighLevelILInstruction conditional;
22+
BinaryNinja::HighLevelILInstruction assignDest;
23+
BinaryNinja::HighLevelILInstruction trueAssign;
24+
BinaryNinja::HighLevelILInstruction falseAssign;
25+
};
26+
27+
std::optional<PseudoCFunction::TernaryInfo> CanSimplifyToTernary(
28+
const BinaryNinja::HighLevelILInstruction& instr
29+
) const;
30+
bool TryEmitSimplifiedTernary(
31+
const BinaryNinja::HighLevelILInstruction& instr,
32+
BinaryNinja::DisassemblySettings* settings,
33+
BinaryNinja::HighLevelILTokenEmitter& emitter
34+
);
35+
1836
BinaryNinja::Ref<BinaryNinja::Type> GetFieldType(const BinaryNinja::HighLevelILInstruction& var, bool deref);
1937
FieldDisplayType GetFieldDisplayType(BinaryNinja::Ref<BinaryNinja::Type> type, uint64_t offset, size_t memberIndex, bool deref);
2038

0 commit comments

Comments
 (0)