Skip to content

Commit 4072c10

Browse files
committed
Add support for ternary simplification in Psuedo C and disable in graph view
1 parent 99965f6 commit 4072c10

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
@@ -19785,6 +19785,10 @@ namespace BinaryNinja {
1978519785
BNBraceRequirement GetBraceRequirement() const;
1978619786
bool HasBracesAroundSwitchCases() const;
1978719787
bool GetDefaultBracesOnSameLine() const;
19788+
19789+
/*! Gets the maximum number of tokens to emit as a ternary operation. */
19790+
size_t GetMaxTernarySimplificationTokens() const;
19791+
1978819792
bool IsSimpleScopeAllowed() const;
1978919793

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

binaryninjacore.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8312,6 +8312,7 @@ extern "C"
83128312
BNHighLevelILTokenEmitter* emitter);
83138313
BINARYNINJACOREAPI bool BNHighLevelILTokenEmitterHasBracesAroundSwitchCases(BNHighLevelILTokenEmitter* emitter);
83148314
BINARYNINJACOREAPI bool BNHighLevelILTokenEmitterGetDefaultBracesOnSameLine(BNHighLevelILTokenEmitter* emitter);
8315+
BINARYNINJACOREAPI size_t BNHighLevelILTokenEmitterGetMaxTernarySimplficationTokens(BNHighLevelILTokenEmitter* emitter);
83158316
BINARYNINJACOREAPI bool BNHighLevelILTokenEmitterIsSimpleScopeAllowed(BNHighLevelILTokenEmitter* emitter);
83168317
BINARYNINJACOREAPI BNInstructionTextToken* BNHighLevelILTokenEmitterGetCurrentTokens(BNHighLevelILTokenEmitter* emitter, size_t* tokenCount);
83178318
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
@@ -442,6 +442,103 @@ PseudoCFunction::FieldDisplayType PseudoCFunction::GetFieldDisplayType(
442442
return FieldDisplayNone;
443443
}
444444

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

446543
void PseudoCFunction::AppendDefaultSplitExpr(const BinaryNinja::HighLevelILInstruction& instr,
447544
BinaryNinja::HighLevelILTokenEmitter& tokens, DisassemblySettings* settings, BNOperatorPrecedence precedence)
@@ -672,6 +769,9 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H
672769
case HLIL_IF:
673770
[&]()
674771
{
772+
if (instr.ast && TryEmitSimplifiedTernary(instr, settings, tokens))
773+
return;
774+
675775
const auto condExpr = instr.GetConditionExpr<HLIL_IF>();
676776
const auto trueExpr = instr.GetTrueExpr<HLIL_IF>();
677777
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)