Skip to content

Commit c0ae543

Browse files
committed
[EVM] Clear the branch condition in removeBranch for stackified code
This fixes the issue #150. Consider MBBs that end with either: JUMPI JUMP or JUMPI When removeBranch() is called, it removes all jump instructions. In stackified code, this disrupts the stack layout because it leaves an unused condition value on top of the stack. To handle this, we introduced a new codegen-only instruction called POP_COND_S, which is replaced with POP_S in the AsmPrinter. This instruction is inserted at the end of the MBBk after removing the conditional branch, ensuring that the unused condition is properly cleared and the stack layout remains consistent. In most cases, removeBranch() is immediately followed by insertBranch(), which typically reintroduces a JUMPI instruction, possibly with a different target. In such scenarios, the condition will again be consumed by the new JUMPI, making POP_COND_S unnecessary. Therefore, it is removed in insertBranch(). Although this approach introduces a minor overhead (creating and then removing POP_COND_S), the impact is negligible since it only applies to stackified code. In the rare cases where removeBranch() is invoked without a subsequent insertBranch(), the POP_COND_S instruction remains and is emitted as a regular POP instruction. This occurs, for example, in several places within the BranchFolder pass. Alternative approaches considered: 1. Identify all locations where removeBranch() is used alone and disable them for stackified code. This doesn't look practical and reliable. 2: Modify analyzeBranch() to skip analyzing basic block terminators when both the JUMPI and JUMP (or fallthrough) targets lead to the same block. This would prevent the logic that calls removeBranch() from triggering. This approach also doesn't look reliable.
1 parent 8e7767f commit c0ae543

File tree

4 files changed

+223
-5
lines changed

4 files changed

+223
-5
lines changed

llvm/lib/Target/EVM/EVMAsmPrinter.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,12 @@ void EVMAsmPrinter::emitInstruction(const MachineInstr *MI) {
237237
case EVM::LOADIMMUTABLE_S:
238238
emitLoadImmutableLabel(MI);
239239
return;
240+
case EVM::POP_COND_S: {
241+
MCInst Pop;
242+
Pop.setOpcode(EVM::POP_S);
243+
EmitToStreamer(*OutStreamer, Pop);
244+
return;
245+
}
240246
}
241247

242248
MCInst TmpInst;

