Skip to content

Commit 4a74c39

Browse files
committed
Memoize reverse expr lookups when building the LLIL-SSA <-> MLIL expr map
`GetMediumLevelILExprIndexes` now derives its result by walking the LLIL-SSA use-def graph rather than reading precomputed data. `GetLLILSSAToMLILExprMap` queries it once per (MLIL expr, LLIL-SSA expr) pair, and the same LLIL-SSA expr recurs across many MLIL exprs, so a whole-function map build repeats the same walk many times over. Cache each expr's reverse result for the life of one map build.
1 parent daa0870 commit 4a74c39

2 files changed

Lines changed: 58 additions & 8 deletions

File tree

mediumlevelil.cpp

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,37 @@
2424
using namespace BinaryNinja;
2525
using namespace std;
2626

27+
namespace {
28+
29+
// Memoizes the reverse (LLIL-SSA -> MLIL) expr lookups for one LLIL-SSA function.
30+
// The same LLIL-SSA expr is reached from many MLIL exprs, and a reverse query
31+
// derives its result by walking the LLIL-SSA use-def graph rather than reading a
32+
// stored table, so repeating it for each recurrence is what dominates a
33+
// whole-function map build.
34+
class ReverseExprMemo
35+
{
36+
Ref<LowLevelILFunction> m_llilSsa;
37+
std::unordered_map<size_t, std::pair<size_t, std::set<size_t>>> m_entries;
38+
39+
public:
40+
explicit ReverseExprMemo(Ref<LowLevelILFunction> llilSsa) : m_llilSsa(std::move(llilSsa)) {}
41+
42+
// The MLIL expr that `llilSsaExpr` maps to directly, paired with every MLIL expr it maps to.
43+
const std::pair<size_t, std::set<size_t>>& Get(size_t llilSsaExpr)
44+
{
45+
auto entry = m_entries.find(llilSsaExpr);
46+
if (entry == m_entries.end())
47+
{
48+
entry = m_entries.emplace(llilSsaExpr,
49+
std::make_pair(m_llilSsa->GetMediumLevelILExprIndex(llilSsaExpr), m_llilSsa->GetMediumLevelILExprIndexes(llilSsaExpr))
50+
).first;
51+
}
52+
return entry->second;
53+
}
54+
};
55+
56+
} // unnamed namespace
57+
2758

