diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp index 317c6463985dc..7c90fff2a5c1d 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp @@ -337,6 +337,12 @@ bool WebAssemblyFastISel::computeAddress(const Value *Obj, Address &Addr) { break; } case Instruction::Add: { + // We should not fold operands into an offset when 'nuw' (no unsigned wrap) + // is not present, because the address calculation does not wrap. + if (auto *OFBinOp = dyn_cast(U)) + if (!OFBinOp->hasNoUnsignedWrap()) + break; + // Adds of constants are common and easy enough. const Value *LHS = U->getOperand(0); const Value *RHS = U->getOperand(1); @@ -360,6 +366,12 @@ bool WebAssemblyFastISel::computeAddress(const Value *Obj, Address &Addr) { break; } case Instruction::Sub: { + // We should not fold operands into an offset when 'nuw' (no unsigned wrap) + // is not present, because the address calculation does not wrap. + if (auto *OFBinOp = dyn_cast(U)) + if (!OFBinOp->hasNoUnsignedWrap()) + break; + // Subs of constants are common and easy enough. const Value *LHS = U->getOperand(0); const Value *RHS = U->getOperand(1); diff --git a/llvm/test/CodeGen/WebAssembly/fast-isel-no-offset.ll b/llvm/test/CodeGen/WebAssembly/fast-isel-no-offset.ll new file mode 100644 index 0000000000000..d4ba1f3bc4a45 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/fast-isel-no-offset.ll @@ -0,0 +1,106 @@ +; 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 +} diff --git a/llvm/test/CodeGen/WebAssembly/fast-isel-pr47040.ll b/llvm/test/CodeGen/WebAssembly/fast-isel-pr47040.ll index 6a1304cb9a93a..75cb5b66b3ebe 100644 --- a/llvm/test/CodeGen/WebAssembly/fast-isel-pr47040.ll +++ b/llvm/test/CodeGen/WebAssembly/fast-isel-pr47040.ll @@ -14,7 +14,7 @@ target triple = "wasm32-unknown-unknown" define i32 @foo() { %stack_addr = alloca i32 %stack_i = ptrtoint ptr %stack_addr to i32 - %added = add i32 %stack_i, undef + %added = add nuw i32 %stack_i, undef %added_addr = inttoptr i32 %added to ptr %ret = load i32, ptr %added_addr ret i32 %ret