Skip to content

Commit 8df30d9

Browse files
committed
[WebAssembly] Do not omit range checks for i64 switches
Summary: Since the br_table instruction takes an i32, switches over i64s (and larger integers) must use the i32.wrap_i64 instruction to truncate the table index. This truncation makes numbers just over 2^32 indistinguishable from small numbers, so it was a miscompilation to omit the range check preceding these br_tables. This change fixes the problem by skipping the "fixing" of the br_table when the range check is an i64 instruction. Fixes PR46447. Reviewers: aheejin, dschuff, kripken Reviewed By: kripken Subscribers: sbc100, jgravelle-google, hiraditya, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D83017
1 parent 1c6e2ec commit 8df30d9

File tree

3 files changed

+86
-21
lines changed

3 files changed

+86
-21
lines changed

llvm/lib/Target/WebAssembly/WebAssemblyFixBrTableDefaults.cpp

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,25 @@ class WebAssemblyFixBrTableDefaults final : public MachineFunctionPass {
4141

4242
char WebAssemblyFixBrTableDefaults::ID = 0;
4343

44-
// `MI` is a br_table instruction missing its default target argument. This
44+
// `MI` is a br_table instruction with a dummy default target argument. This
4545
// function finds and adds the default target argument and removes any redundant
46-
// range check preceding the br_table.
46+
// range check preceding the br_table. Returns the MBB that the br_table is
47+
// moved into so it can be removed from further consideration, or nullptr if the
48+
// br_table cannot be optimized.
4749
MachineBasicBlock *fixBrTable(MachineInstr &MI, MachineBasicBlock *MBB,
4850
MachineFunction &MF) {
4951
// Get the header block, which contains the redundant range check.
5052
assert(MBB->pred_size() == 1 && "Expected a single guard predecessor");
5153
auto *HeaderMBB = *MBB->pred_begin();
5254

5355
// Find the conditional jump to the default target. If it doesn't exist, the
54-
// default target is unreachable anyway, so we can choose anything.
56+
// default target is unreachable anyway, so we can keep the existing dummy
57+
// target.
5558
MachineBasicBlock *TBB = nullptr, *FBB = nullptr;
5659
SmallVector<MachineOperand, 2> Cond;
5760
const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
58-
TII.analyzeBranch(*HeaderMBB, TBB, FBB, Cond);
61+
bool Analyzed = !TII.analyzeBranch(*HeaderMBB, TBB, FBB, Cond);
62+
assert(Analyzed && "Could not analyze jump header branches");
5963

6064
// Here are the possible outcomes. '_' is nullptr, `J` is the jump table block
6165
// aka MBB, 'D' is the default block.
@@ -66,14 +70,27 @@ MachineBasicBlock *fixBrTable(MachineInstr &MI, MachineBasicBlock *MBB,
6670
// D | _ | Header jumps to the default and falls through to the jump table
6771
// D | J | Header jumps to the default and also to the jump table
6872
if (TBB && TBB != MBB) {
69-
// Install the default target.
7073
assert((FBB == nullptr || FBB == MBB) &&
7174
"Expected jump or fallthrough to br_table block");
75+
assert(Cond.size() == 2 && Cond[1].isReg() && "Unexpected condition info");
76+
77+
// If the range check checks an i64 value, we cannot optimize it out because
78+
// the i64 index is truncated to an i32, making values over 2^32
79+
// indistinguishable from small numbers.
80+
MachineRegisterInfo &MRI = MF.getRegInfo();
81+
auto *RangeCheck = MRI.getVRegDef(Cond[1].getReg());
82+
assert(RangeCheck != nullptr);
83+
unsigned RangeCheckOp = RangeCheck->getOpcode();
84+
assert(RangeCheckOp == WebAssembly::GT_U_I32 ||
85+
RangeCheckOp == WebAssembly::GT_U_I64);
86+
if (RangeCheckOp == WebAssembly::GT_U_I64) {
87+
// Bail out and leave the jump table untouched
88+
return nullptr;
89+
}
90+
91+
// Remove the dummy default target and install the real one.
92+
MI.RemoveOperand(MI.getNumExplicitOperands() - 1);
7293
MI.addOperand(MF, MachineOperand::CreateMBB(TBB));
73-
} else {
74-
// Arbitrarily choose the first jump target as the default.
75-
auto *SomeMBB = MI.getOperand(1).getMBB();
76-
MI.addOperand(MachineOperand::CreateMBB(SomeMBB));
7794
}
7895

7996
// Remove any branches from the header and splice in the jump table instead
@@ -110,8 +127,10 @@ bool WebAssemblyFixBrTableDefaults::runOnMachineFunction(MachineFunction &MF) {
110127
for (auto &MI : *MBB) {
111128
if (WebAssembly::isBrTable(MI)) {
112129
auto *Fixed = fixBrTable(MI, MBB, MF);
113-
MBBSet.erase(Fixed);
114-
Changed = true;
130+
if (Fixed != nullptr) {
131+
MBBSet.erase(Fixed);
132+
Changed = true;
133+
}
115134
break;
116135
}
117136
}

llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,8 +1285,10 @@ SDValue WebAssemblyTargetLowering::LowerBR_JT(SDValue Op,
12851285
for (auto MBB : MBBs)
12861286
Ops.push_back(DAG.getBasicBlock(MBB));
12871287

1288-
// Do not add the default case for now. It will be added in
1289-
// WebAssemblyFixBrTableDefaults.
1288+
// Add the first MBB as a dummy default target for now. This will be replaced
1289+
// with the proper default target (and the preceding range check eliminated)
1290+
// if possible by WebAssemblyFixBrTableDefaults.
1291+
Ops.push_back(DAG.getBasicBlock(*MBBs.begin()));
12901292
return DAG.getNode(WebAssemblyISD::BR_TABLE, DL, MVT::Other, Ops);
12911293
}
12921294

llvm/test/CodeGen/WebAssembly/switch.ll

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,26 +95,30 @@ sw.epilog: ; preds = %entry, %sw.bb.5, %s
9595

9696
; CHECK-LABEL: bar64:
9797
; CHECK: block {{$}}
98+
; CHECK: i64.const
99+
; CHECK: i64.gt_u
100+
; CHECK: br_if 0
98101
; CHECK: block {{$}}
99102
; CHECK: block {{$}}
100103
; CHECK: block {{$}}
101104
; CHECK: block {{$}}
102105
; CHECK: block {{$}}
103106
; CHECK: block {{$}}
104-
; CHECK: br_table {{[^,]+}}, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6{{$}}
105-
; CHECK: .LBB{{[0-9]+}}_1:
106-
; CHECK: call foo0{{$}}
107+
; CHECK: i32.wrap_i64
108+
; CHECK: br_table {{[^,]+}}, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 4, 5, 0{{$}}
107109
; CHECK: .LBB{{[0-9]+}}_2:
108-
; CHECK: call foo1{{$}}
110+
; CHECK: call foo0{{$}}
109111
; CHECK: .LBB{{[0-9]+}}_3:
110-
; CHECK: call foo2{{$}}
112+
; CHECK: call foo1{{$}}
111113
; CHECK: .LBB{{[0-9]+}}_4:
112-
; CHECK: call foo3{{$}}
114+
; CHECK: call foo2{{$}}
113115
; CHECK: .LBB{{[0-9]+}}_5:
114-
; CHECK: call foo4{{$}}
116+
; CHECK: call foo3{{$}}
115117
; CHECK: .LBB{{[0-9]+}}_6:
116-
; CHECK: call foo5{{$}}
118+
; CHECK: call foo4{{$}}
117119
; CHECK: .LBB{{[0-9]+}}_7:
120+
; CHECK: call foo5{{$}}
121+
; CHECK: .LBB{{[0-9]+}}_8:
118122
; CHECK: return{{$}}
119123
define void @bar64(i64 %n) {
120124
entry:
@@ -172,3 +176,43 @@ sw.bb.5: ; preds = %entry
172176
sw.epilog: ; preds = %entry, %sw.bb.5, %sw.bb.4, %sw.bb.3, %sw.bb.2, %sw.bb.1, %sw.bb
173177
ret void
174178
}
179+
180+
; CHECK-LABEL: truncated:
181+
; CHECK: block
182+
; CHECK: block
183+
; CHECK: block
184+
; CHECK: i32.wrap_i64
185+
; CHECK: br_table {{[^,]+}}, 0, 1, 2{{$}}
186+
; CHECK: .LBB{{[0-9]+}}_1
187+
; CHECK: end_block
188+
; CHECK: call foo0{{$}}
189+
; CHECK: return{{$}}
190+
; CHECK: .LBB{{[0-9]+}}_2
191+
; CHECK: end_block
192+
; CHECK: call foo1{{$}}
193+
; CHECK: return{{$}}
194+
; CHECK: .LBB{{[0-9]+}}_3
195+
; CHECK: end_block
196+
; CHECK: call foo2{{$}}
197+
; CHECK: return{{$}}
198+
; CHECK: end_function
199+
define void @truncated(i64 %n) {
200+
entry:
201+
%m = trunc i64 %n to i32
202+
switch i32 %m, label %default [
203+
i32 0, label %bb1
204+
i32 1, label %bb2
205+
]
206+
207+
bb1:
208+
tail call void @foo0()
209+
ret void
210+
211+
bb2:
212+
tail call void @foo1()
213+
ret void
214+
215+
default:
216+
tail call void @foo2()
217+
ret void
218+
}

0 commit comments

Comments
 (0)