Skip to content

Commit 3dcf465

Browse files
committed
Coalesce Thumb IT blocks with trailing conditional branches
1 parent 0f56fdd commit 3dcf465

1 file changed

Lines changed: 219 additions & 72 deletions

File tree

arch/armv7/thumb2_disasm/arch_thumb2.cpp

Lines changed: 219 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,55 @@ using namespace std;
2020
#define snprintf _snprintf
2121
#endif
2222

23+
static bool IsConditionalBranch(const decomp_result& decomp)
24+
{
25+
return (decomp.mnem == ARMV7_B) && (decomp.format->operationFlags & INSTR_FORMAT_FLAG_CONDITIONAL)
26+
&& (decomp.fields[FIELD_cond] != COND_AL);
27+
}
28+
29+
static bool IsSameOrInvertedCondition(uint32_t lhs, uint32_t rhs)
30+
{
31+
return (lhs == rhs) || ((lhs < COND_AL) && (rhs < COND_AL) && ((lhs ^ 1) == rhs));
32+
}
33+
34+
static bool ThumbITInstructionWritesAPSR(const decomp_result& decomp)
35+
{
36+
switch (decomp.mnem)
37+
{
38+
case ARMV7_CMN:
39+
case ARMV7_CMP:
40+
case ARMV7_TEQ:
41+
case ARMV7_TST:
42+
return true;
43+
default:
44+
return false;
45+
}
46+
}
47+
48+
static uint32_t GetConditionalBranchTarget(const decomp_result& decomp)
49+
{
50+
return decomp.pc + decomp.fields[decomp.format->operands[0].field0];
51+
}
52+
53+
static void EmitDirectThumbJump(Architecture* arch, LowLevelILFunction& il, uint32_t target)
54+
{
55+
BNLowLevelILLabel* label = il.GetLabelForAddress(arch, target);
56+
if (label)
57+
il.AddInstruction(il.Goto(*label));
58+
else
59+
il.AddInstruction(il.Jump(il.ConstPointer(4, target)));
60+
}
61+
62+
struct ThumbITSlot
63+
{
64+
uint32_t addr = 0;
65+
decomp_result decomp = {};
66+
bool thenSlot = false;
67+
bool lift = false;
68+
bool directBranch = false;
69+
uint32_t branchTarget = 0;
70+
};
71+
2372
static Ref<Enumeration> get_msr_op_enum()
2473
{
2574
EnumerationBuilder builder;
@@ -167,7 +216,9 @@ class Thumb2Architecture: public ArmCommonArchitecture
167216

168217
virtual size_t GetMaxInstructionLength() const override
169218
{
170-
return 18; // IT blocks can have up to four following associated instructions
219+
// IT blocks can have up to four following associated instructions, and
220+
// may be coalesced with a following conditional branch.
221+
return 22;
171222
}
172223

173224
virtual size_t GetInstructionAlignment() const override
@@ -232,6 +283,8 @@ class Thumb2Architecture: public ArmCommonArchitecture
232283
bool falseBranched = false;
233284
bool trueReturned = false;
234285
bool falseReturned = false;
286+
bool trueWroteFlags = false;
287+
bool falseWroteFlags = false;
235288

236289
uint64_t trueBranchTargetAddr = 0;
237290
uint64_t falseBranchTargetAddr = 0;
@@ -240,56 +293,95 @@ class Thumb2Architecture: public ArmCommonArchitecture
240293
{
241294
bool isTrue = (i == 0) || (((mask >> (4 - i)) & 1) == (cond & 1));
242295

243-
InstructionInfo innerResult;
244-
if (!GetInstructionInfo(data + offset, addr + offset, maxLen - offset, innerResult))
296+
if (offset >= maxLen || (maxLen - offset) < 2)
297+
break;
298+
299+
decomp_result innerDecomp;
300+
size_t remainingLen = maxLen - offset;
301+
bool decoded = populateDecomposeRequest(&request, data + offset, remainingLen, addr + offset,
302+
IFTHEN_YES, ((i + 1) >= instrCount) ? IFTHENLAST_YES : IFTHENLAST_NO)
303+
&& (thumb_decompose(&request, &innerDecomp) == STATUS_OK)
304+
&& !(innerDecomp.status & STATUS_UNDEFINED) && innerDecomp.format;
305+
if (!decoded)
245306
break;
246-
if ((offset + innerResult.length) > maxLen)
307+
size_t innerLen = innerDecomp.instrSize / 8;
308+
if ((innerLen == 0) || (innerLen > remainingLen))
247309
break;
248310

249311
bool& terminated = isTrue ? trueTerminated : falseTerminated;
250312
bool& branched = isTrue ? trueBranched : falseBranched;
251313
bool& returned = isTrue ? trueReturned : falseReturned;
314+
bool& wroteFlags = isTrue ? trueWroteFlags : falseWroteFlags;
252315
uint64_t& branchTarget = isTrue ? trueBranchTargetAddr : falseBranchTargetAddr;
253316

254317
// Only process if the conditional branch we're following isn't terminated
255318
// Otherwise, just track if the arch is switching and the offset
256319
if (!terminated)
257320
{
258-
for (size_t j = 0; j < innerResult.branchCount; j++)
321+
wroteFlags |= ThumbITInstructionWritesAPSR(innerDecomp);
322+
323+
InstructionInfo innerResult;
324+
if (GetInstructionInfo(data + offset, addr + offset, remainingLen, innerResult))
259325
{
260-
switch (innerResult.branchType[j])
326+
for (size_t j = 0; j < innerResult.branchCount; j++)
261327
{
262-
case UnconditionalBranch:
263-
case TrueBranch:
264-
case FalseBranch:
265-
branched = true;
266-
terminated = true;
267-
branchTarget = innerResult.branchTarget[j];
268-
break;
269-
case FunctionReturn:
270-
returned = true;
271-
terminated = true;
272-
break;
273-
case CallDestination:
274-
result.AddBranch(CallDestination, innerResult.branchTarget[j],
275-
innerResult.branchArch[j] ? m_armArch : this);
276-
break;
277-
case UnresolvedBranch:
278-
case IndirectBranch:
279-
case ExceptionBranch:
280-
// We don't know the branch target so just set terminated
281-
terminated = true;
282-
break;
283-
default:
284-
break;
328+
switch (innerResult.branchType[j])
329+
{
330+
case UnconditionalBranch:
331+
case TrueBranch:
332+
case FalseBranch:
333+
branched = true;
334+
terminated = true;
335+
branchTarget = innerResult.branchTarget[j];
336+
break;
337+
case FunctionReturn:
338+
returned = true;
339+
terminated = true;
340+
break;
341+
case CallDestination:
342+
result.AddBranch(CallDestination, innerResult.branchTarget[j],
343+
innerResult.branchArch[j] ? m_armArch : this);
344+
break;
345+
case UnresolvedBranch:
346+
case IndirectBranch:
347+
case ExceptionBranch:
348+
// We don't know the branch target so just set terminated
349+
terminated = true;
350+
break;
351+
default:
352+
break;
353+
}
285354
}
355+
356+
if (innerResult.archTransitionByTargetAddr)
357+
result.archTransitionByTargetAddr = true;
286358
}
287359
}
288360

289-
if (innerResult.archTransitionByTargetAddr)
290-
result.archTransitionByTargetAddr = true;
361+
offset += innerLen;
362+
}
363+
364+
decomp_result branchDecomp;
365+
if ((offset < maxLen) && ((maxLen - offset) >= 2)
366+
&& populateDecomposeRequest(&request, data + offset, maxLen - offset, addr + offset, IFTHEN_NO, IFTHENLAST_NO)
367+
&& (thumb_decompose(&request, &branchDecomp) == STATUS_OK)
368+
&& ((offset + (branchDecomp.instrSize / 8)) <= maxLen)
369+
&& !(branchDecomp.status & STATUS_UNDEFINED) && branchDecomp.format && IsConditionalBranch(branchDecomp)
370+
&& IsSameOrInvertedCondition(branchDecomp.fields[FIELD_cond], cond))
371+
{
372+
bool branchOnTrue = branchDecomp.fields[FIELD_cond] == cond;
373+
bool& terminated = branchOnTrue ? trueTerminated : falseTerminated;
374+
bool& branched = branchOnTrue ? trueBranched : falseBranched;
375+
bool& wroteFlags = branchOnTrue ? trueWroteFlags : falseWroteFlags;
376+
uint64_t& branchTarget = branchOnTrue ? trueBranchTargetAddr : falseBranchTargetAddr;
291377

292-
offset += innerResult.length;
378+
if (!terminated && !wroteFlags)
379+
{
380+
branched = true;
381+
terminated = true;
382+
branchTarget = GetConditionalBranchTarget(branchDecomp);
383+
offset += branchDecomp.instrSize / 8;
384+
}
293385
}
294386

295387
result.length = offset;
@@ -2626,82 +2718,137 @@ class Thumb2Architecture: public ArmCommonArchitecture
26262718
uint32_t mask = decomp.fields[FIELD_mask];
26272719
uint32_t cond = decomp.fields[FIELD_firstcond];
26282720

2629-
// Calculate number of instructions
26302721
size_t instrCount;
2631-
if (decomp.fields[FIELD_mask] & 1)
2722+
if (mask & 1)
26322723
instrCount = 4;
2633-
else if (decomp.fields[FIELD_mask] & 2)
2724+
else if (mask & 2)
26342725
instrCount = 3;
2635-
else if (decomp.fields[FIELD_mask] & 4)
2726+
else if (mask & 4)
26362727
instrCount = 2;
26372728
else
26382729
instrCount = 1;
26392730

2640-
// decompose all instructions in the if-then block
2641-
vector<uint32_t> addrsTrue, addrsFalse;
2642-
vector<decomp_result> decompsTrue, decompsFalse;
2731+
// Decompose all instructions in the IT block and keep their original
2732+
// mask slot. A path may skip later slots once it has branched/returned.
2733+
vector<ThumbITSlot> slots;
2734+
bool pathTerminated[2] = {false, false};
2735+
bool pathHasBody[2] = {false, false};
2736+
bool pathWroteFlags[2] = {false, false};
26432737

26442738
for (size_t i = 0; i < instrCount; i++)
26452739
{
26462740
if (offset >= len || (len - offset) < 2)
26472741
return false;
26482742

2649-
bool isTrue = (i == 0) || (((mask >> (4 - i)) & 1) == (cond & 1));
2743+
bool thenSlot = (i == 0) || (((mask >> (4 - i)) & 1) == (cond & 1));
2744+
size_t stateIdx = thenSlot ? 0 : 1;
26502745
size_t remainingLen = len - offset;
26512746

2652-
if (!populateDecomposeRequest(&request, data+offset, remainingLen, addr+offset,
2653-
IFTHEN_YES, ((i + 1) >= instrCount) ? IFTHENLAST_YES : IFTHENLAST_NO))
2654-
return false;
2655-
2656-
if (thumb_decompose(&request, &decomp) != STATUS_OK)
2747+
bool decoded = populateDecomposeRequest(&request, data + offset, remainingLen, addr + offset,
2748+
IFTHEN_YES, ((i + 1) >= instrCount) ? IFTHENLAST_YES : IFTHENLAST_NO)
2749+
&& (thumb_decompose(&request, &decomp) == STATUS_OK)
2750+
&& !(decomp.status & STATUS_UNDEFINED) && decomp.format;
2751+
if (!decoded)
26572752
return false;
26582753
if ((decomp.instrSize / 8) > remainingLen)
26592754
return false;
2660-
if ((decomp.status & STATUS_UNDEFINED) || (!decomp.format))
2661-
return false;
26622755

2663-
if (isTrue) {
2664-
addrsTrue.push_back(request.addr);
2665-
decompsTrue.push_back(decomp);
2666-
}
2667-
else {
2668-
addrsFalse.push_back(request.addr);
2669-
decompsFalse.push_back(decomp);
2756+
ThumbITSlot slot;
2757+
slot.addr = request.addr;
2758+
slot.decomp = decomp;
2759+
slot.thenSlot = thenSlot;
2760+
slot.lift = !pathTerminated[stateIdx];
2761+
slots.push_back(slot);
2762+
pathHasBody[stateIdx] |= slot.lift;
2763+
2764+
if (slot.lift)
2765+
{
2766+
pathWroteFlags[stateIdx] |= ThumbITInstructionWritesAPSR(decomp);
2767+
2768+
InstructionInfo innerResult;
2769+
if (GetInstructionInfo(data + offset, addr + offset, remainingLen, innerResult))
2770+
{
2771+
for (size_t j = 0; j < innerResult.branchCount; j++)
2772+
{
2773+
switch (innerResult.branchType[j])
2774+
{
2775+
case UnconditionalBranch:
2776+
case TrueBranch:
2777+
case FalseBranch:
2778+
case FunctionReturn:
2779+
case UnresolvedBranch:
2780+
case IndirectBranch:
2781+
case ExceptionBranch:
2782+
pathTerminated[stateIdx] = true;
2783+
break;
2784+
default:
2785+
break;
2786+
}
2787+
}
2788+
}
26702789
}
26712790

26722791
offset += decomp.instrSize / 8;
26732792
}
26742793

2794+
decomp_result branchDecomp;
2795+
2796+
if ((offset < len) && ((len - offset) >= 2)
2797+
&& populateDecomposeRequest(&request, data + offset, len - offset, addr + offset, IFTHEN_NO, IFTHENLAST_NO)
2798+
&& (thumb_decompose(&request, &branchDecomp) == STATUS_OK)
2799+
&& ((offset + (branchDecomp.instrSize / 8)) <= len)
2800+
&& !(branchDecomp.status & STATUS_UNDEFINED) && branchDecomp.format && IsConditionalBranch(branchDecomp)
2801+
&& IsSameOrInvertedCondition(branchDecomp.fields[FIELD_cond], cond))
2802+
{
2803+
bool branchOnTrue = branchDecomp.fields[FIELD_cond] == cond;
2804+
size_t stateIdx = branchOnTrue ? 0 : 1;
2805+
if (!pathTerminated[stateIdx] && !pathWroteFlags[stateIdx])
2806+
{
2807+
ThumbITSlot slot;
2808+
slot.addr = request.addr;
2809+
slot.thenSlot = branchOnTrue;
2810+
slot.lift = true;
2811+
slot.directBranch = true;
2812+
slot.branchTarget = GetConditionalBranchTarget(branchDecomp);
2813+
slots.push_back(slot);
2814+
pathHasBody[stateIdx] = true;
2815+
pathTerminated[stateIdx] = true;
2816+
offset += branchDecomp.instrSize / 8;
2817+
}
2818+
}
2819+
26752820
// generate IL
26762821
LowLevelILLabel labelTrue, labelFalse, labelDone;
26772822

26782823
il.AddInstruction(il.If(GetCondition(il, cond), labelTrue, labelFalse));
26792824

2680-
// generate IL for "true" if-else members
2681-
il.MarkLabel(labelTrue);
2682-
2683-
for (size_t i = 0; i < decompsTrue.size(); i++)
2825+
auto liftPath = [&](bool thenPath, LowLevelILLabel& label)
26842826
{
2685-
il.SetCurrentAddress(this, addrsTrue[i]);
2686-
GetLowLevelILForThumbInstruction(this, il, &(decompsTrue[i]), true);
2687-
}
2827+
size_t stateIdx = thenPath ? 0 : 1;
26882828

2689-
if (decompsFalse.empty()) {
2690-
il.MarkLabel(labelFalse);
2691-
}
2692-
else {
2693-
il.AddInstruction(il.Goto(labelDone));
2694-
il.MarkLabel(labelFalse);
2829+
il.MarkLabel(label);
26952830

2696-
// generate IL for "false" if-else members
2697-
for (int i = 0; i < decompsFalse.size(); i++)
2831+
for (auto& slot : slots)
26982832
{
2699-
il.SetCurrentAddress(this, addrsFalse[i]);
2700-
GetLowLevelILForThumbInstruction(this, il, &(decompsFalse[i]), true);
2833+
if (!slot.lift || (slot.thenSlot != thenPath))
2834+
continue;
2835+
2836+
il.SetCurrentAddress(this, slot.addr);
2837+
if (slot.directBranch)
2838+
EmitDirectThumbJump(this, il, slot.branchTarget);
2839+
else
2840+
GetLowLevelILForThumbInstruction(this, il, &slot.decomp, true);
27012841
}
27022842

2843+
if (thenPath && pathHasBody[1] && !pathTerminated[stateIdx])
2844+
il.AddInstruction(il.Goto(labelDone));
2845+
};
2846+
2847+
liftPath(true, labelTrue);
2848+
liftPath(false, labelFalse);
2849+
2850+
if (pathHasBody[1])
27032851
il.MarkLabel(labelDone);
2704-
}
27052852

27062853
len = offset;
27072854
return true;

0 commit comments

Comments
 (0)