llvm-project/llvm/test/CodeGen/WebAssembly/fast-isel-no-offset.ll
Heejin Ahn 115cb402d8
[WebAssembly] Don't fold non-nuw add/sub in FastISel (#111278)
We should not fold one of add/sub operands into a load/store's offset
when `nuw` (no unsigned wrap) is not present, because the address
calculation, which adds the offset with the operand, does not wrap.

This is handled correctly in the normal ISel:

6de5305b3d/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp (L328-L332)
but not in FastISel.

This positivity check in FastISel is not sufficient to avoid this case
fully:

6de5305b3d/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp (L348-L352)

because
1. Even if RHS is within signed int range, depending on the value of the
LHS, the resulting value can exceed uint32 max.
2. When one of the operands is a label, `Address` can contain a
`GlobalValue` and a `Reg` at the same time, so the `GlobalValue` becomes
incorrectly an offset:
6de5305b3d/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp (L53-L69)
6de5305b3d/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp (L409-L417)

Both cases are in the newly added test.

We should handle `SUB` too because `SUB` is the same as `ADD` when RHS's
sign changes. I checked why our current normal ISel only handles `ADD`,
and the reason it's OK for the normal ISel to handle only `ADD` seems
that DAGCombiner replaces `SUB` with `ADD` here:
6de5305b3d/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp (L3904-L3907)

Fixes #111018.
2024-10-09 14:31:16 -07:00

107 lines
3.1 KiB
LLVM

; RUN: llc < %s -asm-verbose=false -fast-isel -fast-isel-abort=1 -verify-machineinstrs | FileCheck %s
target triple = "wasm32-unknown-unknown"
; FastISel should not fold one of the add/sub operands into a load/store's
; offset when 'nuw' (no unsigned wrap) is not present, because the address
; calculation does not wrap. When there is an add/sub and nuw is not present, we
; bail out of FastISel.
@mylabel = external global ptr
; CHECK-LABEL: dont_fold_non_nuw_add_load:
; CHECK: local.get 0
; CHECK-NEXT: i32.const 2147483644
; CHECK-NEXT: i32.add
; CHECK-NEXT: i32.load 0
define i32 @dont_fold_non_nuw_add_load(ptr %p) {
%q = ptrtoint ptr %p to i32
%r = add i32 %q, 2147483644
%s = inttoptr i32 %r to ptr
%t = load i32, ptr %s
ret i32 %t
}
; CHECK-LABEL: dont_fold_non_nuw_add_store:
; CHECK: local.get 0
; CHECK-NEXT: i32.const 2147483644
; CHECK-NEXT: i32.add
; CHECK-NEXT: i32.const 5
; CHECK-NEXT: i32.store 0
define void @dont_fold_non_nuw_add_store(ptr %p) {
%q = ptrtoint ptr %p to i32
%r = add i32 %q, 2147483644
%s = inttoptr i32 %r to ptr
store i32 5, ptr %s
ret void
}
; CHECK-LABEL: dont_fold_non_nuw_add_load_2:
; CHECK: i32.const mylabel
; CHECK-NEXT: i32.const -4
; CHECK-NEXT: i32.add
; CHECK-NEXT: i32.load 0
define i32 @dont_fold_non_nuw_add_load_2() {
%t = load i32, ptr inttoptr (i32 add (i32 ptrtoint (ptr @mylabel to i32), i32 -4) to ptr), align 4
ret i32 %t
}
; CHECK-LABEL: dont_fold_non_nuw_add_store_2:
; CHECK: i32.const mylabel
; CHECK-NEXT: i32.const -4
; CHECK-NEXT: i32.add
; CHECK-NEXT: i32.const 5
; CHECK-NEXT: i32.store 0
define void @dont_fold_non_nuw_add_store_2() {
store i32 5, ptr inttoptr (i32 add (i32 ptrtoint (ptr @mylabel to i32), i32 -4) to ptr), align 4
ret void
}
; CHECK-LABEL: dont_fold_non_nuw_sub_load:
; CHECK: local.get 0
; CHECK-NEXT: i32.const -2147483644
; CHECK-NEXT: i32.sub
; CHECK-NEXT: i32.load 0
define i32 @dont_fold_non_nuw_sub_load(ptr %p) {
%q = ptrtoint ptr %p to i32
%r = sub i32 %q, -2147483644
%s = inttoptr i32 %r to ptr
%t = load i32, ptr %s
ret i32 %t
}
; CHECK-LABEL: dont_fold_non_nuw_sub_store:
; CHECK: local.get 0
; CHECK-NEXT: i32.const -2147483644
; CHECK-NEXT: i32.sub
; CHECK-NEXT: i32.const 5
; CHECK-NEXT: i32.store 0
define void @dont_fold_non_nuw_sub_store(ptr %p) {
%q = ptrtoint ptr %p to i32
%r = sub i32 %q, -2147483644
%s = inttoptr i32 %r to ptr
store i32 5, ptr %s
ret void
}
; CHECK-LABEL: dont_fold_non_nuw_sub_load_2:
; CHECK: i32.const mylabel
; CHECK-NEXT: i32.const 4
; CHECK-NEXT: i32.sub
; CHECK-NEXT: i32.load 0
define i32 @dont_fold_non_nuw_sub_load_2() {
%t = load i32, ptr inttoptr (i32 sub (i32 ptrtoint (ptr @mylabel to i32), i32 4) to ptr), align 4
ret i32 %t
}
; CHECK-LABEL: dont_fold_non_nuw_sub_store_2:
; CHECK: i32.const mylabel
; CHECK-NEXT: i32.const 4
; CHECK-NEXT: i32.sub
; CHECK-NEXT: i32.const 5
; CHECK-NEXT: i32.store 0
define void @dont_fold_non_nuw_sub_store_2() {
store i32 5, ptr inttoptr (i32 sub (i32 ptrtoint (ptr @mylabel to i32), i32 4) to ptr), align 4
ret void
}