Skip to content

Commit 0f51338

Browse files
authored
riscv64: Clear the top 32bits in the br_table index (bytecodealliance#5831)
We were unintentionally relying on these to be zeroed when jumping.
1 parent 4d954f5 commit 0f51338

File tree

6 files changed

+157
-7
lines changed

6 files changed

+157
-7
lines changed

cranelift/codegen/src/isa/riscv64/inst.isle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@
213213
(BrTable
214214
(index Reg)
215215
(tmp1 WritableReg)
216+
(tmp2 WritableReg)
216217
(targets VecBranchTarget))
217218

218219
;; atomic compare and set operation

cranelift/codegen/src/isa/riscv64/inst/emit.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -984,11 +984,13 @@ impl MachInstEmit for Inst {
984984
&Inst::BrTable {
985985
index,
986986
tmp1,
987+
tmp2,
987988
ref targets,
988989
} => {
989990
let index = allocs.next(index);
990991
let tmp1 = allocs.next_writable(tmp1);
991-
let tmp2 = writable_spilltmp_reg();
992+
let tmp2 = allocs.next_writable(tmp2);
993+
let ext_index = writable_spilltmp_reg();
992994

993995
// The default target is passed in as the 0th element of `targets`
994996
// separate it here for clarity.
@@ -1001,22 +1003,38 @@ impl MachInstEmit for Inst {
10011003
// that offset. Each jump table entry is a regular auipc+jalr which we emit sequentially.
10021004
//
10031005
// Build the following sequence:
1006+
//
1007+
// extend_index:
1008+
// zext.w ext_index, index
10041009
// bounds_check:
10051010
// li tmp, n_labels
1006-
// bltu index, tmp, compute_target
1011+
// bltu ext_index, tmp, compute_target
10071012
// jump_to_default_block:
10081013
// auipc pc, 0
10091014
// jalr zero, pc, default_block
10101015
// compute_target:
10111016
// auipc pc, 0
1012-
// slli tmp, index, 3
1017+
// slli tmp, ext_index, 3
10131018
// add pc, pc, tmp
10141019
// jalr zero, pc, 0x10
10151020
// jump_table:
10161021
// ; This repeats for each entry in the jumptable
10171022
// auipc pc, 0
10181023
// jalr zero, pc, block_target
10191024

1025+
// Extend the index to 64 bits.
1026+
//
1027+
// This prevents us branching on the top 32 bits of the index, which
1028+
// are undefined.
1029+
Inst::Extend {
1030+
rd: ext_index,
1031+
rn: index,
1032+
signed: false,
1033+
from_bits: 32,
1034+
to_bits: 64,
1035+
}
1036+
.emit(&[], sink, emit_info, state);
1037+
10201038
// Bounds check.
10211039
//
10221040
// Check if the index passed in is larger than the number of jumptable
@@ -1030,7 +1048,7 @@ impl MachInstEmit for Inst {
10301048
not_taken: BranchTarget::zero(),
10311049
kind: IntegerCompare {
10321050
kind: IntCC::UnsignedLessThan,
1033-
rs1: index,
1051+
rs1: ext_index.to_reg(),
10341052
rs2: tmp2.to_reg(),
10351053
},
10361054
}
@@ -1059,7 +1077,7 @@ impl MachInstEmit for Inst {
10591077
Inst::AluRRImm12 {
10601078
alu_op: AluOPRRI::Slli,
10611079
rd: tmp2,
1062-
rs: index,
1080+
rs: ext_index.to_reg(),
10631081
imm12: Imm12::from_bits(3),
10641082
}
10651083
.emit(&[], sink, emit_info, state);

cranelift/codegen/src/isa/riscv64/inst/mod.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,9 +338,12 @@ fn riscv64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
338338
match inst {
339339
&Inst::Nop0 => {}
340340
&Inst::Nop4 => {}
341-
&Inst::BrTable { index, tmp1, .. } => {
341+
&Inst::BrTable {
342+
index, tmp1, tmp2, ..
343+
} => {
342344
collector.reg_use(index);
343345
collector.reg_early_def(tmp1);
346+
collector.reg_early_def(tmp2);
344347
}
345348
&Inst::Auipc { rd, .. } => collector.reg_def(rd),
346349
&Inst::Lui { rd, .. } => collector.reg_def(rd),
@@ -1171,15 +1174,17 @@ impl Inst {
11711174
&Inst::BrTable {
11721175
index,
11731176
tmp1,
1177+
tmp2,
11741178
ref targets,
11751179
} => {
11761180
let targets: Vec<_> = targets.iter().map(|x| x.as_label().unwrap()).collect();
11771181
format!(
1178-
"{} {},{}##tmp1={}",
1182+
"{} {},{}##tmp1={},tmp2={}",
11791183
"br_table",
11801184
format_reg(index, allocs),
11811185
format_labels(&targets[..]),
11821186
format_reg(tmp1.to_reg(), allocs),
1187+
format_reg(tmp2.to_reg(), allocs),
11831188
)
11841189
}
11851190
&Inst::Auipc { rd, imm } => {

cranelift/codegen/src/isa/riscv64/lower/isle.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Riscv64Backend> {
378378

379379
fn lower_br_table(&mut self, index: Reg, targets: &VecMachLabel) -> Unit {
380380
let tmp1 = self.temp_writable_reg(I64);
381+
let tmp2 = self.temp_writable_reg(I64);
381382
let targets: Vec<BranchTarget> = targets
382383
.into_iter()
383384
.copied()
@@ -386,6 +387,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Riscv64Backend> {
386387
self.emit(&MInst::BrTable {
387388
index,
388389
tmp1,
390+
tmp2,
389391
targets,
390392
});
391393
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
test compile precise-output
2+
set unwind_info=false
3+
target riscv64
4+
5+
function %br_table(i32) -> i32 {
6+
block0(v0: i32):
7+
br_table v0, block4, [block1, block2, block2, block3]
8+
9+
block1:
10+
v1 = iconst.i32 1
11+
jump block5(v1)
12+
13+
block2:
14+
v2 = iconst.i32 2
15+
jump block5(v2)
16+
17+
block3:
18+
v3 = iconst.i32 3
19+
jump block5(v3)
20+
21+
block4:
22+
v4 = iconst.i32 4
23+
jump block5(v4)
24+
25+
block5(v5: i32):
26+
v6 = iadd.i32 v0, v5
27+
return v6
28+
}
29+
30+
; VCode:
31+
; block0:
32+
; br_table a0,[MachLabel(1),MachLabel(3),MachLabel(5),MachLabel(6),MachLabel(8)]##tmp1=t3,tmp2=t4
33+
; block1:
34+
; li a1,4
35+
; j label2
36+
; block2:
37+
; j label10
38+
; block3:
39+
; li a1,1
40+
; j label4
41+
; block4:
42+
; j label10
43+
; block5:
44+
; j label7
45+
; block6:
46+
; j label7
47+
; block7:
48+
; li a1,2
49+
; j label10
50+
; block8:
51+
; li a1,3
52+
; j label9
53+
; block9:
54+
; j label10
55+
; block10:
56+
; addw a0,a0,a1
57+
; ret
58+
;
59+
; Disassembled:
60+
; block0: ; offset 0x0
61+
; slli t6, a0, 0x20
62+
; srli t6, t6, 0x20
63+
; addi t4, zero, 4
64+
; bltu t6, t4, 0xc
65+
; auipc t4, 0
66+
; jalr zero, t4, 0x38
67+
; auipc t3, 0
68+
; slli t4, t6, 3
69+
; add t3, t3, t4
70+
; jalr zero, t3, 0x10
71+
; auipc t4, 0
72+
; jalr zero, t4, 0x28
73+
; auipc t4, 0
74+
; jalr zero, t4, 0x28
75+
; auipc t4, 0
76+
; jalr zero, t4, 0x20
77+
; auipc t4, 0
78+
; jalr zero, t4, 0x20
79+
; block1: ; offset 0x48
80+
; addi a1, zero, 4
81+
; block2: ; offset 0x4c
82+
; j 0x18
83+
; block3: ; offset 0x50
84+
; addi a1, zero, 1
85+
; block4: ; offset 0x54
86+
; j 0x10
87+
; block5: ; offset 0x58
88+
; addi a1, zero, 2
89+
; j 8
90+
; block6: ; offset 0x60
91+
; addi a1, zero, 3
92+
; block7: ; offset 0x64
93+
; addw a0, a0, a1
94+
; ret
95+

cranelift/filetests/filetests/runtests/br_table.clif

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,32 @@ block1(v5: i32):
100100
; run: %br_table_i32_inline_varied(4) == 4
101101
; run: %br_table_i32_inline_varied(297) == 4
102102
; run: %br_table_i32_inline_varied(65535) == 4
103+
104+
105+
106+
; This is a regression test for #5831.
107+
; The riscv64 backend was failing to clear the upper half of the
108+
; index register on a br_table, which caused it to jump to the wrong
109+
; block.
110+
function %br_table_upper_reg() -> i32 {
111+
block0:
112+
v0 = iconst.i32 -555163938
113+
v1 = iconst.i8 -34
114+
jump block1(v0, v1)
115+
116+
block1(v2: i32, v3: i8):
117+
v4 = ishl.i32 v2, v2
118+
v5 = rotr v4, v3
119+
br_table v5, block2, [block2, block2, block3]
120+
121+
block2 cold:
122+
v100 = iconst.i32 100
123+
return v100
124+
125+
block3 cold:
126+
v200 = iconst.i32 200
127+
return v200
128+
}
129+
130+
131+
; run: %br_table_upper_reg() == 200

0 commit comments

Comments
 (0)