Skip to content

Commit 33e8b3d

Browse files
authored
aarch64: Fix miscompile lowering the extr instruction (#12907)
This commit fixes a miscompile in the lowering of the `extr` instruction for the aarch64 backend where one of the shift operands is 0. In this edge case the generated `extr` instruction did not match the input CLIF semantics, calculating a different value. The fix here is to only use the `extr` instruction when both immediates are larger than 0.
1 parent fe5ea39 commit 33e8b3d

File tree

4 files changed

+67
-6
lines changed

4 files changed

+67
-6
lines changed

cranelift/codegen/src/isa/aarch64/lower.isle

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1455,13 +1455,22 @@
14551455
;; `bor`.
14561456
;;
14571457
;; (x << xs) | (y >> ys) if (xs + ys == widthof(ty)) => extr(x, y, ys)
1458+
;;
1459+
;; Note that both `xs` and `ys` must be larger than 0. If either one is 0 and
1460+
;; they sum to the type width then it means that the shifts don't actually do
1461+
;; anything CLIF-wise and this should compile down to a `bor` operation. Leave
1462+
;; that edge case to the mid-end and only lower to `extr` here.
14581463
(rule 5 (lower (has_type (ty_32_or_64 ty)
14591464
(bor _ (ishl _ x (u8_from_iconst xs)) (ushr _ y (u8_from_iconst ys)))))
14601465
(if-let true (u64_eq (ty_bits ty) (u64_wrapping_add xs ys)))
1466+
(if-let true (u64_gt xs 0))
1467+
(if-let true (u64_gt ys 0))
14611468
(a64_extr ty x y (imm_shift_from_u8 ys)))
14621469
(rule 5 (lower (has_type (ty_32_or_64 ty)
14631470
(bor _ (ushr _ y (u8_from_iconst ys)) (ishl _ x (u8_from_iconst xs)))))
14641471
(if-let true (u64_eq (ty_bits ty) (u64_wrapping_add xs ys)))
1472+
(if-let true (u64_gt xs 0))
1473+
(if-let true (u64_gt ys 0))
14651474
(a64_extr ty x y (imm_shift_from_u8 ys)))
14661475

14671476
;;;; Rules for `bxor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

cranelift/filetests/filetests/runtests/bitops.clif

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,8 @@ block0(v0: i32, v1: i32):
224224

225225
function %test_bxor_band_band(i32, i32, i32) -> i32 fast {
226226
block0(v0: i32, v1: i32, v2: i32):
227-
v3 = band v0, v1
228-
v4 = band v0, v2
227+
v3 = band v0, v1
228+
v4 = band v0, v2
229229
v5 = bxor v3, v4
230230
return v5
231231
}
@@ -731,4 +731,15 @@ block0(v0: i32, v1: i32):
731731

732732
; run: %test_bor_band_bnot(0, 0) == 0
733733
; run: %test_bor_band_bnot(1, 0) == 1
734-
; run: %test_bor_band_bnot(0xf0, 0x0f) == 0xff
734+
; run: %test_bor_band_bnot(0xf0, 0x0f) == 0xff
735+
736+
function %test_aarch64_extr(i32, i32) -> i32 fast {
737+
block0(v0: i32, v1: i32):
738+
v2 = ishl_imm v0, 32
739+
v3 = ushr_imm v1, 0
740+
v4 = bor v2, v3
741+
return v4
742+
}
743+
744+
; run: %test_aarch64_extr(1, 0) == 1
745+
; run: %test_aarch64_extr(0, 1) == 1

tests/disas/aarch64-extr.wat

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@
2626
i32.const 11
2727
i32.shr_u
2828
i32.or)
29+
(func $i32_0 (param i32 i32) (result i32)
30+
local.get 0
31+
i32.const 32
32+
i32.shl
33+
local.get 1
34+
i32.const 0
35+
i32.shr_u
36+
i32.or)
2937

3038
(func $i64_21 (param i64 i64) (result i64)
3139
local.get 0
@@ -51,6 +59,14 @@
5159
i64.const 11
5260
i64.shr_u
5361
i64.or)
62+
(func $i64_0 (param i64 i64) (result i64)
63+
local.get 0
64+
i64.const 64
65+
i64.shl
66+
local.get 1
67+
i64.const 0
68+
i64.shr_u
69+
i64.or)
5470
)
5571

5672
;; wasm[0]::function[0]::i32_21:
@@ -74,23 +90,37 @@
7490
;; ldp x29, x30, [sp], #0x10
7591
;; ret
7692
;;
77-
;; wasm[0]::function[3]::i64_21:
93+
;; wasm[0]::function[3]::i32_0:
94+
;; stp x29, x30, [sp, #-0x10]!
95+
;; mov x29, sp
96+
;; orr w2, w4, w5
97+
;; ldp x29, x30, [sp], #0x10
98+
;; ret
99+
;;
100+
;; wasm[0]::function[4]::i64_21:
78101
;; stp x29, x30, [sp, #-0x10]!
79102
;; mov x29, sp
80103
;; extr x2, x4, x5, #0x15
81104
;; ldp x29, x30, [sp], #0x10
82105
;; ret
83106
;;
84-
;; wasm[0]::function[4]::i64_21_swapped:
107+
;; wasm[0]::function[5]::i64_21_swapped:
85108
;; stp x29, x30, [sp, #-0x10]!
86109
;; mov x29, sp
87110
;; extr x2, x4, x5, #0x15
88111
;; ldp x29, x30, [sp], #0x10
89112
;; ret
90113
;;
91-
;; wasm[0]::function[5]::i64_11:
114+
;; wasm[0]::function[6]::i64_11:
92115
;; stp x29, x30, [sp, #-0x10]!
93116
;; mov x29, sp
94117
;; extr x2, x4, x5, #0xb
95118
;; ldp x29, x30, [sp], #0x10
96119
;; ret
120+
;;
121+
;; wasm[0]::function[7]::i64_0:
122+
;; stp x29, x30, [sp, #-0x10]!
123+
;; mov x29, sp
124+
;; orr x2, x4, x5
125+
;; ldp x29, x30, [sp], #0x10
126+
;; ret
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
(module
2+
(memory 1)
3+
(func (export "a64-extr") (param i32 i32) (result i32)
4+
(i32.or
5+
(i32.shl (local.get 0) (i32.const 32))
6+
(i32.shr_u (local.get 1) (i32.const 0))
7+
)
8+
)
9+
)
10+
11+
(assert_return (invoke "a64-extr" (i32.const 65536) (i32.const 0)) (i32.const 65536))

0 commit comments

Comments
 (0)