llvm/lib/Target/EVM/EVMInstrInfo.cpp

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,20 +173,58 @@ unsigned EVMInstrInfo::removeBranch(MachineBasicBlock &MBB,
173173
LLVM_DEBUG(dbgs() << "Removing branches out of " << printMBBReference(MBB)
174174
<< '\n');
175175

176-
unsigned Count = 0;
177176
// Only remove branches from the end of the MBB.
178-
for (auto I = MBB.rbegin(); I != MBB.rend() && I->isBranch(); ++Count) {
179-
177+
unsigned Count = 0;
178+
bool HasConditionalBranch = false;
179+
MachineBasicBlock::iterator I = MBB.end();
180+
while (I != MBB.begin()) {
181+
--I;
182+
if (I->isDebugInstr())
183+
continue;
184+
if (!I->isTerminator())
185+
break;
186+
187+
HasConditionalBranch |= I->isConditionalBranch();
180188
#ifndef NDEBUG
181189
if (I->isUnconditionalBranch())
182190
assert(!Count && "Malformed basic block: unconditional branch is not the "
183191
"last instruction in the block");
184192
#endif // NDEBUG
185193

186194
LLVM_DEBUG(dbgs() << "Removing branch: "; I->dump());
187-
MBB.erase(&MBB.back());
188-
I = MBB.rbegin();
195+
I->eraseFromParent();
196+
I = MBB.end();
197+
++Count;
189198
}
199+
200+
// Let's consider basic blocks that end with
201+
//
202+
// JUMPI
203+
// JUMP
204+
//
205+
// or
206+
//
207+
// JUMPI
208+
//
209+
// When removeBranch() is called, it removes all jump instructions.
210+
// In stackified code, this can disrupt the stack layout by leaving an unused
211+
// condition on top of the stack, a condition that would normally be consumed
212+
// by the JUMPI instruction.
213+
// To handle this, we add a POP_COND_S instruction at the end of the BB after
214+
// removing the conditional branch, ensuring that the unused condition is
215+
// properly consumed:
216+
// In most cases, removeBranch() is followed by insertBranch(), which
217+
// typically reinserts a JUMPI instruction, possibly with a different target.
218+
// In such cases, the condition will again be consumed by the new JUMPI, so
219+
// the POP_COND_S is no longer needed and is removed. Although this adds a
220+
// small overhead, by creating and then removing POP_COND_S, the cost is
221+
// negligible since it only affects stackified code.
222+
//
223+
const bool IsStackified =
224+
MBB.getParent()->getInfo<EVMMachineFunctionInfo>()->getIsStackified();
225+
if (IsStackified && HasConditionalBranch)
226+
BuildMI(&MBB, DebugLoc(), get(EVM::POP_COND_S));
227+
190228
return Count;
191229
}
192230

@@ -215,6 +253,14 @@ unsigned EVMInstrInfo::insertBranch(MachineBasicBlock &MBB,
215253
else
216254
CondOpc = Cond[0].getImm() ? EVM::PseudoJUMPI : EVM::PseudoJUMP_UNLESS;
217255

256+
// Remove the POP_COND_S instruction previously added by removeBranch,
257+
// as the newly inserted conditional branch will now consume that
258+
// condition.
259+
auto Last = MBB.getLastNonDebugInstr();
260+
if (IsStackified && Last != MBB.end() &&
261+
Last->getOpcode() == EVM::POP_COND_S)
262+
MBB.erase(*Last);
263+
218264
auto NewMI = BuildMI(&MBB, DL, get(CondOpc)).addMBB(TBB);
219265

220266
// Add a condition operand, if we are not in stackified form.

llvm/lib/Target/EVM/EVMInstrInfo.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,11 @@ let hasSideEffects = 1 in {
830830

831831
defm POP : I<(outs), (ins), [], "POP", "", 0x50, 2>;
832832

833+
// This instruction is a regular POP operation that indicates it clears the
834+
// branch condition from the top of the stack.
835+
let isCodeGenOnly = 1 in
836+
def POP_COND_S : NI<(outs), (ins), [], true, "POP", 0x50, 2>;
837+
833838
foreach I = {1-16} in {
834839
defm DUP#I : I<(outs), (ins), [],
835840
"DUP"#I, "", !add(I, 0x7F), 3>;
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# RUN: llc -x mir -run-pass=branch-folder < %s | FileCheck %s
2+
# RUN: llc -x mir -start-after=evm-finalize-stack-frames < %s | FileCheck --check-prefix=ASM %s
3+
4+
# Test that after branch folding, the condition at the top of
5+
# the stack is correctly cleared when a conditional branch is removed.
6+
7+
--- |
8+
; ModuleID = 'test.ll'
9+
source_filename = "test.ll"
10+
target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256"
11+
target triple = "evm"
12+
13+
define void @test() {
14+
entry:
15+
%calldata_load_result.i21 = load i256, ptr addrspace(2) null, align 4
16+
%calldata_load_result.i27 = load i256, ptr addrspace(2) inttoptr (i256 32 to ptr addrspace(2)), align 4
17+
%calldata_load_result.i35 = load i256, ptr addrspace(2) inttoptr (i256 64 to ptr addrspace(2)), align 4
18+
%comparison_result111.i = icmp eq i256 %calldata_load_result.i35, 0
19+
br i1 %comparison_result111.i, label %if_main114.i, label %if_main215.i
20+
21+
if_main114.i: ; preds = %entry
22+
%0 = or i256 %calldata_load_result.i21, %calldata_load_result.i27
23+
%or.cond119 = icmp eq i256 %0, 0
24+
br i1 %or.cond119, label %if_main.i.i5, label %fun_check_entrypoint_396.exit
25+
26+
if_main215.i: ; preds = %entry
27+
%1 = or i256 %calldata_load_result.i21, %calldata_load_result.i27
28+
%or.cond118 = icmp eq i256 %1, 0
29+
br i1 %or.cond118, label %if_main.i.i5, label %fun_check_entrypoint_396.exit
30+
31+
if_main.i.i5: ; preds = %if_main215.i, %if_main114.i
32+
call void @llvm.evm.revert(ptr addrspace(1) null, i256 0)
33+
unreachable
34+
35+
fun_check_entrypoint_396.exit: ; preds = %if_main215.i, %if_main114.i
36+
call void @llvm.evm.revert(ptr addrspace(1) null, i256 32)
37+
unreachable
38+
}
39+
40+
declare void @llvm.evm.revert(ptr addrspace(1), i256)
41+
42+
...
43+
# CHECK-LABEL: name: test
44+
# CHECK: POP_S
45+
# CHECK-NEXT: PUSH1_S i256 64
46+
# CHECK-NEXT: CALLDATALOAD_S
47+
# CHECK-NEXT: POP_COND_S
48+
# CHECK-NEXT: PUSH1_S i256 32
49+
# CHECK-NEXT: CALLDATALOAD_S
50+
# CHECK-NEXT: PUSH0_S
51+
# CHECK-NEXT: CALLDATALOAD_S
52+
# CHECK-NEXT: OR_S
53+
# CHECK-NEXT: PseudoJUMP_UNLESS %bb.2
54+
# CHECK-LABEL: bb.1.fun_check_entrypoint_396.exit:
55+
# CHECK-NEXT: PUSH1_S i256 32
56+
# CHECK-NEXT: PUSH0_S
57+
# CHECK-NEXT: REVERT_S
58+
# CHECK-LABEL: bb.2.if_main.i.i5:
59+
# CHECK-NEXT: PUSH0_S
60+
# CHECK-NEXT: PUSH0_S
61+
# CHECK-NEXT: REVERT_S
62+
63+
# ASM-LABEL: test:
64+
# ASM: JUMPDEST
65+
# ASM-NEXT: POP
66+
# ASM-NEXT: PUSH1 0x40
67+
# ASM-NEXT: CALLDATALOAD
68+
# ASM-NEXT: POP
69+
# ASM-NEXT: PUSH1 0x20
70+
# ASM-NEXT: CALLDATALOAD
71+
# ASM-NEXT: PUSH0
72+
# ASM-NEXT: CALLDATALOAD
73+
# ASM-NEXT: OR
74+
# ASM-NEXT: ISZERO
75+
# ASM-NEXT: PUSH4 @.BB0_2
76+
# ASM-NEXT: JUMPI
77+
# ASM-LABEL: ; %bb.1:
78+
# ASM-NEXT: PUSH1 0x20
79+
# ASM-NEXT: PUSH0
80+
# ASM-NEXT: REVERT
81+
# ASM-LABEL: .BB0_2:
82+
# ASM-NEXT: JUMPDEST
83+
# ASM-NEXT: PUSH0
84+
# ASM-NEXT: PUSH0
85+
# ASM-NEXT: REVERT
86+
---
87+
name: test
88+
alignment: 1
89+
machineFunctionInfo:
90+
isStackified: true
91+
numberOfParameters: 0
92+
hasPushDeployAddress: false
93+
body: |
94+
bb.0.entry:
95+
successors: %bb.1(0x40000000), %bb.3(0x40000000); %bb.1(50.00%), %bb.3(50.00%)
96+
97+
POP_S
98+
PUSH1_S i256 64
99+
CALLDATALOAD_S
100+
PseudoJUMPI %bb.3
101+
102+
bb.1.if_main114.i:
103+
; predecessors: %bb.0
104+
successors: %bb.8(0x40000000), %bb.6(0x40000000); %bb.8(50.00%), %bb.6(50.00%)
105+
106+
PUSH1_S i256 32
107+
CALLDATALOAD_S
108+
PUSH0_S
109+
CALLDATALOAD_S
110+
OR_S
111+
PseudoJUMPI %bb.6
112+
113+
bb.8:
114+
; predecessors: %bb.1
115+
successors: %bb.4(0x80000000); %bb.4(100.00%)
116+
117+
PseudoJUMP %bb.4
118+
119+
bb.6:
120+
; predecessors: %bb.1
121+
successors: %bb.2(0x80000000); %bb.2(100.00%)
122+
123+
124+
bb.2.fun_check_entrypoint_396.exit:
125+
; predecessors: %bb.5, %bb.6
126+
127+
PUSH1_S i256 32
128+
PUSH0_S
129+
REVERT_S
130+
131+
bb.3.if_main215.i:
132+
; predecessors: %bb.0
133+
successors: %bb.7(0x40000000), %bb.5(0x40000000); %bb.7(50.00%), %bb.5(50.00%)
134+
135+
PUSH1_S i256 32
136+
CALLDATALOAD_S
137+
PUSH0_S
138+
CALLDATALOAD_S
139+
OR_S
140+
PseudoJUMPI %bb.5
141+
142+
bb.7:
143+
; predecessors: %bb.3
144+
successors: %bb.4(0x80000000); %bb.4(100.00%)
145+
146+
PseudoJUMP %bb.4
147+
148+
bb.5:
149+
; predecessors: %bb.3
150+
successors: %bb.2(0x80000000); %bb.2(100.00%)
151+
152+
PseudoJUMP %bb.2
153+
154+
bb.4.if_main.i.i5:
155+
; predecessors: %bb.7, %bb.8
156+
157+
PUSH0_S
158+
PUSH0_S
159+
REVERT_S
160+
161+
...

0 commit comments

Comments
 (0)