2859
ILSourceLocation::ILSourceLocation(const struct MediumLevelILInstruction& instr):
2960
address(instr.address), sourceOperand(instr.sourceOperand), valid(true),
@@ -117,12 +148,14 @@ std::unordered_map<size_t /* llil ssa */, size_t /* mlil */> MediumLevelILFuncti
117148
std::vector<BNExprMapInfo> MediumLevelILFunction::GetLLILSSAToMLILExprMap(bool fromTranslation)
118149
{
119150
std::vector<BNExprMapInfo> result;
151+
120152
if (fromTranslation)
121153
{
122154
// TODO: Handle LLIL SSA -> MLIL mappings in case someone is brave enough to try
123155
// lifting LLILSSA->MLIL themselves instead of an MLIL->MLIL translation
124156
// (which is the only one I've seen people do so far)
125157

158+
ReverseExprMemo reverse(m_translationData->copyingFunction->GetLowLevelIL()->GetSSAForm());
126159
for (auto& [oldExprIndex, newExprIndices]: m_translationData->mlilToMlilExprMap)
127160
{
128161
// Look up the LLIL SSA expression for the old expr in its function
@@ -132,8 +165,7 @@ std::vector<BNExprMapInfo> MediumLevelILFunction::GetLLILSSAToMLILExprMap(bool f
132165
auto oldLLILSSAIndices = m_translationData->copyingFunction->GetLowLevelILExprIndexes(oldExprIndex);
133166
for (auto& oldLLILSSAIndex: oldLLILSSAIndices)
134167
{
135-
size_t oldReverseDirect = m_translationData->copyingFunction->GetLowLevelIL()->GetSSAForm()->GetMediumLevelILExprIndex(oldLLILSSAIndex);
136-
auto oldReverseAll = m_translationData->copyingFunction->GetLowLevelIL()->GetSSAForm()->GetMediumLevelILExprIndexes(oldLLILSSAIndex);
168+
const auto& [oldReverseDirect, oldReverseAll] = reverse.Get(oldLLILSSAIndex);
137169
for (auto& [newExprIndex, newDirect]: newExprIndices)
138170
{
139171
BNExprMapInfo info;
@@ -150,6 +182,7 @@ std::vector<BNExprMapInfo> MediumLevelILFunction::GetLLILSSAToMLILExprMap(bool f
150182
}
151183
else
152184
{
185+
ReverseExprMemo reverse(GetLowLevelIL()->GetSSAForm());
153186
for (auto& block: GetBasicBlocks())
154187
{
155188
for (size_t instrIndex = block->GetStart(); instrIndex < block->GetEnd(); instrIndex++)
@@ -160,8 +193,7 @@ std::vector<BNExprMapInfo> MediumLevelILFunction::GetLLILSSAToMLILExprMap(bool f
160193
auto llilSSAIndices = GetLowLevelILExprIndexes(expr.exprIndex);
161194
for (auto& llilSSAIndex: llilSSAIndices)
162195
{
163-
size_t reverseDirect = GetLowLevelIL()->GetSSAForm()->GetMediumLevelILExprIndex(llilSSAIndex);
164-
auto reverseAll = GetLowLevelIL()->GetSSAForm()->GetMediumLevelILExprIndexes(llilSSAIndex);
196+
const auto& [reverseDirect, reverseAll] = reverse.Get(llilSSAIndex);
165197

166198
BNExprMapInfo info;
167199
info.lowerIndex = llilSSAIndex;

python/mediumlevelil.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4511,6 +4511,24 @@ def _get_llil_ssa_to_mlil_instr_map(self, from_builders: bool) -> LLILSSAToMLILI
45114511
def _get_llil_ssa_to_mlil_expr_map(self, from_builders: bool) -> LLILSSAToMLILExpressionMapping:
45124512
llil_ssa_to_mlil_expr_map = []
45134513

4514+
# Memoize the reverse (LLIL-SSA -> MLIL) expr lookups, per LLIL-SSA function. The same LLIL-SSA expr is
4515+
# reached from many MLIL exprs, and a reverse query derives its result by walking the LLIL-SSA use-def graph
4516+
# rather than reading a stored table, so repeating it for each recurrence is what dominates a whole-function
4517+
# map build.
4518+
reverse_memo: Dict['lowlevelil.LowLevelILFunction', Dict[ExpressionIndex, Tuple[
4519+
Optional[ExpressionIndex], List[ExpressionIndex]]]] = {}
4520+
4521+
def reverse_lookup(llil_ssa, llil_ssa_expr):
4522+
entries = reverse_memo.setdefault(llil_ssa, {})
4523+
entry = entries.get(llil_ssa_expr)
4524+
if entry is None:
4525+
entry = (
4526+
llil_ssa.get_medium_level_il_expr_index(llil_ssa_expr),
4527+
llil_ssa.get_medium_level_il_expr_indexes(llil_ssa_expr)
4528+
)
4529+
entries[llil_ssa_expr] = entry
4530+
return entry
4531+
45144532
if from_builders:
45154533
# TODO: Handle LLIL SSA -> MLIL mappings in case someone is brave enough to try
45164534
# lifting LLILSSA->MLIL themselves instead of an MLIL->MLIL translation
@@ -4523,11 +4541,11 @@ def _get_llil_ssa_to_mlil_expr_map(self, from_builders: bool) -> LLILSSAToMLILEx
45234541
# Look up the LLIL SSA expression for the old expr in its function
45244542
# And then store that mapping for the new function
45254543

4544+
old_llil_ssa = old_expr.function.low_level_il.ssa_form
45264545
old_llil_ssa_direct = old_expr.function.get_low_level_il_expr_index(old_expr.expr_index)
45274546
old_llil_ssa_indices = old_expr.function.get_low_level_il_expr_indexes(old_expr.expr_index)
45284547
for old_index in old_llil_ssa_indices:
4529-
old_reverse_direct = old_expr.function.low_level_il.ssa_form.get_medium_level_il_expr_index(old_index)
4530-
old_reverse_all = old_expr.function.low_level_il.ssa_form.get_medium_level_il_expr_indexes(old_index)
4548+
old_reverse_direct, old_reverse_all = reverse_lookup(old_llil_ssa, old_index)
45314549

45324550
for (new_index, new_direct) in new_indices:
45334551
lower_to_higher_direct = new_direct and old_reverse_direct == old_expr.expr_index
@@ -4544,13 +4562,13 @@ def _get_llil_ssa_to_mlil_expr_map(self, from_builders: bool) -> LLILSSAToMLILEx
45444562
higher_to_lower_direct
45454563
))
45464564
else:
4565+
llil_ssa = self.low_level_il.ssa_form
45474566
for instr in self.instructions:
45484567
for expr in instr.traverse(lambda e: e):
45494568
llil_ssa_direct = self.get_low_level_il_expr_index(expr.expr_index)
45504569
llil_ssa_indices = self.get_low_level_il_expr_indexes(expr.expr_index)
45514570
for llil_ssa_index in llil_ssa_indices:
4552-
reverse_direct = self.low_level_il.ssa_form.get_medium_level_il_expr_index(llil_ssa_index)
4553-
reverse_all = self.low_level_il.ssa_form.get_medium_level_il_expr_indexes(llil_ssa_index)
4571+
reverse_direct, reverse_all = reverse_lookup(llil_ssa, llil_ssa_index)
45544572

45554573
lower_to_higher_direct = reverse_direct == expr.expr_index
45564574
higher_to_lower_direct = llil_ssa_index == llil_ssa_direct

0 commit comments

Comments
 (0)