Skip to content

Commit 49dc635

Browse files
committed
Add support for ternary simplification in Psuedo C and disable in graph view
1 parent 46e058a commit 49dc635

File tree

5 files changed

+127
-0
lines changed

5 files changed

+127
-0
lines changed

binaryninjaapi.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19567,6 +19567,10 @@ namespace BinaryNinja {
1956719567
BNBraceRequirement GetBraceRequirement() const;
1956819568
bool HasBracesAroundSwitchCases() const;
1956919569
bool GetDefaultBracesOnSameLine() const;
19570+
19571+
/*! Gets the maximum number of tokens to emit as a ternary operation. */
19572+
size_t GetMaxTernarySimplificationTokens() const;
19573+
1957019574
bool IsSimpleScopeAllowed() const;
1957119575

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

binaryninjacore.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8222,6 +8222,7 @@ extern "C"
82228222
BNHighLevelILTokenEmitter* emitter);
82238223
BINARYNINJACOREAPI bool BNHighLevelILTokenEmitterHasBracesAroundSwitchCases(BNHighLevelILTokenEmitter* emitter);
82248224
BINARYNINJACOREAPI bool BNHighLevelILTokenEmitterGetDefaultBracesOnSameLine(BNHighLevelILTokenEmitter* emitter);
8225+
BINARYNINJACOREAPI size_t BNHighLevelILTokenEmitterGetMaxTernarySimplficationTokens(BNHighLevelILTokenEmitter* emitter);
82258226
BINARYNINJACOREAPI bool BNHighLevelILTokenEmitterIsSimpleScopeAllowed(BNHighLevelILTokenEmitter* emitter);
82268227
BINARYNINJACOREAPI BNInstructionTextToken* BNHighLevelILTokenEmitterGetCurrentTokens(BNHighLevelILTokenEmitter* emitter, size_t* tokenCount);
82278228
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: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,102 @@ 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+
531+
// If the ternary expression is too complex (i.e. too many tokens), revert back.
532+
if (tokens.size() - originalTokenCount > emitter.GetMaxTernarySimplificationTokens())
533+
{
534+
tokens.resize(originalTokenCount);
535+
return false;
536+
}
537+
return true;
538+
}
539+
444540

445541
void PseudoCFunction::AppendDefaultSplitExpr(const BinaryNinja::HighLevelILInstruction& instr,
446542
BinaryNinja::HighLevelILTokenEmitter& tokens, DisassemblySettings* settings, BNOperatorPrecedence precedence)
@@ -671,6 +767,9 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H
671767
case HLIL_IF:
672768
[&]()
673769
{
770+
if (instr.ast && TryEmitSimplifiedTernary(instr, settings, tokens))
771+
return;
772+
674773
const auto condExpr = instr.GetConditionExpr<HLIL_IF>();
675774
const auto trueExpr = instr.GetTrueExpr<HLIL_IF>();
676775
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)