Index: lib/Target/WebAssembly/WebAssemblyInstrMemory.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrMemory.td +++ lib/Target/WebAssembly/WebAssemblyInstrMemory.td @@ -41,15 +41,13 @@ }]>; // GlobalAddresses are conceptually unsigned values, so we can also fold them -// into immediate values as long as their offsets are non-negative. +// into immediate values as long as the add is 'nuw'. +// TODO: We'd like to also match GA offsets but there are cases where the +// register can have a negative value. Find out what more we can do. def regPlusGA : PatFrag<(ops node:$addr, node:$off), (add node:$addr, node:$off), [{ - return N->getFlags()->hasNoUnsignedWrap() || - (N->getOperand(1)->getOpcode() == WebAssemblyISD::Wrapper && - isa(N->getOperand(1)->getOperand(0)) && - cast(N->getOperand(1)->getOperand(0)) - ->getOffset() >= 0); + return N->getFlags()->hasNoUnsignedWrap(); }]>; // We don't need a regPlusES because external symbols never have constant Index: test/CodeGen/WebAssembly/address-offsets.ll =================================================================== --- test/CodeGen/WebAssembly/address-offsets.ll +++ test/CodeGen/WebAssembly/address-offsets.ll @@ -28,13 +28,17 @@ ret i32 %t } +; TODO: load_test1 - load_test8 are disabled because folding GA+reg is disabled +; (there are cases where the value in the reg can be negative). +; Likewise for stores. + ; CHECK-LABEL: load_test1: ; CHECK-NEXT: param i32{{$}} ; CHECK-NEXT: result i32{{$}} ; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.load $push2=, g+40($pop1){{$}} -; CHECK-NEXT: return $pop2{{$}} +; CHECK-NEX T: i32.shl $push1=, $0, $pop0{{$}} +; CHECK-NEX T: i32.load $push2=, g+40($pop1){{$}} +; CHECK-NEX T: return $pop2{{$}} define i32 @load_test1(i32 %n) { %add = add nsw i32 %n, 10 %arrayidx = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add @@ -46,9 +50,9 @@ ; CHECK-NEXT: param i32{{$}} ; CHECK-NEXT: result i32{{$}} ; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.load $push2=, g+40($pop1){{$}} -; CHECK-NEXT: return $pop2{{$}} +; CHECK-NEX T: i32.shl $push1=, $0, $pop0{{$}} +; CHECK-NEX T: i32.load $push2=, g+40($pop1){{$}} +; CHECK-NEX T: return $pop2{{$}} define i32 @load_test2(i32 %n) { %add = add nsw i32 10, %n %arrayidx = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add @@ -60,9 +64,9 @@ ; CHECK-NEXT: param i32{{$}} ; CHECK-NEXT: result i32{{$}} ; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.load $push2=, g+40($pop1){{$}} -; CHECK-NEXT: return $pop2{{$}} +; CHECK-NEX T: i32.shl $push1=, $0, $pop0{{$}} +; CHECK-NEX T: i32.load $push2=, g+40($pop1){{$}} +; CHECK-NEX T: return $pop2{{$}} define i32 @load_test3(i32 %n) { %add.ptr = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %n %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10 @@ -74,9 +78,9 @@ ; CHECK-NEXT: param i32{{$}} ; CHECK-NEXT: result i32{{$}} ; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.load $push2=, g+40($pop1){{$}} -; CHECK-NEXT: return $pop2{{$}} +; CHECK-NEX T: i32.shl $push1=, $0, $pop0{{$}} +; CHECK-NEX T: i32.load $push2=, g+40($pop1){{$}} +; CHECK-NEX T: return $pop2{{$}} define i32 @load_test4(i32 %n) { %add.ptr = getelementptr inbounds i32, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 10), i32 %n %t = load i32, i32* %add.ptr, align 4 @@ -87,9 +91,9 @@ ; CHECK-NEXT: param i32{{$}} ; CHECK-NEXT: result i32{{$}} ; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.load $push2=, g+40($pop1){{$}} -; CHECK-NEXT: return $pop2{{$}} +; CHECK-NEX T: i32.shl $push1=, $0, $pop0{{$}} +; CHECK-NEX T: i32.load $push2=, g+40($pop1){{$}} +; CHECK-NEX T: return $pop2{{$}} define i32 @load_test5(i32 %n) { %add.ptr = getelementptr inbounds i32, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 10), i32 %n %t = load i32, i32* %add.ptr, align 4 @@ -100,9 +104,9 @@ ; CHECK-NEXT: param i32{{$}} ; CHECK-NEXT: result i32{{$}} ; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.load $push2=, g+40($pop1){{$}} -; CHECK-NEXT: return $pop2{{$}} +; CHECK-NEX T: i32.shl $push1=, $0, $pop0{{$}} +; CHECK-NEX T: i32.load $push2=, g+40($pop1){{$}} +; CHECK-NEX T: return $pop2{{$}} define i32 @load_test6(i32 %n) { %add = add nsw i32 %n, 10 %add.ptr = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add @@ -114,9 +118,9 @@ ; CHECK-NEXT: param i32{{$}} ; CHECK-NEXT: result i32{{$}} ; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.load $push2=, g+40($pop1){{$}} -; CHECK-NEXT: return $pop2{{$}} +; CHECK-NEX T: i32.shl $push1=, $0, $pop0{{$}} +; CHECK-NEX T: i32.load $push2=, g+40($pop1){{$}} +; CHECK-NEX T: return $pop2{{$}} define i32 @load_test7(i32 %n) { %add.ptr = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %n %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10 @@ -128,9 +132,9 @@ ; CHECK-NEXT: param i32{{$}} ; CHECK-NEXT: result i32{{$}} ; CHECK-NEXT: i32.const $push0=, 2{{$}} -; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.load $push2=, g+40($pop1){{$}} -; CHECK-NEXT: return $pop2{{$}} +; CHECK-NEX T: i32.shl $push1=, $0, $pop0{{$}} +; CHECK-NEX T: i32.load $push2=, g+40($pop1){{$}} +; CHECK-NEX T: return $pop2{{$}} define i32 @load_test8(i32 %n) { %add = add nsw i32 10, %n %add.ptr = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add @@ -374,8 +378,8 @@ ; CHECK-NEXT: param i32, i32{{$}} ; CHECK-NEXT: i32.const $push0=, 2{{$}} ; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.store $drop=, g+40($pop1), $1{{$}} -; CHECK-NEXT: return{{$}} +; CHECK-NEX T: i32.store $drop=, g+40($pop1), $1{{$}} +; CHECK-NEX T: return{{$}} define void @store_test1(i32 %n, i32 %i) { %add = add nsw i32 %n, 10 %arrayidx = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add @@ -387,8 +391,8 @@ ; CHECK-NEXT: param i32, i32{{$}} ; CHECK-NEXT: i32.const $push0=, 2{{$}} ; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.store $drop=, g+40($pop1), $1{{$}} -; CHECK-NEXT: return{{$}} +; CHECK-NEX T: i32.store $drop=, g+40($pop1), $1{{$}} +; CHECK-NEX T: return{{$}} define void @store_test2(i32 %n, i32 %i) { %add = add nsw i32 10, %n %arrayidx = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add @@ -400,8 +404,8 @@ ; CHECK-NEXT: param i32, i32{{$}} ; CHECK-NEXT: i32.const $push0=, 2{{$}} ; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.store $drop=, g+40($pop1), $1{{$}} -; CHECK-NEXT: return{{$}} +; CHECK-NEX T: i32.store $drop=, g+40($pop1), $1{{$}} +; CHECK-NEX T: return{{$}} define void @store_test3(i32 %n, i32 %i) { %add.ptr = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %n %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10 @@ -413,8 +417,8 @@ ; CHECK-NEXT: param i32, i32{{$}} ; CHECK-NEXT: i32.const $push0=, 2{{$}} ; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.store $drop=, g+40($pop1), $1{{$}} -; CHECK-NEXT: return{{$}} +; CHECK-NEX T: i32.store $drop=, g+40($pop1), $1{{$}} +; CHECK-NEX T: return{{$}} define void @store_test4(i32 %n, i32 %i) { %add.ptr = getelementptr inbounds i32, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 10), i32 %n store i32 %i, i32* %add.ptr, align 4 @@ -425,8 +429,8 @@ ; CHECK-NEXT: param i32, i32{{$}} ; CHECK-NEXT: i32.const $push0=, 2{{$}} ; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.store $drop=, g+40($pop1), $1{{$}} -; CHECK-NEXT: return{{$}} +; CHECK-NEX T: i32.store $drop=, g+40($pop1), $1{{$}} +; CHECK-NEX T: return{{$}} define void @store_test5(i32 %n, i32 %i) { %add.ptr = getelementptr inbounds i32, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 10), i32 %n store i32 %i, i32* %add.ptr, align 4 @@ -437,8 +441,8 @@ ; CHECK-NEXT: param i32, i32{{$}} ; CHECK-NEXT: i32.const $push0=, 2{{$}} ; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.store $drop=, g+40($pop1), $1{{$}} -; CHECK-NEXT: return{{$}} +; CHECK-NEX T: i32.store $drop=, g+40($pop1), $1{{$}} +; CHECK-NEX T: return{{$}} define void @store_test6(i32 %n, i32 %i) { %add = add nsw i32 %n, 10 %add.ptr = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add @@ -450,8 +454,8 @@ ; CHECK-NEXT: param i32, i32{{$}} ; CHECK-NEXT: i32.const $push0=, 2{{$}} ; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.store $drop=, g+40($pop1), $1{{$}} -; CHECK-NEXT: return{{$}} +; CHECK-NEX T: i32.store $drop=, g+40($pop1), $1{{$}} +; CHECK-NEX T: return{{$}} define void @store_test7(i32 %n, i32 %i) { %add.ptr = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %n %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10 @@ -463,8 +467,8 @@ ; CHECK-NEXT: param i32, i32{{$}} ; CHECK-NEXT: i32.const $push0=, 2{{$}} ; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} -; CHECK-NEXT: i32.store $drop=, g+40($pop1), $1{{$}} -; CHECK-NEXT: return{{$}} +; CHECK-NEX T: i32.store $drop=, g+40($pop1), $1{{$}} +; CHECK-NEX T: return{{$}} define void @store_test8(i32 %n, i32 %i) { %add = add nsw i32 10, %n %add.ptr = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add Index: test/CodeGen/WebAssembly/negative-base-reg.ll =================================================================== --- /dev/null +++ test/CodeGen/WebAssembly/negative-base-reg.ll @@ -0,0 +1,43 @@ +; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -disable-wasm-fallthrough-return-opt | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32" + +@args = hidden local_unnamed_addr global [32 x i32] zeroinitializer, align 16 + +; Function Attrs: norecurse nounwind +define hidden i32 @main() local_unnamed_addr #0 { + +; If LSR stops selecting a negative base reg value, then this test will no +; longer be useful as written. +; CHECK: i32.const $0=, -128 +entry: + br label %for.body + +for.body: ; preds = %for.body, %entry + %i.04 = phi i32 [ 0, %entry ], [ %inc, %for.body ] +; The offset should not be folded into the store. +; CHECK: i32.const $push{{[0-9]+}}=, args+128 +; CHECK: i32.add +; CHECK: i32.store $drop=, 0( + %arrayidx = getelementptr inbounds [32 x i32], [32 x i32]* @args, i32 0, i32 %i.04 + store i32 1, i32* %arrayidx, align 4, !tbaa !1 + %inc = add nuw nsw i32 %i.04, 1 + %exitcond = icmp eq i32 %inc, 32 + br i1 %exitcond, label %for.end, label %for.body, !llvm.loop !5 + +for.end: ; preds = %for.body + ret i32 0 +} + +attributes #0 = { norecurse nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.ident = !{!0} + +!0 = !{!"clang version 4.0.0 (trunk 279056) (llvm/trunk 279074)"} +!1 = !{!2, !2, i64 0} +!2 = !{!"int", !3, i64 0} +!3 = !{!"omnipotent char", !4, i64 0} +!4 = !{!"Simple C/C++ TBAA"} +!5 = distinct !{!5, !6} +!6 = !{!"llvm.loop.unroll.disable"}