diff options
Diffstat (limited to 'test/CodeGen/WebAssembly')
53 files changed, 3448 insertions, 552 deletions
diff --git a/test/CodeGen/WebAssembly/address-offsets.ll b/test/CodeGen/WebAssembly/address-offsets.ll new file mode 100644 index 000000000000..6403b3762992 --- /dev/null +++ b/test/CodeGen/WebAssembly/address-offsets.ll @@ -0,0 +1,672 @@ +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s + +; Test folding constant offsets and symbols into load and store addresses under +; a variety of circumstances. + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +@g = external global [0 x i32], align 4 + +; CHECK-LABEL: load_test0: +; CHECK-NEXT: result i32{{$}} +; CHECK-NEXT: i32.const $push0=, 0{{$}} +; CHECK-NEXT: i32.load $push1=, g+40($pop0){{$}} +; CHECK-NEXT: return $pop1{{$}} +define i32 @load_test0() { + %t = load i32, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 10), align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test0_noinbounds: +; CHECK-NEXT: result i32{{$}} +; CHECK-NEXT: i32.const $push0=, 0{{$}} +; CHECK-NEXT: i32.load $push1=, g+40($pop0){{$}} +; CHECK-NEXT: return $pop1{{$}} +define i32 @load_test0_noinbounds() { + %t = load i32, i32* getelementptr ([0 x i32], [0 x i32]* @g, i32 0, i32 10), align 4 + ret i32 %t +} + +; 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{{$}} +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 + %t = load i32, i32* %arrayidx, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test2: +; 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{{$}} +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 + %t = load i32, i32* %arrayidx, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test3: +; 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{{$}} +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 + %t = load i32, i32* %add.ptr1, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test4: +; 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{{$}} +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 + ret i32 %t +} + +; CHECK-LABEL: load_test5: +; 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{{$}} +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 + ret i32 %t +} + +; CHECK-LABEL: load_test6: +; 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{{$}} +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 + %t = load i32, i32* %add.ptr, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test7: +; 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{{$}} +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 + %t = load i32, i32* %add.ptr1, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test8: +; 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{{$}} +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 + %t = load i32, i32* %add.ptr, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test9: +; CHECK-NEXT: result i32{{$}} +; CHECK-NEXT: i32.const $push0=, 0{{$}} +; CHECK-NEXT: i32.load $push1=, g-40($pop0){{$}} +; CHECK-NEXT: return $pop1{{$}} +define i32 @load_test9() { + %t = load i32, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 1073741814), align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test10: +; CHECK-NEXT: param i32{{$}} +; CHECK-NEXT: result i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} +; CHECK-NEXT: i32.const $push2=, g-40{{$}} +; CHECK-NEXT: i32.add $push3=, $pop1, $pop2{{$}} +; CHECK-NEXT: i32.load $push4=, 0($pop3){{$}} +; CHECK-NEXT: return $pop4{{$}} +define i32 @load_test10(i32 %n) { + %add = add nsw i32 %n, -10 + %arrayidx = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add + %t = load i32, i32* %arrayidx, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test11: +; CHECK-NEXT: param i32{{$}} +; CHECK-NEXT: result i32{{$}} +; CHECK-NEXT: i32.load $push0=, 40($0){{$}} +; CHECK-NEXT: return $pop0{{$}} +define i32 @load_test11(i32* %p) { + %arrayidx = getelementptr inbounds i32, i32* %p, i32 10 + %t = load i32, i32* %arrayidx, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test11_noinbounds: +; CHECK-NEXT: param i32{{$}} +; CHECK-NEXT: result i32{{$}} +; CHECK-NEXT: i32.const $push0=, 40{{$}} +; CHECK-NEXT: i32.add $push1=, $0, $pop0{{$}} +; CHECK-NEXT: i32.load $push2=, 0($pop1){{$}} +; CHECK-NEXT: return $pop2{{$}} +define i32 @load_test11_noinbounds(i32* %p) { + %arrayidx = getelementptr i32, i32* %p, i32 10 + %t = load i32, i32* %arrayidx, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test12: +; CHECK-NEXT: param i32, i32{{$}} +; CHECK-NEXT: result i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} +; CHECK-NEXT: return $pop5{{$}} +define i32 @load_test12(i32* %p, i32 %n) { + %add = add nsw i32 %n, 10 + %arrayidx = getelementptr inbounds i32, i32* %p, i32 %add + %t = load i32, i32* %arrayidx, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test13: +; CHECK-NEXT: param i32, i32{{$}} +; CHECK-NEXT: result i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} +; CHECK-NEXT: return $pop5{{$}} +define i32 @load_test13(i32* %p, i32 %n) { + %add = add nsw i32 10, %n + %arrayidx = getelementptr inbounds i32, i32* %p, i32 %add + %t = load i32, i32* %arrayidx, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test14: +; CHECK-NEXT: param i32, i32{{$}} +; CHECK-NEXT: result i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.load $push3=, 40($pop2){{$}} +; CHECK-NEXT: return $pop3{{$}} +define i32 @load_test14(i32* %p, i32 %n) { + %add.ptr = getelementptr inbounds i32, i32* %p, i32 %n + %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10 + %t = load i32, i32* %add.ptr1, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test15: +; CHECK-NEXT: param i32, i32{{$}} +; CHECK-NEXT: result i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} +; CHECK-NEXT: return $pop5{{$}} +define i32 @load_test15(i32* %p, i32 %n) { + %add.ptr = getelementptr inbounds i32, i32* %p, i32 10 + %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 %n + %t = load i32, i32* %add.ptr1, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test16: +; CHECK-NEXT: param i32, i32{{$}} +; CHECK-NEXT: result i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} +; CHECK-NEXT: return $pop5{{$}} +define i32 @load_test16(i32* %p, i32 %n) { + %add.ptr = getelementptr inbounds i32, i32* %p, i32 10 + %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 %n + %t = load i32, i32* %add.ptr1, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test17: +; CHECK-NEXT: param i32, i32{{$}} +; CHECK-NEXT: result i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} +; CHECK-NEXT: return $pop5{{$}} +define i32 @load_test17(i32* %p, i32 %n) { + %add = add nsw i32 %n, 10 + %add.ptr = getelementptr inbounds i32, i32* %p, i32 %add + %t = load i32, i32* %add.ptr, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test18: +; CHECK-NEXT: param i32, i32{{$}} +; CHECK-NEXT: result i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.load $push3=, 40($pop2){{$}} +; CHECK-NEXT: return $pop3{{$}} +define i32 @load_test18(i32* %p, i32 %n) { + %add.ptr = getelementptr inbounds i32, i32* %p, i32 %n + %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10 + %t = load i32, i32* %add.ptr1, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test19: +; CHECK-NEXT: param i32, i32{{$}} +; CHECK-NEXT: result i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} +; CHECK-NEXT: return $pop5{{$}} +define i32 @load_test19(i32* %p, i32 %n) { + %add = add nsw i32 10, %n + %add.ptr = getelementptr inbounds i32, i32* %p, i32 %add + %t = load i32, i32* %add.ptr, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test20: +; CHECK-NEXT: param i32{{$}} +; CHECK-NEXT: result i32{{$}} +; CHECK-NEXT: i32.const $push0=, -40{{$}} +; CHECK-NEXT: i32.add $push1=, $0, $pop0{{$}} +; CHECK-NEXT: i32.load $push2=, 0($pop1){{$}} +; CHECK-NEXT: return $pop2{{$}} +define i32 @load_test20(i32* %p) { + %arrayidx = getelementptr inbounds i32, i32* %p, i32 -10 + %t = load i32, i32* %arrayidx, align 4 + ret i32 %t +} + +; CHECK-LABEL: load_test21: +; CHECK-NEXT: param i32, i32{{$}} +; CHECK-NEXT: result i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, -40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.load $push5=, 0($pop4){{$}} +; CHECK-NEXT: return $pop5{{$}} +define i32 @load_test21(i32* %p, i32 %n) { + %add = add nsw i32 %n, -10 + %arrayidx = getelementptr inbounds i32, i32* %p, i32 %add + %t = load i32, i32* %arrayidx, align 4 + ret i32 %t +} + +; CHECK-LABEL: store_test0: +; CHECK-NEXT: param i32{{$}} +; CHECK-NEXT: i32.const $push0=, 0{{$}} +; CHECK-NEXT: i32.store $drop=, g+40($pop0), $0{{$}} +; CHECK-NEXT: return{{$}} +define void @store_test0(i32 %i) { + store i32 %i, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 10), align 4 + ret void +} + +; CHECK-LABEL: store_test0_noinbounds: +; CHECK-NEXT: param i32{{$}} +; CHECK-NEXT: i32.const $push0=, 0{{$}} +; CHECK-NEXT: i32.store $drop=, g+40($pop0), $0{{$}} +; CHECK-NEXT: return{{$}} +define void @store_test0_noinbounds(i32 %i) { + store i32 %i, i32* getelementptr ([0 x i32], [0 x i32]* @g, i32 0, i32 10), align 4 + ret void +} + +; CHECK-LABEL: store_test1: +; 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{{$}} +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 + store i32 %i, i32* %arrayidx, align 4 + ret void +} + +; CHECK-LABEL: store_test2: +; 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{{$}} +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 + store i32 %i, i32* %arrayidx, align 4 + ret void +} + +; CHECK-LABEL: store_test3: +; 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{{$}} +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 + store i32 %i, i32* %add.ptr1, align 4 + ret void +} + +; CHECK-LABEL: store_test4: +; 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{{$}} +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 + ret void +} + +; CHECK-LABEL: store_test5: +; 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{{$}} +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 + ret void +} + +; CHECK-LABEL: store_test6: +; 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{{$}} +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 + store i32 %i, i32* %add.ptr, align 4 + ret void +} + +; CHECK-LABEL: store_test7: +; 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{{$}} +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 + store i32 %i, i32* %add.ptr1, align 4 + ret void +} + +; CHECK-LABEL: store_test8: +; 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{{$}} +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 + store i32 %i, i32* %add.ptr, align 4 + ret void +} + +; CHECK-LABEL: store_test9: +; CHECK-NEXT: param i32{{$}} +; CHECK-NEXT: i32.const $push0=, 0{{$}} +; CHECK-NEXT: i32.store $drop=, g-40($pop0), $0{{$}} +; CHECK-NEXT: return{{$}} +define void @store_test9(i32 %i) { + store i32 %i, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 1073741814), align 4 + ret void +} + +; CHECK-LABEL: store_test10: +; CHECK-NEXT: param i32, i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $0, $pop0{{$}} +; CHECK-NEXT: i32.const $push2=, g-40{{$}} +; CHECK-NEXT: i32.add $push3=, $pop1, $pop2{{$}} +; CHECK-NEXT: i32.store $drop=, 0($pop3), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @store_test10(i32 %n, i32 %i) { + %add = add nsw i32 %n, -10 + %arrayidx = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add + store i32 %i, i32* %arrayidx, align 4 + ret void +} + +; CHECK-LABEL: store_test11: +; CHECK-NEXT: param i32, i32{{$}} +; CHECK-NEXT: i32.store $drop=, 40($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @store_test11(i32* %p, i32 %i) { + %arrayidx = getelementptr inbounds i32, i32* %p, i32 10 + store i32 %i, i32* %arrayidx, align 4 + ret void +} + +; CHECK-LABEL: store_test11_noinbounds: +; CHECK-NEXT: param i32, i32{{$}} +; CHECK-NEXT: i32.const $push0=, 40{{$}} +; CHECK-NEXT: i32.add $push1=, $0, $pop0{{$}} +; CHECK-NEXT: i32.store $drop=, 0($pop1), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @store_test11_noinbounds(i32* %p, i32 %i) { + %arrayidx = getelementptr i32, i32* %p, i32 10 + store i32 %i, i32* %arrayidx, align 4 + ret void +} + +; CHECK-LABEL: store_test12: +; CHECK-NEXT: param i32, i32, i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.store $drop=, 0($pop4), $2{{$}} +; CHECK-NEXT: return{{$}} +define void @store_test12(i32* %p, i32 %n, i32 %i) { + %add = add nsw i32 %n, 10 + %arrayidx = getelementptr inbounds i32, i32* %p, i32 %add + store i32 %i, i32* %arrayidx, align 4 + ret void +} + +; CHECK-LABEL: store_test13: +; CHECK-NEXT: param i32, i32, i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.store $drop=, 0($pop4), $2{{$}} +; CHECK-NEXT: return{{$}} +define void @store_test13(i32* %p, i32 %n, i32 %i) { + %add = add nsw i32 10, %n + %arrayidx = getelementptr inbounds i32, i32* %p, i32 %add + store i32 %i, i32* %arrayidx, align 4 + ret void +} + +; CHECK-LABEL: store_test14: +; CHECK-NEXT: param i32, i32, i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.store $drop=, 40($pop2), $2{{$}} +; CHECK-NEXT: return{{$}} +define void @store_test14(i32* %p, i32 %n, i32 %i) { + %add.ptr = getelementptr inbounds i32, i32* %p, i32 %n + %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10 + store i32 %i, i32* %add.ptr1, align 4 + ret void +} + +; CHECK-LABEL: store_test15: +; CHECK-NEXT: param i32, i32, i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.store $drop=, 0($pop4), $2{{$}} +; CHECK-NEXT: return{{$}} +define void @store_test15(i32* %p, i32 %n, i32 %i) { + %add.ptr = getelementptr inbounds i32, i32* %p, i32 10 + %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 %n + store i32 %i, i32* %add.ptr1, align 4 + ret void +} + +; CHECK-LABEL: store_test16: +; CHECK-NEXT: param i32, i32, i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.store $drop=, 0($pop4), $2{{$}} +; CHECK-NEXT: return{{$}} +define void @store_test16(i32* %p, i32 %n, i32 %i) { + %add.ptr = getelementptr inbounds i32, i32* %p, i32 10 + %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 %n + store i32 %i, i32* %add.ptr1, align 4 + ret void +} + +; CHECK-LABEL: store_test17: +; CHECK-NEXT: param i32, i32, i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.store $drop=, 0($pop4), $2{{$}} +; CHECK-NEXT: return{{$}} +define void @store_test17(i32* %p, i32 %n, i32 %i) { + %add = add nsw i32 %n, 10 + %add.ptr = getelementptr inbounds i32, i32* %p, i32 %add + store i32 %i, i32* %add.ptr, align 4 + ret void +} + +; CHECK-LABEL: store_test18: +; CHECK-NEXT: param i32, i32, i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.store $drop=, 40($pop2), $2{{$}} +; CHECK-NEXT: return{{$}} +define void @store_test18(i32* %p, i32 %n, i32 %i) { + %add.ptr = getelementptr inbounds i32, i32* %p, i32 %n + %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10 + store i32 %i, i32* %add.ptr1, align 4 + ret void +} + +; CHECK-LABEL: store_test19: +; CHECK-NEXT: param i32, i32, i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, 40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.store $drop=, 0($pop4), $2{{$}} +; CHECK-NEXT: return{{$}} +define void @store_test19(i32* %p, i32 %n, i32 %i) { + %add = add nsw i32 10, %n + %add.ptr = getelementptr inbounds i32, i32* %p, i32 %add + store i32 %i, i32* %add.ptr, align 4 + ret void +} + +; CHECK-LABEL: store_test20: +; CHECK-NEXT: param i32, i32{{$}} +; CHECK-NEXT: i32.const $push0=, -40{{$}} +; CHECK-NEXT: i32.add $push1=, $0, $pop0{{$}} +; CHECK-NEXT: i32.store $drop=, 0($pop1), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @store_test20(i32* %p, i32 %i) { + %arrayidx = getelementptr inbounds i32, i32* %p, i32 -10 + store i32 %i, i32* %arrayidx, align 4 + ret void +} + +; CHECK-LABEL: store_test21: +; CHECK-NEXT: param i32, i32, i32{{$}} +; CHECK-NEXT: i32.const $push0=, 2{{$}} +; CHECK-NEXT: i32.shl $push1=, $1, $pop0{{$}} +; CHECK-NEXT: i32.add $push2=, $0, $pop1{{$}} +; CHECK-NEXT: i32.const $push3=, -40{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: i32.store $drop=, 0($pop4), $2{{$}} +; CHECK-NEXT: return{{$}} +define void @store_test21(i32* %p, i32 %n, i32 %i) { + %add = add nsw i32 %n, -10 + %arrayidx = getelementptr inbounds i32, i32* %p, i32 %add + store i32 %i, i32* %arrayidx, align 4 + ret void +} diff --git a/test/CodeGen/WebAssembly/byval.ll b/test/CodeGen/WebAssembly/byval.ll new file mode 100644 index 000000000000..ebbf50313a58 --- /dev/null +++ b/test/CodeGen/WebAssembly/byval.ll @@ -0,0 +1,131 @@ +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -verify-machineinstrs | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -verify-machineinstrs -fast-isel | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +%SmallStruct = type { i32 } +%OddStruct = type { i32, i8, i32 } +%AlignedStruct = type { double, double } +%BigStruct = type { double, double, double, double, double, double, double, double, double, double, double, i8, i8, i8 } +%EmptyStruct = type { } + +%BigArray = type { [33 x i8] } + +declare void @ext_func(%SmallStruct*) +declare void @ext_func_empty(%EmptyStruct* byval) +declare void @ext_byval_func(%SmallStruct* byval) +declare void @ext_byval_func_align8(%SmallStruct* byval align 8) +declare void @ext_byval_func_alignedstruct(%AlignedStruct* byval) +declare void @ext_byval_func_bigarray(%BigArray* byval) +declare void @ext_byval_func_empty(%EmptyStruct* byval) + +; CHECK-LABEL: byval_arg +define void @byval_arg(%SmallStruct* %ptr) { + ; CHECK: .param i32 + ; CHECK: i32.const $push[[L4:.+]]=, 0 + ; Subtract 16 from SP (SP is 16-byte aligned) + ; CHECK: i32.const $push[[L1:.+]]=, 0 + ; CHECK-NEXT: i32.load $push[[L2:.+]]=, __stack_pointer($pop[[L1]]) + ; CHECK-NEXT: i32.const $push[[L3:.+]]=, 16 + ; CHECK-NEXT: i32.sub $push[[L10:.+]]=, $pop[[L2]], $pop[[L3]] + ; Ensure SP is stored back before the call + ; CHECK-NEXT: i32.store $push[[L12:.+]]=, __stack_pointer($pop[[L4]]), $pop[[L10]]{{$}} + ; CHECK-NEXT: tee_local $push[[L11:.+]]=, $[[SP:.+]]=, $pop[[L12]]{{$}} + ; Copy the SmallStruct argument to the stack (SP+12, original SP-4) + ; CHECK-NEXT: i32.load $push[[L0:.+]]=, 0($0) + ; CHECK-NEXT: i32.store $drop=, 12($pop[[L11]]), $pop[[L0]] + ; Pass a pointer to the stack slot to the function + ; CHECK-NEXT: i32.const $push[[L5:.+]]=, 12{{$}} + ; CHECK-NEXT: i32.add $push[[ARG:.+]]=, $[[SP]], $pop[[L5]]{{$}} + ; CHECK-NEXT: call ext_byval_func@FUNCTION, $pop[[ARG]]{{$}} + call void @ext_byval_func(%SmallStruct* byval %ptr) + ; Restore the stack + ; CHECK-NEXT: i32.const $push[[L7:.+]]=, 0 + ; CHECK-NEXT: i32.const $push[[L6:.+]]=, 16 + ; CHECK-NEXT: i32.add $push[[L8:.+]]=, $[[SP]], $pop[[L6]] + ; CHECK-NEXT: i32.store {{.*}}=, __stack_pointer($pop[[L7]]), $pop[[L8]] + ; CHECK-NEXT: return + ret void +} + +; CHECK-LABEL: byval_arg_align8 +define void @byval_arg_align8(%SmallStruct* %ptr) { + ; CHECK: .param i32 + ; Don't check the entire SP sequence, just enough to get the alignment. + ; CHECK: i32.const $push[[L1:.+]]=, 16 + ; CHECK-NEXT: i32.sub $push[[L10:.+]]=, {{.+}}, $pop[[L1]] + ; CHECK-NEXT: i32.store $push[[L12:.+]]=, __stack_pointer($pop{{.+}}), $pop[[L10]]{{$}} + ; CHECK-NEXT: tee_local $push[[L11:.+]]=, $[[SP:.+]]=, $pop[[L12]]{{$}} + ; Copy the SmallStruct argument to the stack (SP+8, original SP-8) + ; CHECK-NEXT: i32.load $push[[L0:.+]]=, 0($0){{$}} + ; CHECK-NEXT: i32.store $drop=, 8($pop[[L11]]), $pop[[L0]]{{$}} + ; Pass a pointer to the stack slot to the function + ; CHECK-NEXT: i32.const $push[[L5:.+]]=, 8{{$}} + ; CHECK-NEXT: i32.add $push[[ARG:.+]]=, $[[SP]], $pop[[L5]]{{$}} + ; CHECK-NEXT: call ext_byval_func_align8@FUNCTION, $pop[[ARG]]{{$}} + call void @ext_byval_func_align8(%SmallStruct* byval align 8 %ptr) + ret void +} + +; CHECK-LABEL: byval_arg_double +define void @byval_arg_double(%AlignedStruct* %ptr) { + ; CHECK: .param i32 + ; Subtract 16 from SP (SP is 16-byte aligned) + ; CHECK: i32.const $push[[L1:.+]]=, 16 + ; CHECK-NEXT: i32.sub $push[[L12:.+]]=, {{.+}}, $pop[[L1]] + ; CHECK-NEXT: i32.store $push[[L15:.+]]=, {{.+}}, $pop[[L12]] + ; CHECK-NEXT: tee_local $push[[L14:.+]]=, $[[SP:.+]]=, $pop[[L15]] + ; Copy the AlignedStruct argument to the stack (SP+0, original SP-16) + ; Just check the last load/store pair of the memcpy + ; CHECK: i64.load $push[[L4:.+]]=, 0($0) + ; CHECK-NEXT: i64.store $drop=, 0($[[SP]]), $pop[[L4]] + ; Pass a pointer to the stack slot to the function + ; CHECK-NEXT: call ext_byval_func_alignedstruct@FUNCTION, $[[SP]] + tail call void @ext_byval_func_alignedstruct(%AlignedStruct* byval %ptr) + ret void +} + +; CHECK-LABEL: byval_param +define void @byval_param(%SmallStruct* byval align 32 %ptr) { + ; CHECK: .param i32 + ; %ptr is just a pointer to a struct, so pass it directly through + ; CHECK: call ext_func@FUNCTION, $0 + call void @ext_func(%SmallStruct* %ptr) + ret void +} + +; CHECK-LABEL: byval_empty_caller +define void @byval_empty_caller(%EmptyStruct* %ptr) { + ; CHECK: .param i32 + ; CHECK: call ext_byval_func_empty@FUNCTION, $0 + call void @ext_byval_func_empty(%EmptyStruct* byval %ptr) + ret void +} + +; CHECK-LABEL: byval_empty_callee +define void @byval_empty_callee(%EmptyStruct* byval %ptr) { + ; CHECK: .param i32 + ; CHECK: call ext_func_empty@FUNCTION, $0 + call void @ext_func_empty(%EmptyStruct* %ptr) + ret void +} + +; Call memcpy for "big" byvals. +; CHECK-LABEL: big_byval: +; CHECK: i32.const $push[[L4:.+]]=, 0 +; CHECK: i32.const $push[[L1:.+]]=, 0 +; CHECK-NEXT: i32.load $push[[L2:.+]]=, __stack_pointer($pop[[L1]]) +; CHECK-NEXT: i32.const $push[[L3:.+]]=, 131072 +; CHECK-NEXT: i32.sub $push[[L8:.+]]=, $pop[[L2]], $pop[[L3]] +; CHECK-NEXT: i32.store $push[[L12:.+]]=, __stack_pointer($pop[[L4]]), $pop[[L8]]{{$}} +; CHECK-NEXT: i32.const $push[[L0:.+]]=, 131072 +; CHECK-NEXT: i32.call $push[[L11:.+]]=, memcpy@FUNCTION, $pop{{.+}}, ${{.+}}, $pop{{.+}} +; CHECK-NEXT: tee_local $push[[L9:.+]]=, $[[SP:.+]]=, $pop[[L11]]{{$}} +; CHECK-NEXT: call big_byval_callee@FUNCTION, +%big = type [131072 x i8] +declare void @big_byval_callee(%big* byval align 1) +define void @big_byval(%big* byval align 1 %x) { + call void @big_byval_callee(%big* byval align 1 %x) + ret void +} diff --git a/test/CodeGen/WebAssembly/call.ll b/test/CodeGen/WebAssembly/call.ll index 6d5542c89d3d..bd5f7b80edba 100644 --- a/test/CodeGen/WebAssembly/call.ll +++ b/test/CodeGen/WebAssembly/call.ll @@ -1,4 +1,5 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -fast-isel -fast-isel-abort=1 | FileCheck %s ; Test that basic call operations assemble as expected. @@ -120,7 +121,7 @@ define void @coldcc_tail_call_void_nullary() { ret void } -; FIXME test the following: +; TODO: test the following: ; - More argument combinations. ; - Tail call. ; - Interesting returns (struct, multiple). diff --git a/test/CodeGen/WebAssembly/cfg-stackify.ll b/test/CodeGen/WebAssembly/cfg-stackify.ll index f0e5f4471678..d46898b44703 100644 --- a/test/CodeGen/WebAssembly/cfg-stackify.ll +++ b/test/CodeGen/WebAssembly/cfg-stackify.ll @@ -1,8 +1,11 @@ -; RUN: llc < %s -asm-verbose=false -disable-block-placement -verify-machineinstrs | FileCheck %s -; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck -check-prefix=OPT %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-block-placement -verify-machineinstrs -fast-isel=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -verify-machineinstrs -fast-isel=false | FileCheck -check-prefix=OPT %s ; Test the CFG stackifier pass. +; Explicitly disable fast-isel, since it gets implicitly enabled in the +; optnone test. + target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" @@ -12,19 +15,23 @@ declare void @something() ; CHECK-LABEL: test0: ; CHECK: loop -; CHECK-NOT: br -; CHECK: i32.add -; CHECK-NEXT: i32.ge_s +; CHECK-NEXT: block +; CHECK-NEXT: i32.const +; CHECK-NEXT: i32.add +; CHECK: i32.lt_s ; CHECK-NEXT: br_if -; CHECK-NOT: br -; CHECK: call -; CHECK: br 0{{$}} -; CHECK: return{{$}} +; CHECK-NEXT: return +; CHECK-NEXT: .LBB0_3: +; CHECK-NEXT: end_block +; CHECK-NEXT: call +; CHECK-NEXT: br +; CHECK-NEXT: .LBB0_4: +; CHECK-NEXT: end_loop ; OPT-LABEL: test0: ; OPT: loop -; OPT-NOT: br -; OPT: i32.add -; OPT-NEXT: i32.ge_s +; OPT-NEXT: i32.const +; OPT-NEXT: i32.add +; OPT: i32.ge_s ; OPT-NEXT: br_if ; OPT-NOT: br ; OPT: call @@ -53,19 +60,23 @@ back: ; CHECK-LABEL: test1: ; CHECK: loop -; CHECK-NOT: br -; CHECK: i32.add -; CHECK-NEXT: i32.ge_s +; CHECK-NEXT: block +; CHECK-NEXT: i32.const +; CHECK-NEXT: i32.add +; CHECK: i32.lt_s ; CHECK-NEXT: br_if -; CHECK-NOT: br -; CHECK: call -; CHECK: br 0{{$}} -; CHECK: return{{$}} +; CHECK-NEXT: return +; CHECK-NEXT: .LBB1_3: +; CHECK-NEXT: end_block +; CHECK-NEXT: call +; CHECK-NEXT: br +; CHECK-NEXT: .LBB1_4: +; CHECK-NEXT: end_loop ; OPT-LABEL: test1: ; OPT: loop -; OPT-NOT: br -; OPT: i32.add -; OPT-NEXT: i32.ge_s +; OPT-NEXT: i32.const +; OPT-NEXT: i32.add +; OPT: i32.ge_s ; OPT-NEXT: br_if ; OPT-NOT: br ; OPT: call @@ -95,18 +106,24 @@ back: ; CHECK-LABEL: test2: ; CHECK-NOT: local ; CHECK: block{{$}} -; CHECK: br_if {{[^,]*}}, 0{{$}} -; CHECK: .LBB2_1: -; CHECK: br_if ${{[0-9]+}}, 0{{$}} -; CHECK: .LBB2_2: +; CHECK: br_if 0, {{[^,]+}}{{$}} +; CHECK: .LBB2_{{[0-9]+}}: +; CHECK: loop +; CHECK: br_if 0, $pop{{[0-9]+}}{{$}} +; CHECK: .LBB2_{{[0-9]+}}: +; CHECK: end_loop +; CHECK: end_block ; CHECK: return{{$}} ; OPT-LABEL: test2: ; OPT-NOT: local ; OPT: block{{$}} -; OPT: br_if {{[^,]*}}, 0{{$}} -; OPT: .LBB2_1: -; OPT: br_if ${{[0-9]+}}, 0{{$}} -; OPT: .LBB2_2: +; OPT: br_if 0, {{[^,]+}}{{$}} +; OPT: .LBB2_{{[0-9]+}}: +; OPT: loop +; OPT: br_if 0, $pop{{[0-9]+}}{{$}} +; OPT: .LBB2_{{[0-9]+}}: +; OPT: end_loop +; OPT: end_block ; OPT: return{{$}} define void @test2(double* nocapture %p, i32 %n) { entry: @@ -136,28 +153,33 @@ for.end: ; CHECK-LABEL: doublediamond: ; CHECK: block{{$}} ; CHECK-NEXT: block{{$}} -; CHECK: br_if ${{[^,]*}}, 0{{$}} +; CHECK: br_if 0, ${{[^,]+}}{{$}} ; CHECK: br 1{{$}} ; CHECK: .LBB3_2: ; CHECK-NEXT: end_block{{$}} ; CHECK: block{{$}} -; CHECK: br_if ${{[^,]*}}, 0{{$}} +; CHECK: br_if 0, ${{[^,]+}}{{$}} ; CHECK: br 1{{$}} ; CHECK: .LBB3_4: ; CHECK-NEXT: end_block{{$}} ; CHECK: .LBB3_5: ; CHECK-NEXT: end_block{{$}} -; CHECK: return ${{[0-9]+}}{{$}} +; CHECK: i32.const $push{{[0-9]+}}=, 0{{$}} +; CHECK-NEXT: return $pop{{[0-9]+}}{{$}} ; OPT-LABEL: doublediamond: -; OPT: block{{$}} +; OPT: block{{$}} ; OPT-NEXT: block{{$}} -; OPT: br_if ${{[^,]*}}, 0{{$}} -; OPT: block{{$}} -; OPT: br_if ${{[^,]*}}, 0{{$}} -; OPT: br 1{{$}} -; OPT: .LBB3_4: -; OPT: .LBB3_5: -; OPT: return ${{[0-9]+}}{{$}} +; OPT-NEXT: block{{$}} +; OPT: br_if 0, ${{[^,]+}}{{$}} +; OPT: br_if 1, ${{[^,]+}}{{$}} +; OPT: br 2{{$}} +; OPT-NEXT: .LBB3_3: +; OPT-NEXT: end_block +; OPT: br 1{{$}} +; OPT-NEXT: .LBB3_4: +; OPT: .LBB3_5: +; OPT-NEXT: end_block +; OPT: return $pop{{[0-9]+}}{{$}} define i32 @doublediamond(i32 %a, i32 %b, i32* %p) { entry: %c = icmp eq i32 %a, 0 @@ -183,12 +205,12 @@ exit: ; CHECK-LABEL: triangle: ; CHECK: block{{$}} -; CHECK: br_if $1, 0{{$}} +; CHECK: br_if 0, $1{{$}} ; CHECK: .LBB4_2: ; CHECK: return ${{[0-9]+}}{{$}} ; OPT-LABEL: triangle: ; OPT: block{{$}} -; OPT: br_if $1, 0{{$}} +; OPT: br_if 0, $1{{$}} ; OPT: .LBB4_2: ; OPT: return ${{[0-9]+}}{{$}} define i32 @triangle(i32* %p, i32 %a) { @@ -207,19 +229,21 @@ exit: ; CHECK-LABEL: diamond: ; CHECK: block{{$}} ; CHECK: block{{$}} -; CHECK: br_if $1, 0{{$}} +; CHECK: br_if 0, $1{{$}} ; CHECK: br 1{{$}} ; CHECK: .LBB5_2: ; CHECK: .LBB5_3: -; CHECK: return ${{[0-9]+}}{{$}} +; CHECK: i32.const $push{{[0-9]+}}=, 0{{$}} +; CHECK-NEXT: return $pop{{[0-9]+}}{{$}} ; OPT-LABEL: diamond: ; OPT: block{{$}} ; OPT: block{{$}} -; OPT: br_if {{[^,]*}}, 0{{$}} +; OPT: br_if 0, {{[^,]+}}{{$}} ; OPT: br 1{{$}} ; OPT: .LBB5_2: ; OPT: .LBB5_3: -; OPT: return ${{[0-9]+}}{{$}} +; OPT: i32.const $push{{[0-9]+}}=, 0{{$}} +; OPT-NEXT: return $pop{{[0-9]+}}{{$}} define i32 @diamond(i32* %p, i32 %a) { entry: %c = icmp eq i32 %a, 0 @@ -251,13 +275,13 @@ entry: ; CHECK-LABEL: minimal_loop: ; CHECK-NOT: br ; CHECK: .LBB7_1: -; CHECK: i32.store $discard=, 0($0), $pop{{[0-9]+}}{{$}} +; CHECK: i32.store $drop=, 0($0), $pop{{[0-9]+}}{{$}} ; CHECK: br 0{{$}} ; CHECK: .LBB7_2: ; OPT-LABEL: minimal_loop: ; OPT-NOT: br ; OPT: .LBB7_1: -; OPT: i32.store $discard=, 0($0), $pop{{[0-9]+}}{{$}} +; OPT: i32.store $drop=, 0($0), $pop{{[0-9]+}}{{$}} ; OPT: br 0{{$}} ; OPT: .LBB7_2: define i32 @minimal_loop(i32* %p) { @@ -273,16 +297,18 @@ loop: ; CHECK-NOT: br ; CHECK: .LBB8_1: ; CHECK: loop{{$}} -; CHECK: br_if $pop{{[0-9]+}}, 0{{$}} +; CHECK: br_if 0, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: end_loop{{$}} -; CHECK: return ${{[0-9]+}}{{$}} +; CHECK: i32.const $push{{[0-9]+}}=, 0{{$}} +; CHECK-NEXT: return $pop{{[0-9]+}}{{$}} ; OPT-LABEL: simple_loop: ; OPT-NOT: br ; OPT: .LBB8_1: ; OPT: loop{{$}} -; OPT: br_if {{[^,]*}}, 0{{$}} +; OPT: br_if 0, {{[^,]+}}{{$}} ; OPT-NEXT: end_loop{{$}} -; OPT: return ${{[0-9]+}}{{$}} +; OPT: i32.const $push{{[0-9]+}}=, 0{{$}} +; OPT-NEXT: return $pop{{[0-9]+}}{{$}} define i32 @simple_loop(i32* %p, i32 %a) { entry: %c = icmp eq i32 %a, 0 @@ -298,17 +324,17 @@ exit: ; CHECK-LABEL: doubletriangle: ; CHECK: block{{$}} -; CHECK: br_if $0, 0{{$}} +; CHECK: br_if 0, $0{{$}} ; CHECK: block{{$}} -; CHECK: br_if $1, 0{{$}} +; CHECK: br_if 0, $1{{$}} ; CHECK: .LBB9_3: ; CHECK: .LBB9_4: ; CHECK: return ${{[0-9]+}}{{$}} ; OPT-LABEL: doubletriangle: ; OPT: block{{$}} -; OPT: br_if $0, 0{{$}} +; OPT: br_if 0, $0{{$}} ; OPT: block{{$}} -; OPT: br_if $1, 0{{$}} +; OPT: br_if 0, $1{{$}} ; OPT: .LBB9_3: ; OPT: .LBB9_4: ; OPT: return ${{[0-9]+}}{{$}} @@ -335,21 +361,23 @@ exit: ; CHECK-LABEL: ifelse_earlyexits: ; CHECK: block{{$}} ; CHECK: block{{$}} -; CHECK: br_if $0, 0{{$}} +; CHECK: br_if 0, $0{{$}} ; CHECK: br 1{{$}} ; CHECK: .LBB10_2: -; CHECK: br_if $1, 0{{$}} +; CHECK: br_if 0, $1{{$}} ; CHECK: .LBB10_4: -; CHECK: return ${{[0-9]+}}{{$}} +; CHECK: i32.const $push{{[0-9]+}}=, 0{{$}} +; CHECK-NEXT: return $pop{{[0-9]+}}{{$}} ; OPT-LABEL: ifelse_earlyexits: ; OPT: block{{$}} ; OPT: block{{$}} -; OPT: br_if {{[^,]*}}, 0{{$}} -; OPT: br_if $1, 1{{$}} +; OPT: br_if 0, {{[^,]+}}{{$}} +; OPT: br_if 1, $1{{$}} ; OPT: br 1{{$}} ; OPT: .LBB10_3: ; OPT: .LBB10_4: -; OPT: return ${{[0-9]+}}{{$}} +; OPT: i32.const $push{{[0-9]+}}=, 0{{$}} +; OPT-NEXT: return $pop{{[0-9]+}}{{$}} define i32 @ifelse_earlyexits(i32 %a, i32 %b, i32* %p) { entry: %c = icmp eq i32 %a, 0 @@ -374,36 +402,32 @@ exit: ; CHECK: .LBB11_1: ; CHECK: loop{{$}} ; CHECK: block{{$}} -; CHECK: block{{$}} -; CHECK: br_if $0, 0{{$}} +; CHECK: br_if 0, $0{{$}} ; CHECK: br 1{{$}} ; CHECK: .LBB11_3: +; CHECK: end_block{{$}} ; CHECK: block{{$}} -; CHECK: br_if $1, 0{{$}} +; CHECK: br_if 0, $1{{$}} ; CHECK: br 1{{$}} ; CHECK: .LBB11_5: -; CHECK: .LBB11_6: ; CHECK: br 0{{$}} -; CHECK: .LBB11_7: +; CHECK: .LBB11_6: ; CHECK-NEXT: end_loop{{$}} ; OPT-LABEL: doublediamond_in_a_loop: -; OPT: .LBB11_1: -; OPT: loop{{$}} -; OPT: block{{$}} -; OPT: block{{$}} -; OPT: br_if {{[^,]*}}, 0{{$}} -; OPT: block{{$}} -; OPT: br_if {{[^,]*}}, 0{{$}} -; OPT: br 2{{$}} -; OPT: .LBB11_4: -; OPT-NEXT: end_block{{$}} -; OPT: br 1{{$}} -; OPT: .LBB11_5: +; OPT: .LBB11_1: +; OPT: loop{{$}} +; OPT: block{{$}} +; OPT: br_if 0, {{[^,]+}}{{$}} +; OPT: block{{$}} +; OPT: br_if 0, {{[^,]+}}{{$}} +; OPT: br 2{{$}} +; OPT-NEXT: .LBB11_4: ; OPT-NEXT: end_block{{$}} -; OPT: .LBB11_6: +; OPT: br 1{{$}} +; OPT: .LBB11_5: ; OPT-NEXT: end_block{{$}} -; OPT: br 0{{$}} -; OPT: .LBB11_7: +; OPT: br 0{{$}} +; OPT: .LBB11_6: ; OPT-NEXT: end_loop{{$}} define i32 @doublediamond_in_a_loop(i32 %a, i32 %b, i32* %p) { entry: @@ -438,10 +462,26 @@ exit: ; CHECK-NEXT: .LBB{{[0-9]+}}_{{[0-9]+}}: ; CHECK-NEXT: loop ; OPT-LABEL: test3: -; OPT: loop +; OPT: block +; OPT: br_if +; OPT: .LBB{{[0-9]+}}_{{[0-9]+}}: +; OPT-NEXT: loop +; OPT-NEXT: block +; OPT-NEXT: block ; OPT-NEXT: br_if ; OPT-NEXT: .LBB{{[0-9]+}}_{{[0-9]+}}: ; OPT-NEXT: loop +; OPT: br_if +; OPT-NEXT: br +; OPT-NEXT: .LBB{{[0-9]+}}_{{[0-9]+}}: +; OPT-NEXT: end_loop +; OPT-NEXT: end_block +; OPT-NEXT: unreachable +; OPT-NEXT: .LBB{{[0-9]+}}_{{[0-9]+}}: +; OPT-NEXT: end_block +; OPT: br +; OPT-NEXT: .LBB{{[0-9]+}}_{{[0-9]+}}: +; OPT-NEXT: end_loop declare void @bar() define void @test3(i32 %w) { entry: @@ -475,44 +515,36 @@ if.end: ; CHECK-NEXT: .param i32{{$}} ; CHECK: block{{$}} ; CHECK-NEXT: block{{$}} -; CHECK-NEXT: block{{$}} -; CHECK: br_if $pop{{[0-9]*}}, 0{{$}} -; CHECK-NEXT: block{{$}} -; CHECK: br_if $pop{{[0-9]*}}, 0{{$}} -; CHECK: br_if $pop{{[0-9]*}}, 2{{$}} +; CHECK: br_if 0, $pop{{[0-9]+}}{{$}} +; CHECK: br_if 1, $pop{{[0-9]+}}{{$}} +; CHECK: br 1{{$}} ; CHECK-NEXT: .LBB13_3: ; CHECK-NEXT: end_block{{$}} -; CHECK-NEXT: return{{$}} -; CHECK-NEXT: .LBB13_4: -; CHECK: br_if $pop{{[0-9]*}}, 1{{$}} -; CHECK: br_if $pop{{[0-9]*}}, 0{{$}} -; CHECK-NEXT: return{{$}} -; CHECK-NEXT: .LBB13_7: +; CHECK-NEXT: block{{$}} +; CHECK: br_if 0, $pop{{[0-9]+}}{{$}} +; CHECK: br_if 1, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: .LBB13_5: ; CHECK-NEXT: end_block{{$}} ; CHECK-NEXT: return{{$}} -; CHECK-NEXT: .LBB13_8: +; CHECK-NEXT: .LBB13_6: ; CHECK-NEXT: end_block{{$}} ; CHECK-NEXT: return{{$}} ; OPT-LABEL: test4: ; OPT-NEXT: .param i32{{$}} ; OPT: block{{$}} ; OPT-NEXT: block{{$}} -; OPT-NEXT: block{{$}} -; OPT: br_if $pop{{[0-9]*}}, 0{{$}} -; OPT-NEXT: block{{$}} -; OPT: br_if $pop{{[0-9]*}}, 0{{$}} -; OPT: br_if $pop{{[0-9]*}}, 2{{$}} +; OPT: br_if 0, $pop{{[0-9]+}}{{$}} +; OPT: br_if 1, $pop{{[0-9]+}}{{$}} +; OPT: br 1{{$}} ; OPT-NEXT: .LBB13_3: ; OPT-NEXT: end_block{{$}} -; OPT-NEXT: return{{$}} -; OPT-NEXT: .LBB13_4: -; OPT: br_if $pop{{[0-9]*}}, 1{{$}} -; OPT: br_if $pop{{[0-9]*}}, 0{{$}} -; OPT-NEXT: return{{$}} -; OPT-NEXT: .LBB13_7: +; OPT-NEXT: block{{$}} +; OPT: br_if 0, $pop{{[0-9]+}}{{$}} +; OPT: br_if 1, $pop{{[0-9]+}}{{$}} +; OPT-NEXT: .LBB13_5: ; OPT-NEXT: end_block{{$}} ; OPT-NEXT: return{{$}} -; OPT-NEXT: .LBB13_8: +; OPT-NEXT: .LBB13_6: ; OPT-NEXT: end_block{{$}} ; OPT-NEXT: return{{$}} define void @test4(i32 %t) { @@ -544,8 +576,8 @@ default: ; CHECK: .LBB14_1: ; CHECK-NEXT: block{{$}} ; CHECK-NEXT: loop{{$}} -; CHECK: br_if {{[^,]*}}, 2{{$}} -; CHECK: br_if {{[^,]*}}, 0{{$}} +; CHECK: br_if 2, {{[^,]+}}{{$}} +; CHECK: br_if 0, {{[^,]+}}{{$}} ; CHECK-NEXT: end_loop{{$}} ; CHECK: return{{$}} ; CHECK-NEXT: .LBB14_4: @@ -554,8 +586,8 @@ default: ; OPT: .LBB14_1: ; OPT-NEXT: block{{$}} ; OPT-NEXT: loop{{$}} -; OPT: br_if {{[^,]*}}, 2{{$}} -; OPT: br_if {{[^,]*}}, 0{{$}} +; OPT: br_if 2, {{[^,]+}}{{$}} +; OPT: br_if 0, {{[^,]+}}{{$}} ; OPT-NEXT: end_loop{{$}} ; OPT: return{{$}} ; OPT-NEXT: .LBB14_4: @@ -591,11 +623,11 @@ return: ; CHECK-NEXT: block{{$}} ; CHECK-NEXT: loop{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 3{{$}} +; CHECK: br_if 3, {{[^,]+}}{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 2{{$}} +; CHECK: br_if 2, {{[^,]+}}{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 0{{$}} +; CHECK: br_if 0, {{[^,]+}}{{$}} ; CHECK-NEXT: end_loop{{$}} ; CHECK-NOT: block ; CHECK: return{{$}} @@ -612,11 +644,11 @@ return: ; OPT-NEXT: block{{$}} ; OPT-NEXT: loop{{$}} ; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 3{{$}} +; OPT: br_if 3, {{[^,]+}}{{$}} ; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 2{{$}} +; OPT: br_if 2, {{[^,]+}}{{$}} ; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 0{{$}} +; OPT: br_if 0, {{[^,]+}}{{$}} ; OPT-NEXT: end_loop{{$}} ; OPT-NOT: block ; OPT: return{{$}} @@ -664,34 +696,38 @@ second: ; CHECK-NEXT: loop{{$}} ; CHECK-NOT: block ; CHECK: block{{$}} -; CHECK: br_if {{[^,]*}}, 0{{$}} +; CHECK: br_if 0, {{[^,]+}}{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 1{{$}} +; CHECK: br_if 1, {{[^,]+}}{{$}} ; CHECK-NOT: block ; CHECK: unreachable ; CHECK-NEXT: .LBB16_4: ; CHECK-NEXT: end_block{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 0{{$}} +; CHECK: br_if 0, {{[^,]+}}{{$}} ; CHECK-NEXT: end_loop{{$}} ; CHECK-NOT: block ; CHECK: unreachable ; OPT-LABEL: test7: ; OPT: .LBB16_1: +; OPT-NEXT: block ; OPT-NEXT: loop{{$}} ; OPT-NOT: block ; OPT: block{{$}} ; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 0{{$}} +; OPT: br_if 0, {{[^,]+}}{{$}} ; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 1{{$}} +; OPT: br_if 1, {{[^,]+}}{{$}} +; OPT: br 3{{$}} +; OPT-NEXT: .LBB16_3: +; OPT-NEXT: end_block ; OPT-NOT: block -; OPT: unreachable -; OPT-NEXT: .LBB16_4: -; OPT-NEXT: end_block{{$}} +; OPT: br_if 0, {{[^,]+}}{{$}} +; OPT-NEXT: end_loop ; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 0{{$}} -; OPT-NEXT: end_loop{{$}} +; OPT: unreachable +; OPT-NEXT: .LBB16_5: +; OPT-NEXT: end_block ; OPT-NOT: block ; OPT: unreachable define void @test7(i1 %tobool2, i1 %tobool9) { @@ -725,31 +761,19 @@ u1: ; CHECK-LABEL: test8: ; CHECK: .LBB17_1: ; CHECK-NEXT: loop{{$}} -; CHECK-NEXT: block{{$}} -; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 0{{$}} -; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 1{{$}} -; CHECK-NEXT: .LBB17_3: -; CHECK-NEXT: end_block{{$}} -; CHECK-NEXT: loop{{$}} -; CHECK-NEXT: br_if {{[^,]*}}, 0{{$}} -; CHECK-NEXT: br 2{{$}} -; CHECK-NEXT: .LBB17_4: +; CHECK-NEXT: i32.const $push{{[^,]+}}, 0{{$}} +; CHECK-NEXT: br_if 0, {{[^,]+}}{{$}} +; CHECK-NEXT: br 0{{$}} +; CHECK-NEXT: .LBB17_2: +; CHECK-NEXT: end_loop{{$}} ; OPT-LABEL: test8: ; OPT: .LBB17_1: ; OPT-NEXT: loop{{$}} -; OPT-NEXT: block{{$}} -; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 0{{$}} -; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 1{{$}} -; OPT-NEXT: .LBB17_3: -; OPT-NEXT: end_block{{$}} -; OPT-NEXT: loop{{$}} -; OPT-NEXT: br_if {{[^,]*}}, 0{{$}} -; OPT-NEXT: br 2{{$}} -; OPT-NEXT: .LBB17_4: +; OPT-NEXT: i32.const $push{{[^,]+}}, 0{{$}} +; OPT-NEXT: br_if 0, {{[^,]+}}{{$}} +; OPT-NEXT: br 0{{$}} +; OPT-NEXT: .LBB17_2: +; OPT-NEXT: end_loop{{$}} define i32 @test8() { bb: br label %bb1 @@ -774,21 +798,21 @@ bb3: ; CHECK: .LBB18_1: ; CHECK-NEXT: loop{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 1{{$}} +; CHECK: br_if 1, {{[^,]+}}{{$}} ; CHECK-NEXT: .LBB18_2: ; CHECK-NEXT: loop{{$}} ; CHECK-NOT: block ; CHECK: block{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 0{{$}} +; CHECK: br_if 0, {{[^,]+}}{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 1{{$}} -; CHECK-NEXT: br 3{{$}} +; CHECK: br_if 3, {{[^,]+}}{{$}} +; CHECK-NEXT: br 1{{$}} ; CHECK-NEXT: .LBB18_4: ; CHECK-NEXT: end_block{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 0{{$}} -; CHECK-NEXT: br 2{{$}} +; CHECK: br_if 2, {{[^,]+}}{{$}} +; CHECK-NEXT: br 0{{$}} ; CHECK-NEXT: .LBB18_5: ; CHECK-NOT: block ; CHECK: return{{$}} @@ -796,20 +820,20 @@ bb3: ; OPT: .LBB18_1: ; OPT-NEXT: loop{{$}} ; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 1{{$}} +; OPT: br_if 1, {{[^,]+}}{{$}} ; OPT-NEXT: .LBB18_2: ; OPT-NEXT: loop{{$}} ; OPT-NOT: block ; OPT: block{{$}} ; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 0{{$}} +; OPT: br_if 0, {{[^,]+}}{{$}} ; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 1{{$}} +; OPT: br_if 1, {{[^,]+}}{{$}} ; OPT-NEXT: br 3{{$}} ; OPT-NEXT: .LBB18_4: ; OPT-NEXT: end_block{{$}} ; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 0{{$}} +; OPT: br_if 0, {{[^,]+}}{{$}} ; OPT-NEXT: br 2{{$}} ; OPT-NEXT: .LBB18_5: ; OPT-NOT: block @@ -852,50 +876,50 @@ end: ; CHECK: .LBB19_1: ; CHECK-NEXT: loop{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 0{{$}} -; CHECK-NEXT: .LBB19_2: +; CHECK: br_if 0, {{[^,]+}}{{$}} +; CHECK: .LBB19_3: ; CHECK-NEXT: block{{$}} ; CHECK-NEXT: loop{{$}} ; CHECK-NOT: block -; CHECK: .LBB19_3: +; CHECK: .LBB19_4: ; CHECK-NEXT: loop{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 5{{$}} +; CHECK: br_if 5, {{[^,]+}}{{$}} ; CHECK-NOT: block -; CHECK: tableswitch {{[^,]*}}, 0, 0, 1, 5, 2, 4{{$}} -; CHECK-NEXT: .LBB19_5: +; CHECK: br_table {{[^,]+}}, 0, 1, 5, 2, 4, 0{{$}} +; CHECK-NEXT: .LBB19_6: ; CHECK-NEXT: end_loop{{$}} ; CHECK-NEXT: end_loop{{$}} ; CHECK-NEXT: return{{$}} -; CHECK-NEXT: .LBB19_6: +; CHECK-NEXT: .LBB19_7: ; CHECK-NEXT: end_block{{$}} ; CHECK-NOT: block ; CHECK: br 0{{$}} -; CHECK-NEXT: .LBB19_7: +; CHECK-NEXT: .LBB19_8: ; OPT-LABEL: test10: ; OPT: .LBB19_1: ; OPT-NEXT: loop{{$}} ; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 0{{$}} -; OPT-NEXT: .LBB19_2: +; OPT: br_if 0, {{[^,]+}}{{$}} +; OPT: .LBB19_3: ; OPT-NEXT: block{{$}} ; OPT-NEXT: loop{{$}} ; OPT-NOT: block -; OPT: .LBB19_3: +; OPT: .LBB19_4: ; OPT-NEXT: loop{{$}} ; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 5{{$}} +; OPT: br_if 5, {{[^,]+}}{{$}} ; OPT-NOT: block -; OPT: tableswitch {{[^,]*}}, 0, 0, 1, 5, 2, 4{{$}} -; OPT-NEXT: .LBB19_5: +; OPT: br_table {{[^,]+}}, 0, 1, 5, 2, 4, 0{{$}} +; OPT-NEXT: .LBB19_6: ; OPT-NEXT: end_loop{{$}} ; OPT-NEXT: end_loop{{$}} ; OPT-NEXT: return{{$}} -; OPT-NEXT: .LBB19_6: +; OPT-NEXT: .LBB19_7: ; OPT-NEXT: end_block{{$}} ; OPT-NOT: block ; OPT: br 0{{$}} -; OPT-NEXT: .LBB19_7: +; OPT-NEXT: .LBB19_8: define void @test10() { bb0: br label %bb1 @@ -938,12 +962,12 @@ bb6: ; CHECK-NEXT: block{{$}} ; CHECK-NEXT: block{{$}} ; CHECK-NEXT: block{{$}} -; CHECK-NEXT: br_if {{[^,]*}}, 0{{$}} -; CHECK-NEXT: block{{$}} +; CHECK: br_if 0, {{[^,]+}}{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 0{{$}} +; CHECK: block{{$}} +; CHECK-NEXT: br_if 0, {{[^,]+}}{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 2{{$}} +; CHECK: br_if 2, {{[^,]+}}{{$}} ; CHECK-NEXT: .LBB20_3: ; CHECK-NEXT: end_block{{$}} ; CHECK-NOT: block @@ -951,9 +975,9 @@ bb6: ; CHECK-NEXT: .LBB20_4: ; CHECK-NEXT: end_block{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 2{{$}} +; CHECK: br_if 1, {{[^,]+}}{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 1{{$}} +; CHECK: br_if 2, {{[^,]+}}{{$}} ; CHECK-NEXT: .LBB20_6: ; CHECK-NEXT: end_block{{$}} ; CHECK-NOT: block @@ -969,12 +993,12 @@ bb6: ; OPT-LABEL: test11: ; OPT: block{{$}} ; OPT-NEXT: block{{$}} -; OPT-NEXT: br_if $0, 0{{$}} -; OPT-NEXT: block{{$}} +; OPT: br_if 0, $pop{{[0-9]+}}{{$}} ; OPT-NOT: block -; OPT: br_if $0, 0{{$}} +; OPT: block{{$}} +; OPT-NEXT: br_if 0, $0{{$}} ; OPT-NOT: block -; OPT: br_if $0, 2{{$}} +; OPT: br_if 2, {{[^,]+}}{{$}} ; OPT-NEXT: .LBB20_3: ; OPT-NEXT: end_block{{$}} ; OPT-NOT: block @@ -984,13 +1008,13 @@ bb6: ; OPT-NOT: block ; OPT: block{{$}} ; OPT-NOT: block -; OPT: br_if $pop9, 0{{$}} +; OPT: br_if 0, $pop{{[0-9]+}}{{$}} ; OPT-NOT: block ; OPT: return{{$}} ; OPT-NEXT: .LBB20_6: ; OPT-NEXT: end_block{{$}} ; OPT-NOT: block -; OPT: br_if $0, 0{{$}} +; OPT: br_if 0, $pop{{[0-9]+}}{{$}} ; OPT-NOT: block ; OPT: return{{$}} ; OPT-NEXT: .LBB20_8: @@ -1033,54 +1057,49 @@ bb8: ; CHECK-NOT: block ; CHECK: block{{$}} ; CHECK-NEXT: block{{$}} -; CHECK-NEXT: block{{$}} -; CHECK: br_if {{[^,]*}}, 0{{$}} +; CHECK: br_if 0, {{[^,]+}}{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 2{{$}} +; CHECK: br_if 1, {{[^,]+}}{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 2{{$}} -; CHECK-NEXT: br 1{{$}} +; CHECK: br_if 1, {{[^,]+}}{{$}} +; CHECK-NEXT: br 3{{$}} ; CHECK-NEXT: .LBB21_4: ; CHECK-NEXT: end_block{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 1{{$}} +; CHECK: br_if 0, {{[^,]+}}{{$}} ; CHECK-NOT: block -; CHECK: br_if {{[^,]*}}, 1{{$}} +; CHECK: br_if 2, {{[^,]+}}{{$}} ; CHECK-NEXT: .LBB21_6: ; CHECK-NEXT: end_block{{$}} -; CHECK-NEXT: return{{$}} -; CHECK-NEXT: .LBB21_7: -; CHECK-NEXT: end_block{{$}} ; CHECK-NOT: block ; CHECK: br 0{{$}} -; CHECK-NEXT: .LBB21_8: +; CHECK-NEXT: .LBB21_7: +; CHECK-NEXT: end_loop{{$}} +; CHECK-NEXT: return{{$}} ; OPT-LABEL: test12: ; OPT: .LBB21_1: ; OPT-NEXT: loop{{$}} ; OPT-NOT: block ; OPT: block{{$}} ; OPT-NEXT: block{{$}} -; OPT-NEXT: block{{$}} -; OPT: br_if {{[^,]*}}, 0{{$}} +; OPT: br_if 0, {{[^,]+}}{{$}} ; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 2{{$}} +; OPT: br_if 1, {{[^,]+}}{{$}} ; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 2{{$}} -; OPT-NEXT: br 1{{$}} +; OPT: br_if 1, {{[^,]+}}{{$}} +; OPT-NEXT: br 3{{$}} ; OPT-NEXT: .LBB21_4: ; OPT-NEXT: end_block{{$}} ; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 1{{$}} +; OPT: br_if 0, {{[^,]+}}{{$}} ; OPT-NOT: block -; OPT: br_if {{[^,]*}}, 1{{$}} +; OPT: br_if 2, {{[^,]+}}{{$}} ; OPT-NEXT: .LBB21_6: ; OPT-NEXT: end_block{{$}} -; OPT-NEXT: return{{$}} -; OPT-NEXT: .LBB21_7: -; OPT-NEXT: end_block{{$}} -; OPT-NOT: block ; OPT: br 0{{$}} -; OPT-NEXT: .LBB21_8: +; OPT-NEXT: .LBB21_7: +; OPT-NEXT: end_loop{{$}} +; OPT-NEXT: return{{$}} define void @test12(i8* %arg) { bb: br label %bb1 @@ -1109,33 +1128,37 @@ bb7: ; optnone to disable optimizations to test this case. ; CHECK-LABEL: test13: -; CHECK-NEXT: local i32{{$}} +; CHECK-NEXT: .local i32{{$}} +; CHECK-NEXT: block{{$}} +; CHECK-NEXT: block{{$}} +; CHECK: br_if 0, $pop0{{$}} ; CHECK: block{{$}} -; CHECK: br_if $pop4, 0{{$}} -; CHECK-NEXT: return{{$}} -; CHECK-NEXT: .LBB22_2: +; CHECK: br_if 0, $pop3{{$}} +; CHECK: .LBB22_3: ; CHECK-NEXT: end_block{{$}} -; CHECK: block{{$}} -; CHECK-NEXT: br_if $0, 0{{$}} -; CHECK: .LBB22_4: +; CHECK: br_if 1, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: br 1{{$}} +; CHECK-NEXT: .LBB22_4: ; CHECK-NEXT: end_block{{$}} -; CHECK: block{{$}} -; CHECK: br_if $pop6, 0{{$}} +; CHECK-NEXT: return{{$}} +; CHECK-NEXT: .LBB22_5: ; CHECK-NEXT: end_block{{$}} ; CHECK-NEXT: unreachable{{$}} ; OPT-LABEL: test13: -; OPT-NEXT: local i32{{$}} -; OPT: block{{$}} -; OPT: br_if $pop4, 0{{$}} -; OPT-NEXT: return{{$}} -; OPT-NEXT: .LBB22_2: -; OPT-NEXT: end_block{{$}} +; OPT-NEXT: .local i32{{$}} +; OPT-NEXT: block{{$}} +; OPT-NEXT: block{{$}} +; OPT: br_if 0, $pop0{{$}} ; OPT: block{{$}} -; OPT-NEXT: br_if $0, 0{{$}} -; OPT: .LBB22_4: +; OPT: br_if 0, $pop3{{$}} +; OPT: .LBB22_3: ; OPT-NEXT: end_block{{$}} -; OPT: block{{$}} -; OPT: br_if $pop6, 0{{$}} +; OPT: br_if 1, $pop{{[0-9]+}}{{$}} +; OPT-NEXT: br 1{{$}} +; OPT-NEXT: .LBB22_4: +; OPT-NEXT: end_block +; OPT-NEXT: return +; OPT-NEXT: .LBB22_5: ; OPT-NEXT: end_block{{$}} ; OPT-NEXT: unreachable{{$}} define void @test13() noinline optnone { @@ -1159,15 +1182,15 @@ bb5: ; before the loop for the second. ; CHECK-LABEL: test14: -; CHECK-NEXT: local i32{{$}} -; CHECK-NEXT: i32.const $0=, 0{{$}} ; CHECK-NEXT: .LBB23_1:{{$}} ; CHECK-NEXT: loop{{$}} -; CHECK-NEXT: br_if $0, 0{{$}} -; CHECK-NEXT: .LBB23_2:{{$}} +; CHECK-NEXT: i32.const $push0=, 0{{$}} +; CHECK-NEXT: br_if 0, $pop0{{$}} ; CHECK-NEXT: end_loop{{$}} +; CHECK-NEXT: .LBB23_3:{{$}} ; CHECK-NEXT: loop{{$}} -; CHECK-NEXT: br_if $0, 0{{$}} +; CHECK-NEXT: i32.const $push1=, 0{{$}} +; CHECK-NEXT: br_if 0, $pop1{{$}} ; CHECK-NEXT: end_loop{{$}} ; CHECK-NEXT: return{{$}} define void @test14() { @@ -1215,3 +1238,81 @@ bb48: bb50: ret void } + +; Test that a block boundary which ends one block, begins another block, and +; also begins a loop, has the markers placed in the correct order. + +; CHECK-LABEL: test15: +; CHECK: block +; CHECK-NEXT: block +; CHECK: br_if 0, $pop{{.*}}{{$}} +; CHECK: .LBB24_2: +; CHECK-NEXT: block{{$}} +; CHECK-NEXT: loop{{$}} +; CHECK: br_if 1, $pop{{.*}}{{$}} +; CHECK: br_if 0, ${{.*}}{{$}} +; CHECK-NEXT: br 2{{$}} +; CHECK-NEXT: .LBB24_4: +; CHECK-NEXT: end_loop{{$}} +; CHECK: .LBB24_5: +; CHECK-NEXT: end_block{{$}} +; CHECK: br_if 1, $pop{{.*}}{{$}} +; CHECK: return{{$}} +; CHECK: .LBB24_7: +; CHECK-NEXT: end_block{{$}} +; CHECK: .LBB24_8: +; CHECK-NEXT: end_block{{$}} +; CHECK-NEXT: return{{$}} +; OPT-LABEL: test15: +; OPT: block +; OPT: block +; OPT-NEXT: i32.const $push +; OPT-NEXT: i32.eqz $push{{.*}}=, $pop{{.*}}{{$}} +; OPT-NEXT: br_if 0, $pop{{.*}}{{$}} +; OPT-NEXT: call test15_callee1@FUNCTION{{$}} +; OPT-NEXT: br 1{{$}} +; OPT-NEXT: .LBB24_2: +; OPT-NEXT: end_block +; OPT-NEXT: i32.const +; OPT-NEXT: .LBB24_3: +; OPT-NEXT: block +; OPT-NEXT: loop +%0 = type { i8, i32 } +declare void @test15_callee0() +declare void @test15_callee1() +define void @test15() { +bb: + %tmp1 = icmp eq i8 1, 0 + br i1 %tmp1, label %bb2, label %bb14 + +bb2: + %tmp3 = phi %0** [ %tmp6, %bb5 ], [ null, %bb ] + %tmp4 = icmp eq i32 0, 11 + br i1 %tmp4, label %bb5, label %bb8 + +bb5: + %tmp = bitcast i8* null to %0** + %tmp6 = getelementptr %0*, %0** %tmp3, i32 1 + %tmp7 = icmp eq %0** %tmp6, null + br i1 %tmp7, label %bb10, label %bb2 + +bb8: + %tmp9 = icmp eq %0** null, undef + br label %bb10 + +bb10: + %tmp11 = phi %0** [ null, %bb8 ], [ %tmp, %bb5 ] + %tmp12 = icmp eq %0** null, %tmp11 + br i1 %tmp12, label %bb15, label %bb13 + +bb13: + call void @test15_callee0() + ret void + +bb14: + call void @test15_callee1() + ret void + +bb15: + ret void +} diff --git a/test/CodeGen/WebAssembly/comparisons_f32.ll b/test/CodeGen/WebAssembly/comparisons_f32.ll index 2d324f7f2083..10e037d57a7a 100644 --- a/test/CodeGen/WebAssembly/comparisons_f32.ll +++ b/test/CodeGen/WebAssembly/comparisons_f32.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test that basic 32-bit floating-point comparison operations assemble as ; expected. diff --git a/test/CodeGen/WebAssembly/comparisons_f64.ll b/test/CodeGen/WebAssembly/comparisons_f64.ll index 22fbc1ae4c1f..7d038a09ccbf 100644 --- a/test/CodeGen/WebAssembly/comparisons_f64.ll +++ b/test/CodeGen/WebAssembly/comparisons_f64.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test that basic 64-bit floating-point comparison operations assemble as ; expected. diff --git a/test/CodeGen/WebAssembly/comparisons_i32.ll b/test/CodeGen/WebAssembly/comparisons_i32.ll index db81ef36e270..d2ba73f79a3d 100644 --- a/test/CodeGen/WebAssembly/comparisons_i32.ll +++ b/test/CodeGen/WebAssembly/comparisons_i32.ll @@ -1,4 +1,5 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -fast-isel -fast-isel-abort=1 | FileCheck %s ; Test that basic 32-bit integer comparison operations assemble as expected. diff --git a/test/CodeGen/WebAssembly/comparisons_i64.ll b/test/CodeGen/WebAssembly/comparisons_i64.ll index 19e5cf8603bf..80950ae5cd9a 100644 --- a/test/CodeGen/WebAssembly/comparisons_i64.ll +++ b/test/CodeGen/WebAssembly/comparisons_i64.ll @@ -1,4 +1,5 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -fast-isel -fast-isel-abort=1 | FileCheck %s ; Test that basic 64-bit integer comparison operations assemble as expected. diff --git a/test/CodeGen/WebAssembly/conv.ll b/test/CodeGen/WebAssembly/conv.ll index 1a4bd72d72d6..27cebb117dd4 100644 --- a/test/CodeGen/WebAssembly/conv.ll +++ b/test/CodeGen/WebAssembly/conv.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test that basic conversion operations assemble as expected. diff --git a/test/CodeGen/WebAssembly/cpus.ll b/test/CodeGen/WebAssembly/cpus.ll index 51856fcd12c2..78aee0f59d92 100644 --- a/test/CodeGen/WebAssembly/cpus.ll +++ b/test/CodeGen/WebAssembly/cpus.ll @@ -9,8 +9,8 @@ ; RUN: llc < %s -asm-verbose=false -mtriple=wasm32-unknown-unknown -mcpu=invalidcpu 2>&1 | FileCheck %s --check-prefix=INVALID ; RUN: llc < %s -asm-verbose=false -mtriple=wasm64-unknown-unknown -mcpu=invalidcpu 2>&1 | FileCheck %s --check-prefix=INVALID -; CHECK-NOT: {{.*}} is not a recognized processor for this target -; INVALID: {{.*}} is not a recognized processor for this target +; CHECK-NOT: is not a recognized processor for this target +; INVALID: {{.+}} is not a recognized processor for this target define i32 @f(i32 %i_like_the_web) { ret i32 %i_like_the_web diff --git a/test/CodeGen/WebAssembly/dead-vreg.ll b/test/CodeGen/WebAssembly/dead-vreg.ll index 29a41990961d..190a08564001 100644 --- a/test/CodeGen/WebAssembly/dead-vreg.ll +++ b/test/CodeGen/WebAssembly/dead-vreg.ll @@ -8,7 +8,7 @@ target triple = "wasm32-unknown-unknown" define void @foo(i32* nocapture %a, i32 %w, i32 %h) { ; CHECK-LABEL: foo: ; CHECK-NEXT: .param i32, i32, i32{{$}} -; CHECK-NEXT: .local i32, i32, i32, i32, i32, i32, i32{{$}} +; CHECK-NEXT: .local i32, i32, i32, i32, i32, i32{{$}} entry: %cmp.19 = icmp sgt i32 %h, 0 br i1 %cmp.19, label %for.cond.1.preheader.lr.ph, label %for.end.7 diff --git a/test/CodeGen/WebAssembly/divrem-constant.ll b/test/CodeGen/WebAssembly/divrem-constant.ll new file mode 100644 index 000000000000..6150cab4d4fd --- /dev/null +++ b/test/CodeGen/WebAssembly/divrem-constant.ll @@ -0,0 +1,62 @@ +; RUN: llc < %s -asm-verbose=false | FileCheck %s + +; Test that integer div and rem by constant are optimized appropriately. + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +; CHECK-LABEL: test_udiv_2: +; CHECK: i32.shr_u +define i32 @test_udiv_2(i32 %x) { + %t = udiv i32 %x, 2 + ret i32 %t +} + +; CHECK-LABEL: test_udiv_5: +; CHECK: i32.div_u +define i32 @test_udiv_5(i32 %x) { + %t = udiv i32 %x, 5 + ret i32 %t +} + +; CHECK-LABEL: test_sdiv_2: +; CHECK: i32.div_s +define i32 @test_sdiv_2(i32 %x) { + %t = sdiv i32 %x, 2 + ret i32 %t +} + +; CHECK-LABEL: test_sdiv_5: +; CHECK: i32.div_s +define i32 @test_sdiv_5(i32 %x) { + %t = sdiv i32 %x, 5 + ret i32 %t +} + +; CHECK-LABEL: test_urem_2: +; CHECK: i32.and +define i32 @test_urem_2(i32 %x) { + %t = urem i32 %x, 2 + ret i32 %t +} + +; CHECK-LABEL: test_urem_5: +; CHECK: i32.rem_u +define i32 @test_urem_5(i32 %x) { + %t = urem i32 %x, 5 + ret i32 %t +} + +; CHECK-LABEL: test_srem_2: +; CHECK: i32.rem_s +define i32 @test_srem_2(i32 %x) { + %t = srem i32 %x, 2 + ret i32 %t +} + +; CHECK-LABEL: test_srem_5: +; CHECK: i32.rem_s +define i32 @test_srem_5(i32 %x) { + %t = srem i32 %x, 5 + ret i32 %t +} diff --git a/test/CodeGen/WebAssembly/f32.ll b/test/CodeGen/WebAssembly/f32.ll index c32a7c3dc7d9..1c1d8191a987 100644 --- a/test/CodeGen/WebAssembly/f32.ll +++ b/test/CodeGen/WebAssembly/f32.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test that basic 32-bit floating-point operations assemble as expected. diff --git a/test/CodeGen/WebAssembly/f64.ll b/test/CodeGen/WebAssembly/f64.ll index 92284999cbf7..670f3f0b6978 100644 --- a/test/CodeGen/WebAssembly/f64.ll +++ b/test/CodeGen/WebAssembly/f64.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test that basic 64-bit floating-point operations assemble as expected. diff --git a/test/CodeGen/WebAssembly/fast-isel.ll b/test/CodeGen/WebAssembly/fast-isel.ll index 7f9f20fa7083..d3ee77632bca 100644 --- a/test/CodeGen/WebAssembly/fast-isel.ll +++ b/test/CodeGen/WebAssembly/fast-isel.ll @@ -18,3 +18,31 @@ define float @immediate_f32() { define double @immediate_f64() { ret double 2.5 } + +; CHECK-LABEL: bitcast_i32_f32: +; CHECK: i32.reinterpret/f32 $push{{[0-9]+}}=, $0{{$}} +define i32 @bitcast_i32_f32(float %x) { + %y = bitcast float %x to i32 + ret i32 %y +} + +; CHECK-LABEL: bitcast_f32_i32: +; CHECK: f32.reinterpret/i32 $push{{[0-9]+}}=, $0{{$}} +define float @bitcast_f32_i32(i32 %x) { + %y = bitcast i32 %x to float + ret float %y +} + +; CHECK-LABEL: bitcast_i64_f64: +; CHECK: i64.reinterpret/f64 $push{{[0-9]+}}=, $0{{$}} +define i64 @bitcast_i64_f64(double %x) { + %y = bitcast double %x to i64 + ret i64 %y +} + +; CHECK-LABEL: bitcast_f64_i64: +; CHECK: f64.reinterpret/i64 $push{{[0-9]+}}=, $0{{$}} +define double @bitcast_f64_i64(i64 %x) { + %y = bitcast i64 %x to double + ret double %y +} diff --git a/test/CodeGen/WebAssembly/frem.ll b/test/CodeGen/WebAssembly/frem.ll index b8c80fbe6997..b8745224ab82 100644 --- a/test/CodeGen/WebAssembly/frem.ll +++ b/test/CodeGen/WebAssembly/frem.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test that the frem instruction works. diff --git a/test/CodeGen/WebAssembly/func.ll b/test/CodeGen/WebAssembly/func.ll index 9857dadee414..71c00a46de86 100644 --- a/test/CodeGen/WebAssembly/func.ll +++ b/test/CodeGen/WebAssembly/func.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test that basic functions assemble as expected. @@ -44,7 +44,8 @@ define void @f3(i32 %p1, float %p2) { ; CHECK-LABEL: f4: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: local +; CHECK-NOT: local +; CHECK: .size f4, define i32 @f4(i32 %x) { entry: %c = trunc i32 %x to i1 diff --git a/test/CodeGen/WebAssembly/global.ll b/test/CodeGen/WebAssembly/global.ll index 85fe5c896565..1d24035d8dd4 100644 --- a/test/CodeGen/WebAssembly/global.ll +++ b/test/CodeGen/WebAssembly/global.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test that globals assemble as expected. @@ -21,8 +21,8 @@ define i32 @foo() { ; CHECK-LABEL: call_memcpy: ; CHECK-NEXT: .param i32, i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: call memcpy@FUNCTION, $0, $1, $2{{$}} -; CHECK-NEXT: return $0{{$}} +; CHECK-NEXT: i32.call $push0=, memcpy@FUNCTION, $0, $1, $2{{$}} +; CHECK-NEXT: return $pop0{{$}} declare void @llvm.memcpy.p0i8.p0i8.i32(i8* nocapture, i8* nocapture readonly, i32, i32, i1) define i8* @call_memcpy(i8* %p, i8* nocapture readonly %q, i32 %n) { tail call void @llvm.memcpy.p0i8.p0i8.i32(i8* %p, i8* %q, i32 %n, i32 1, i1 false) @@ -30,7 +30,7 @@ define i8* @call_memcpy(i8* %p, i8* nocapture readonly %q, i32 %n) { } ; CHECK: .type .Lg,@object -; CHECK: .align 2{{$}} +; CHECK: .p2align 2{{$}} ; CHECK-NEXT: .Lg: ; CHECK-NEXT: .int32 1337{{$}} ; CHECK-NEXT: .size .Lg, 4{{$}} @@ -50,28 +50,28 @@ define i8* @call_memcpy(i8* %p, i8* nocapture readonly %q, i32 %n) { @z = internal global i32 0 ; CHECK-NEXT: .type one,@object -; CHECK-NEXT: .align 2{{$}} +; CHECK-NEXT: .p2align 2{{$}} ; CHECK-NEXT: one: ; CHECK-NEXT: .int32 1{{$}} ; CHECK-NEXT: .size one, 4{{$}} @one = internal global i32 1 ; CHECK: .type answer,@object -; CHECK: .align 2{{$}} +; CHECK: .p2align 2{{$}} ; CHECK-NEXT: answer: ; CHECK-NEXT: .int32 42{{$}} ; CHECK-NEXT: .size answer, 4{{$}} @answer = internal global i32 42 ; CHECK: .type u32max,@object -; CHECK: .align 2{{$}} +; CHECK: .p2align 2{{$}} ; CHECK-NEXT: u32max: ; CHECK-NEXT: .int32 4294967295{{$}} ; CHECK-NEXT: .size u32max, 4{{$}} @u32max = internal global i32 -1 ; CHECK: .type ud64,@object -; CHECK: .align 3{{$}} +; CHECK: .p2align 3{{$}} ; CHECK-NEXT: ud64: ; CHECK-NEXT: .skip 8{{$}} ; CHECK-NEXT: .size ud64, 8{{$}} @@ -86,21 +86,21 @@ define i8* @call_memcpy(i8* %p, i8* nocapture readonly %q, i32 %n) { @z64 = internal global i64 0 ; CHECK: .type twoP32,@object -; CHECK: .align 3{{$}} +; CHECK: .p2align 3{{$}} ; CHECK-NEXT: twoP32: ; CHECK-NEXT: .int64 4294967296{{$}} ; CHECK-NEXT: .size twoP32, 8{{$}} @twoP32 = internal global i64 4294967296 ; CHECK: .type u64max,@object -; CHECK: .align 3{{$}} +; CHECK: .p2align 3{{$}} ; CHECK-NEXT: u64max: ; CHECK-NEXT: .int64 -1{{$}} ; CHECK-NEXT: .size u64max, 8{{$}} @u64max = internal global i64 -1 ; CHECK: .type f32ud,@object -; CHECK: .align 2{{$}} +; CHECK: .p2align 2{{$}} ; CHECK-NEXT: f32ud: ; CHECK-NEXT: .skip 4{{$}} ; CHECK-NEXT: .size f32ud, 4{{$}} @@ -115,21 +115,21 @@ define i8* @call_memcpy(i8* %p, i8* nocapture readonly %q, i32 %n) { @f32z = internal global float 0.0 ; CHECK: .type f32nz,@object -; CHECK: .align 2{{$}} +; CHECK: .p2align 2{{$}} ; CHECK: f32nz: ; CHECK: .int32 2147483648{{$}} ; CHECK: .size f32nz, 4{{$}} @f32nz = internal global float -0.0 ; CHECK: .type f32two,@object -; CHECK: .align 2{{$}} +; CHECK: .p2align 2{{$}} ; CHECK-NEXT: f32two: ; CHECK-NEXT: .int32 1073741824{{$}} ; CHECK-NEXT: .size f32two, 4{{$}} @f32two = internal global float 2.0 ; CHECK: .type f64ud,@object -; CHECK: .align 3{{$}} +; CHECK: .p2align 3{{$}} ; CHECK-NEXT: f64ud: ; CHECK-NEXT: .skip 8{{$}} ; CHECK-NEXT: .size f64ud, 8{{$}} @@ -144,14 +144,14 @@ define i8* @call_memcpy(i8* %p, i8* nocapture readonly %q, i32 %n) { @f64z = internal global double 0.0 ; CHECK: .type f64nz,@object -; CHECK: .align 3{{$}} +; CHECK: .p2align 3{{$}} ; CHECK-NEXT: f64nz: ; CHECK-NEXT: .int64 -9223372036854775808{{$}} ; CHECK-NEXT: .size f64nz, 8{{$}} @f64nz = internal global double -0.0 ; CHECK: .type f64two,@object -; CHECK: .align 3{{$}} +; CHECK: .p2align 3{{$}} ; CHECK-NEXT: f64two: ; CHECK-NEXT: .int64 4611686018427387904{{$}} ; CHECK-NEXT: .size f64two, 8{{$}} @@ -170,8 +170,22 @@ define i8* @call_memcpy(i8* %p, i8* nocapture readonly %q, i32 %n) { ; CHECK: .type rom,@object{{$}} ; CHECK: .section .rodata,"a",@progbits{{$}} ; CHECK: .globl rom{{$}} -; CHECK: .align 4{{$}} +; CHECK: .p2align 4{{$}} ; CHECK: rom: ; CHECK: .skip 512{{$}} ; CHECK: .size rom, 512{{$}} @rom = constant [128 x i32] zeroinitializer, align 16 + +; CHECK: .type array,@object +; CHECK-NEXT: array: +; CHECK-NEXT: .skip 8 +; CHECK-NEXT: .size array, 8 +; CHECK: .type pointer_to_array,@object +; CHECK-NEXT: .section .data.rel.ro,"aw",@progbits +; CHECK-NEXT: .globl pointer_to_array +; CHECK-NEXT: .p2align 2 +; CHECK-NEXT: pointer_to_array: +; CHECK-NEXT: .int32 array+4 +; CHECK-NEXT: .size pointer_to_array, 4 +@array = internal constant [8 x i8] zeroinitializer, align 1 +@pointer_to_array = constant i8* getelementptr inbounds ([8 x i8], [8 x i8]* @array, i32 0, i32 4), align 4 diff --git a/test/CodeGen/WebAssembly/i128.ll b/test/CodeGen/WebAssembly/i128.ll new file mode 100644 index 000000000000..29bf787863d5 --- /dev/null +++ b/test/CodeGen/WebAssembly/i128.ll @@ -0,0 +1,280 @@ +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s + +; Test that basic 128-bit integer operations assemble as expected. + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +declare i128 @llvm.ctlz.i128(i128, i1) +declare i128 @llvm.cttz.i128(i128, i1) +declare i128 @llvm.ctpop.i128(i128) + +; CHECK-LABEL: add128: +; CHECK-NEXT: .param i32, i64, i64, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: i64.add +; CHECK: i64.store +; CHECK: i64.add +; CHECK: i64.store +; CHECK-NEXT: return{{$}} +define i128 @add128(i128 %x, i128 %y) { + %a = add i128 %x, %y + ret i128 %a +} + +; CHECK-LABEL: sub128: +; CHECK-NEXT: .param i32, i64, i64, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: i64.sub +; CHECK: i64.store +; CHECK: i64.sub +; CHECK: i64.store +; CHECK-NEXT: return{{$}} +define i128 @sub128(i128 %x, i128 %y) { + %a = sub i128 %x, %y + ret i128 %a +} + +; CHECK-LABEL: mul128: +; CHECK-NEXT: .param i32, i64, i64, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: call __multi3@FUNCTION, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}{{$}} +; CHECK: return{{$}} +define i128 @mul128(i128 %x, i128 %y) { + %a = mul i128 %x, %y + ret i128 %a +} + +; CHECK-LABEL: sdiv128: +; CHECK-NEXT: .param i32, i64, i64, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: call __divti3@FUNCTION, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}{{$}} +; CHECK: return{{$}} +define i128 @sdiv128(i128 %x, i128 %y) { + %a = sdiv i128 %x, %y + ret i128 %a +} + +; CHECK-LABEL: udiv128: +; CHECK-NEXT: .param i32, i64, i64, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: call __udivti3@FUNCTION, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}{{$}} +; CHECK: return{{$}} +define i128 @udiv128(i128 %x, i128 %y) { + %a = udiv i128 %x, %y + ret i128 %a +} + +; CHECK-LABEL: srem128: +; CHECK-NEXT: .param i32, i64, i64, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: call __modti3@FUNCTION, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}{{$}} +; CHECK: return{{$}} +define i128 @srem128(i128 %x, i128 %y) { + %a = srem i128 %x, %y + ret i128 %a +} + +; CHECK-LABEL: urem128: +; CHECK-NEXT: .param i32, i64, i64, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: call __umodti3@FUNCTION, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}{{$}} +; CHECK: return{{$}} +define i128 @urem128(i128 %x, i128 %y) { + %a = urem i128 %x, %y + ret i128 %a +} + +; CHECK-LABEL: and128: +; CHECK-NEXT: .param i32, i64, i64, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: i64.and +; CHECK: i64.store +; CHECK: i64.and +; CHECK: i64.store +; CHECK-NEXT: return{{$}} +define i128 @and128(i128 %x, i128 %y) { + %a = and i128 %x, %y + ret i128 %a +} + +; CHECK-LABEL: or128: +; CHECK-NEXT: .param i32, i64, i64, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: i64.or +; CHECK: i64.store +; CHECK: i64.or +; CHECK: i64.store +; CHECK-NEXT: return{{$}} +define i128 @or128(i128 %x, i128 %y) { + %a = or i128 %x, %y + ret i128 %a +} + +; CHECK-LABEL: xor128: +; CHECK-NEXT: .param i32, i64, i64, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: i64.xor +; CHECK: i64.store +; CHECK: i64.xor +; CHECK: i64.store +; CHECK-NEXT: return{{$}} +define i128 @xor128(i128 %x, i128 %y) { + %a = xor i128 %x, %y + ret i128 %a +} + +; CHECK-LABEL: shl128: +; CHECK-NEXT: .param i32, i64, i64, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: call __ashlti3@FUNCTION, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}{{$}} +; CHECK: return{{$}} +define i128 @shl128(i128 %x, i128 %y) { + %a = shl i128 %x, %y + ret i128 %a +} + +; CHECK-LABEL: shr128: +; CHECK-NEXT: .param i32, i64, i64, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: call __lshrti3@FUNCTION, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}{{$}} +; CHECK: return{{$}} +define i128 @shr128(i128 %x, i128 %y) { + %a = lshr i128 %x, %y + ret i128 %a +} + +; CHECK-LABEL: sar128: +; CHECK-NEXT: .param i32, i64, i64, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: call __ashrti3@FUNCTION, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}{{$}} +; CHECK: return{{$}} +define i128 @sar128(i128 %x, i128 %y) { + %a = ashr i128 %x, %y + ret i128 %a +} + +; CHECK-LABEL: clz128: +; CHECK-NEXT: .param i32, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: i64.clz +; CHECK: i64.clz +; CHECK: return{{$}} +define i128 @clz128(i128 %x) { + %a = call i128 @llvm.ctlz.i128(i128 %x, i1 false) + ret i128 %a +} + +; CHECK-LABEL: clz128_zero_undef: +; CHECK-NEXT: .param i32, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: i64.clz +; CHECK: i64.clz +; CHECK: return{{$}} +define i128 @clz128_zero_undef(i128 %x) { + %a = call i128 @llvm.ctlz.i128(i128 %x, i1 true) + ret i128 %a +} + +; CHECK-LABEL: ctz128: +; CHECK-NEXT: .param i32, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: i64.ctz +; CHECK: i64.ctz +; CHECK: return{{$}} +define i128 @ctz128(i128 %x) { + %a = call i128 @llvm.cttz.i128(i128 %x, i1 false) + ret i128 %a +} + +; CHECK-LABEL: ctz128_zero_undef: +; CHECK-NEXT: .param i32, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: i64.ctz +; CHECK: i64.ctz +; CHECK: return{{$}} +define i128 @ctz128_zero_undef(i128 %x) { + %a = call i128 @llvm.cttz.i128(i128 %x, i1 true) + ret i128 %a +} + +; CHECK-LABEL: popcnt128: +; CHECK-NEXT: .param i32, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: i64.popcnt +; CHECK: i64.popcnt +; CHECK: return{{$}} +define i128 @popcnt128(i128 %x) { + %a = call i128 @llvm.ctpop.i128(i128 %x) + ret i128 %a +} + +; CHECK-LABEL: eqz128: +; CHECK-NEXT: .param i64, i64{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK: i64.or +; CHECK: i64.eqz +; CHECK: return $ +define i32 @eqz128(i128 %x) { + %a = icmp eq i128 %x, 0 + %b = zext i1 %a to i32 + ret i32 %b +} + +; CHECK-LABEL: rotl: +; CHECK-NEXT: .param i32, i64, i64, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: call __ashlti3@FUNCTION, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}{{$}} +; CHECK: call __lshrti3@FUNCTION, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}{{$}} +; CHECK: return{{$}} +define i128 @rotl(i128 %x, i128 %y) { + %z = sub i128 128, %y + %b = shl i128 %x, %y + %c = lshr i128 %x, %z + %d = or i128 %b, %c + ret i128 %d +} + +; CHECK-LABEL: masked_rotl: +; CHECK-NEXT: .param i32, i64, i64, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: call __ashlti3@FUNCTION, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}{{$}} +; CHECK: call __lshrti3@FUNCTION, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}{{$}} +; CHECK: return{{$}} +define i128 @masked_rotl(i128 %x, i128 %y) { + %a = and i128 %y, 127 + %z = sub i128 128, %a + %b = shl i128 %x, %a + %c = lshr i128 %x, %z + %d = or i128 %b, %c + ret i128 %d +} + +; CHECK-LABEL: rotr: +; CHECK-NEXT: .param i32, i64, i64, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: call __lshrti3@FUNCTION, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}{{$}} +; CHECK: call __ashlti3@FUNCTION, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}{{$}} +; CHECK: return{{$}} +define i128 @rotr(i128 %x, i128 %y) { + %z = sub i128 128, %y + %b = lshr i128 %x, %y + %c = shl i128 %x, %z + %d = or i128 %b, %c + ret i128 %d +} + +; CHECK-LABEL: masked_rotr: +; CHECK-NEXT: .param i32, i64, i64, i64, i64{{$}} +; CHECK-NOT: .result +; CHECK: call __lshrti3@FUNCTION, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}{{$}} +; CHECK: call __ashlti3@FUNCTION, ${{.+}}, ${{.+}}, ${{.+}}, ${{.+}}{{$}} +; CHECK: return{{$}} +define i128 @masked_rotr(i128 %x, i128 %y) { + %a = and i128 %y, 127 + %z = sub i128 128, %a + %b = lshr i128 %x, %a + %c = shl i128 %x, %z + %d = or i128 %b, %c + ret i128 %d +} diff --git a/test/CodeGen/WebAssembly/i32-load-store-alignment.ll b/test/CodeGen/WebAssembly/i32-load-store-alignment.ll new file mode 100644 index 000000000000..b254413d380f --- /dev/null +++ b/test/CodeGen/WebAssembly/i32-load-store-alignment.ll @@ -0,0 +1,212 @@ +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s + +; Test loads and stores with custom alignment values. + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +; CHECK-LABEL: ldi32_a1: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.load $push[[NUM:[0-9]+]]=, 0($0):p2align=0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i32 @ldi32_a1(i32 *%p) { + %v = load i32, i32* %p, align 1 + ret i32 %v +} + +; CHECK-LABEL: ldi32_a2: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.load $push[[NUM:[0-9]+]]=, 0($0):p2align=1{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i32 @ldi32_a2(i32 *%p) { + %v = load i32, i32* %p, align 2 + ret i32 %v +} + +; 4 is the default alignment for i32 so no attribute is needed. + +; CHECK-LABEL: ldi32_a4: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.load $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i32 @ldi32_a4(i32 *%p) { + %v = load i32, i32* %p, align 4 + ret i32 %v +} + +; The default alignment in LLVM is the same as the defualt alignment in wasm. + +; CHECK-LABEL: ldi32: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.load $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i32 @ldi32(i32 *%p) { + %v = load i32, i32* %p + ret i32 %v +} + +; 8 is greater than the default alignment so it is ignored. + +; CHECK-LABEL: ldi32_a8: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.load $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i32 @ldi32_a8(i32 *%p) { + %v = load i32, i32* %p, align 8 + ret i32 %v +} + +; Extending loads. + +; CHECK-LABEL: ldi8_a1: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.load8_u $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i8 @ldi8_a1(i8 *%p) { + %v = load i8, i8* %p, align 1 + ret i8 %v +} + +; CHECK-LABEL: ldi8_a2: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.load8_u $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i8 @ldi8_a2(i8 *%p) { + %v = load i8, i8* %p, align 2 + ret i8 %v +} + +; CHECK-LABEL: ldi16_a1: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.load16_u $push[[NUM:[0-9]+]]=, 0($0):p2align=0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i16 @ldi16_a1(i16 *%p) { + %v = load i16, i16* %p, align 1 + ret i16 %v +} + +; CHECK-LABEL: ldi16_a2: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.load16_u $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i16 @ldi16_a2(i16 *%p) { + %v = load i16, i16* %p, align 2 + ret i16 %v +} + +; CHECK-LABEL: ldi16_a4: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.load16_u $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i16 @ldi16_a4(i16 *%p) { + %v = load i16, i16* %p, align 4 + ret i16 %v +} + +; Stores. + +; CHECK-LABEL: sti32_a1: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: i32.store $drop=, 0($0):p2align=0, $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti32_a1(i32 *%p, i32 %v) { + store i32 %v, i32* %p, align 1 + ret void +} + +; CHECK-LABEL: sti32_a2: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: i32.store $drop=, 0($0):p2align=1, $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti32_a2(i32 *%p, i32 %v) { + store i32 %v, i32* %p, align 2 + ret void +} + +; 4 is the default alignment for i32 so no attribute is needed. + +; CHECK-LABEL: sti32_a4: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: i32.store $drop=, 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti32_a4(i32 *%p, i32 %v) { + store i32 %v, i32* %p, align 4 + ret void +} + +; The default alignment in LLVM is the same as the defualt alignment in wasm. + +; CHECK-LABEL: sti32: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: i32.store $drop=, 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti32(i32 *%p, i32 %v) { + store i32 %v, i32* %p + ret void +} + +; CHECK-LABEL: sti32_a8: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: i32.store $drop=, 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti32_a8(i32 *%p, i32 %v) { + store i32 %v, i32* %p, align 8 + ret void +} + +; Truncating stores. + +; CHECK-LABEL: sti8_a1: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: i32.store8 $drop=, 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti8_a1(i8 *%p, i8 %v) { + store i8 %v, i8* %p, align 1 + ret void +} + +; CHECK-LABEL: sti8_a2: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: i32.store8 $drop=, 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti8_a2(i8 *%p, i8 %v) { + store i8 %v, i8* %p, align 2 + ret void +} + +; CHECK-LABEL: sti16_a1: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: i32.store16 $drop=, 0($0):p2align=0, $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti16_a1(i16 *%p, i16 %v) { + store i16 %v, i16* %p, align 1 + ret void +} + +; CHECK-LABEL: sti16_a2: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: i32.store16 $drop=, 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti16_a2(i16 *%p, i16 %v) { + store i16 %v, i16* %p, align 2 + ret void +} + +; CHECK-LABEL: sti16_a4: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: i32.store16 $drop=, 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti16_a4(i16 *%p, i16 %v) { + store i16 %v, i16* %p, align 4 + ret void +} diff --git a/test/CodeGen/WebAssembly/i32.ll b/test/CodeGen/WebAssembly/i32.ll index 10d97ad9e6d1..a07dd02beced 100644 --- a/test/CodeGen/WebAssembly/i32.ll +++ b/test/CodeGen/WebAssembly/i32.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test that basic 32-bit integer operations assemble as expected. @@ -188,3 +188,68 @@ define i32 @popcnt32(i32 %x) { %a = call i32 @llvm.ctpop.i32(i32 %x) ret i32 %a } + +; CHECK-LABEL: eqz32: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.eqz $push0=, $0{{$}} +; CHECK-NEXT: return $pop0{{$}} +define i32 @eqz32(i32 %x) { + %a = icmp eq i32 %x, 0 + %b = zext i1 %a to i32 + ret i32 %b +} + +; CHECK-LABEL: rotl: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.rotl $push0=, $0, $1 +; CHECK-NEXT: return $pop0{{$}} +define i32 @rotl(i32 %x, i32 %y) { + %z = sub i32 32, %y + %b = shl i32 %x, %y + %c = lshr i32 %x, %z + %d = or i32 %b, %c + ret i32 %d +} + +; CHECK-LABEL: masked_rotl: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.rotl $push0=, $0, $1 +; CHECK-NEXT: return $pop0{{$}} +define i32 @masked_rotl(i32 %x, i32 %y) { + %a = and i32 %y, 31 + %z = sub i32 32, %a + %b = shl i32 %x, %a + %c = lshr i32 %x, %z + %d = or i32 %b, %c + ret i32 %d +} + +; CHECK-LABEL: rotr: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.rotr $push0=, $0, $1 +; CHECK-NEXT: return $pop0{{$}} +define i32 @rotr(i32 %x, i32 %y) { + %z = sub i32 32, %y + %b = lshr i32 %x, %y + %c = shl i32 %x, %z + %d = or i32 %b, %c + ret i32 %d +} + +; CHECK-LABEL: masked_rotr: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.rotr $push0=, $0, $1 +; CHECK-NEXT: return $pop0{{$}} +define i32 @masked_rotr(i32 %x, i32 %y) { + %a = and i32 %y, 31 + %z = sub i32 32, %a + %b = lshr i32 %x, %a + %c = shl i32 %x, %z + %d = or i32 %b, %c + ret i32 %d +} diff --git a/test/CodeGen/WebAssembly/i64-load-store-alignment.ll b/test/CodeGen/WebAssembly/i64-load-store-alignment.ll new file mode 100644 index 000000000000..b2fb96290391 --- /dev/null +++ b/test/CodeGen/WebAssembly/i64-load-store-alignment.ll @@ -0,0 +1,325 @@ +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s + +; Test loads and stores with custom alignment values. + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +; CHECK-LABEL: ldi64_a1: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0):p2align=0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @ldi64_a1(i64 *%p) { + %v = load i64, i64* %p, align 1 + ret i64 %v +} + +; CHECK-LABEL: ldi64_a2: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0):p2align=1{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @ldi64_a2(i64 *%p) { + %v = load i64, i64* %p, align 2 + ret i64 %v +} + +; CHECK-LABEL: ldi64_a4: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0):p2align=2{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @ldi64_a4(i64 *%p) { + %v = load i64, i64* %p, align 4 + ret i64 %v +} + +; 8 is the default alignment for i64 so no attribute is needed. + +; CHECK-LABEL: ldi64_a8: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @ldi64_a8(i64 *%p) { + %v = load i64, i64* %p, align 8 + ret i64 %v +} + +; The default alignment in LLVM is the same as the defualt alignment in wasm. + +; CHECK-LABEL: ldi64: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @ldi64(i64 *%p) { + %v = load i64, i64* %p + ret i64 %v +} + +; 16 is greater than the default alignment so it is ignored. + +; CHECK-LABEL: ldi64_a16: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @ldi64_a16(i64 *%p) { + %v = load i64, i64* %p, align 16 + ret i64 %v +} + +; Extending loads. + +; CHECK-LABEL: ldi8_a1: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.load8_u $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @ldi8_a1(i8 *%p) { + %v = load i8, i8* %p, align 1 + %w = zext i8 %v to i64 + ret i64 %w +} + +; CHECK-LABEL: ldi8_a2: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.load8_u $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @ldi8_a2(i8 *%p) { + %v = load i8, i8* %p, align 2 + %w = zext i8 %v to i64 + ret i64 %w +} + +; CHECK-LABEL: ldi16_a1: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.load16_u $push[[NUM:[0-9]+]]=, 0($0):p2align=0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @ldi16_a1(i16 *%p) { + %v = load i16, i16* %p, align 1 + %w = zext i16 %v to i64 + ret i64 %w +} + +; CHECK-LABEL: ldi16_a2: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.load16_u $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @ldi16_a2(i16 *%p) { + %v = load i16, i16* %p, align 2 + %w = zext i16 %v to i64 + ret i64 %w +} + +; CHECK-LABEL: ldi16_a4: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.load16_u $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @ldi16_a4(i16 *%p) { + %v = load i16, i16* %p, align 4 + %w = zext i16 %v to i64 + ret i64 %w +} + +; CHECK-LABEL: ldi32_a1: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.load32_u $push[[NUM:[0-9]+]]=, 0($0):p2align=0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @ldi32_a1(i32 *%p) { + %v = load i32, i32* %p, align 1 + %w = zext i32 %v to i64 + ret i64 %w +} + +; CHECK-LABEL: ldi32_a2: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.load32_u $push[[NUM:[0-9]+]]=, 0($0):p2align=1{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @ldi32_a2(i32 *%p) { + %v = load i32, i32* %p, align 2 + %w = zext i32 %v to i64 + ret i64 %w +} + +; CHECK-LABEL: ldi32_a4: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.load32_u $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @ldi32_a4(i32 *%p) { + %v = load i32, i32* %p, align 4 + %w = zext i32 %v to i64 + ret i64 %w +} + +; CHECK-LABEL: ldi32_a8: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.load32_u $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @ldi32_a8(i32 *%p) { + %v = load i32, i32* %p, align 8 + %w = zext i32 %v to i64 + ret i64 %w +} + +; Stores. + +; CHECK-LABEL: sti64_a1: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.store $drop=, 0($0):p2align=0, $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti64_a1(i64 *%p, i64 %v) { + store i64 %v, i64* %p, align 1 + ret void +} + +; CHECK-LABEL: sti64_a2: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.store $drop=, 0($0):p2align=1, $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti64_a2(i64 *%p, i64 %v) { + store i64 %v, i64* %p, align 2 + ret void +} + +; CHECK-LABEL: sti64_a4: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.store $drop=, 0($0):p2align=2, $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti64_a4(i64 *%p, i64 %v) { + store i64 %v, i64* %p, align 4 + ret void +} + +; 8 is the default alignment for i32 so no attribute is needed. + +; CHECK-LABEL: sti64_a8: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.store $drop=, 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti64_a8(i64 *%p, i64 %v) { + store i64 %v, i64* %p, align 8 + ret void +} + +; The default alignment in LLVM is the same as the defualt alignment in wasm. + +; CHECK-LABEL: sti64: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.store $drop=, 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti64(i64 *%p, i64 %v) { + store i64 %v, i64* %p + ret void +} + +; CHECK-LABEL: sti64_a16: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.store $drop=, 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti64_a16(i64 *%p, i64 %v) { + store i64 %v, i64* %p, align 16 + ret void +} + +; Truncating stores. + +; CHECK-LABEL: sti8_a1: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.store8 $drop=, 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti8_a1(i8 *%p, i64 %w) { + %v = trunc i64 %w to i8 + store i8 %v, i8* %p, align 1 + ret void +} + +; CHECK-LABEL: sti8_a2: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.store8 $drop=, 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti8_a2(i8 *%p, i64 %w) { + %v = trunc i64 %w to i8 + store i8 %v, i8* %p, align 2 + ret void +} + +; CHECK-LABEL: sti16_a1: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.store16 $drop=, 0($0):p2align=0, $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti16_a1(i16 *%p, i64 %w) { + %v = trunc i64 %w to i16 + store i16 %v, i16* %p, align 1 + ret void +} + +; CHECK-LABEL: sti16_a2: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.store16 $drop=, 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti16_a2(i16 *%p, i64 %w) { + %v = trunc i64 %w to i16 + store i16 %v, i16* %p, align 2 + ret void +} + +; CHECK-LABEL: sti16_a4: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.store16 $drop=, 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti16_a4(i16 *%p, i64 %w) { + %v = trunc i64 %w to i16 + store i16 %v, i16* %p, align 4 + ret void +} + +; CHECK-LABEL: sti32_a1: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.store32 $drop=, 0($0):p2align=0, $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti32_a1(i32 *%p, i64 %w) { + %v = trunc i64 %w to i32 + store i32 %v, i32* %p, align 1 + ret void +} + +; CHECK-LABEL: sti32_a2: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.store32 $drop=, 0($0):p2align=1, $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti32_a2(i32 *%p, i64 %w) { + %v = trunc i64 %w to i32 + store i32 %v, i32* %p, align 2 + ret void +} + +; CHECK-LABEL: sti32_a4: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.store32 $drop=, 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti32_a4(i32 *%p, i64 %w) { + %v = trunc i64 %w to i32 + store i32 %v, i32* %p, align 4 + ret void +} + +; CHECK-LABEL: sti32_a8: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.store32 $drop=, 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti32_a8(i32 *%p, i64 %w) { + %v = trunc i64 %w to i32 + store i32 %v, i32* %p, align 8 + ret void +} diff --git a/test/CodeGen/WebAssembly/i64.ll b/test/CodeGen/WebAssembly/i64.ll index 6dd46a91fad0..93e32bfc0e1d 100644 --- a/test/CodeGen/WebAssembly/i64.ll +++ b/test/CodeGen/WebAssembly/i64.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test that basic 64-bit integer operations assemble as expected. @@ -188,3 +188,68 @@ define i64 @popcnt64(i64 %x) { %a = call i64 @llvm.ctpop.i64(i64 %x) ret i64 %a } + +; CHECK-LABEL: eqz64: +; CHECK-NEXT: .param i64{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i64.eqz $push0=, $0{{$}} +; CHECK-NEXT: return $pop0{{$}} +define i32 @eqz64(i64 %x) { + %a = icmp eq i64 %x, 0 + %b = zext i1 %a to i32 + ret i32 %b +} + +; CHECK-LABEL: rotl: +; CHECK-NEXT: .param i64, i64{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.rotl $push0=, $0, $1 +; CHECK-NEXT: return $pop0{{$}} +define i64 @rotl(i64 %x, i64 %y) { + %z = sub i64 64, %y + %b = shl i64 %x, %y + %c = lshr i64 %x, %z + %d = or i64 %b, %c + ret i64 %d +} + +; CHECK-LABEL: masked_rotl: +; CHECK-NEXT: .param i64, i64{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.rotl $push0=, $0, $1 +; CHECK-NEXT: return $pop0{{$}} +define i64 @masked_rotl(i64 %x, i64 %y) { + %a = and i64 %y, 63 + %z = sub i64 64, %a + %b = shl i64 %x, %a + %c = lshr i64 %x, %z + %d = or i64 %b, %c + ret i64 %d +} + +; CHECK-LABEL: rotr: +; CHECK-NEXT: .param i64, i64{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.rotr $push0=, $0, $1 +; CHECK-NEXT: return $pop0{{$}} +define i64 @rotr(i64 %x, i64 %y) { + %z = sub i64 64, %y + %b = lshr i64 %x, %y + %c = shl i64 %x, %z + %d = or i64 %b, %c + ret i64 %d +} + +; CHECK-LABEL: masked_rotr: +; CHECK-NEXT: .param i64, i64{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.rotr $push0=, $0, $1 +; CHECK-NEXT: return $pop0{{$}} +define i64 @masked_rotr(i64 %x, i64 %y) { + %a = and i64 %y, 63 + %z = sub i64 64, %a + %b = lshr i64 %x, %a + %c = shl i64 %x, %z + %d = or i64 %b, %c + ret i64 %d +} diff --git a/test/CodeGen/WebAssembly/immediates.ll b/test/CodeGen/WebAssembly/immediates.ll index 735b386b4fc0..3d11f9410a79 100644 --- a/test/CodeGen/WebAssembly/immediates.ll +++ b/test/CodeGen/WebAssembly/immediates.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test that basic immediates assemble as expected. @@ -133,6 +133,25 @@ define float @neginf_f32() { ret float 0xFFF0000000000000 } +; CHECK-LABEL: custom_nan_f32: +; CHECK-NEXT: .result f32{{$}} +; CHECK-NEXT: f32.const $push[[NUM:[0-9]+]]=, -nan:0x6bcdef{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define float @custom_nan_f32() { + ret float 0xFFFD79BDE0000000 +} + +; TODO: LLVM's MC layer stores f32 operands as host doubles, requiring a +; conversion, so the bits of the NaN are not fully preserved. + +; CHECK-LABEL: custom_nans_f32: +; CHECK-NEXT: .result f32{{$}} +; CHECK-NEXT: f32.const $push[[NUM:[0-9]+]]=, -nan:0x6bcdef{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define float @custom_nans_f32() { + ret float 0xFFF579BDE0000000 +} + ; CHECK-LABEL: negzero_f64: ; CHECK-NEXT: .result f64{{$}} ; CHECK-NEXT: f64.const $push[[NUM:[0-9]+]]=, -0x0p0{{$}} @@ -196,3 +215,19 @@ define double @inf_f64() { define double @neginf_f64() { ret double 0xFFF0000000000000 } + +; CHECK-LABEL: custom_nan_f64: +; CHECK-NEXT: .result f64{{$}} +; CHECK-NEXT: f64.const $push[[NUM:[0-9]+]]=, -nan:0xabcdef0123456{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define double @custom_nan_f64() { + ret double 0xFFFABCDEF0123456 +} + +; CHECK-LABEL: custom_nans_f64: +; CHECK-NEXT: .result f64{{$}} +; CHECK-NEXT: f64.const $push[[NUM:[0-9]+]]=, -nan:0x2bcdef0123456{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define double @custom_nans_f64() { + ret double 0xFFF2BCDEF0123456 +} diff --git a/test/CodeGen/WebAssembly/indirect-import.ll b/test/CodeGen/WebAssembly/indirect-import.ll new file mode 100644 index 000000000000..1bde65bcbbba --- /dev/null +++ b/test/CodeGen/WebAssembly/indirect-import.ll @@ -0,0 +1,73 @@ +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -verify-machineinstrs | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -verify-machineinstrs -fast-isel | FileCheck %s + +; ModuleID = 'test/dot_s/indirect-import.c' +source_filename = "test/dot_s/indirect-import.c" +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32" + +%struct.big = type { float, double, i32 } + +; Function Attrs: nounwind +; CHECK: bar: +define hidden i32 @bar() #0 { +entry: + %fd = alloca float (double)*, align 4 + %vj = alloca void (i64)*, align 4 + %v = alloca void ()*, align 4 + %ijidf = alloca i32 (i64, i32, double, float)*, align 4 + %vs = alloca void (%struct.big*)*, align 4 + %s = alloca void (%struct.big*)*, align 4 + +; CHECK: i32.const {{.+}}=, extern_fd@FUNCTION + store float (double)* @extern_fd, float (double)** %fd, align 4 +; CHECK: i32.const {{.+}}=, extern_vj@FUNCTION + store void (i64)* @extern_vj, void (i64)** %vj, align 4 + %0 = load void (i64)*, void (i64)** %vj, align 4 + call void %0(i64 1) + +; CHECK: i32.const {{.+}}=, extern_v@FUNCTION + store void ()* @extern_v, void ()** %v, align 4 + %1 = load void ()*, void ()** %v, align 4 + call void %1() + +; CHECK: i32.const {{.+}}=, extern_ijidf@FUNCTION + store i32 (i64, i32, double, float)* @extern_ijidf, i32 (i64, i32, double, float)** %ijidf, align 4 + %2 = load i32 (i64, i32, double, float)*, i32 (i64, i32, double, float)** %ijidf, align 4 + %call = call i32 %2(i64 1, i32 2, double 3.000000e+00, float 4.000000e+00) + +; CHECK: i32.const {{.+}}=, extern_struct@FUNCTION + store void (%struct.big*)* @extern_struct, void (%struct.big*)** %vs, align 4 + +; CHECK: i32.const {{.+}}=, extern_sret@FUNCTION + store void (%struct.big*)* @extern_sret, void (%struct.big*)** %s, align 4 + %3 = load float (double)*, float (double)** %fd, align 4 + %4 = ptrtoint float (double)* %3 to i32 + ret i32 %4 +} + +declare float @extern_fd(double) #1 + +declare void @extern_vj(i64) #1 + +declare void @extern_v() #1 + +declare i32 @extern_ijidf(i64, i32, double, float) #1 + +declare void @extern_struct(%struct.big* byval align 8) #1 + +declare void @extern_sret(%struct.big* sret) #1 + +declare i128 @extern_i128ret(i64) #1 + +attributes #0 = { nounwind "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" "stack-protector-buffer-size"="8" "target-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } + + +; CHECK: .functype extern_fd, f32, f64 +; CHECK: .functype extern_vj, void, i64 +; CHECK: .functype extern_v, void +; CHECK: .functype extern_ijidf, i32, i64, i32, f64, f32 +; CHECK: .functype extern_struct, void, i32 +; CHECK: .functype extern_sret, void, i32 +; CHECK: .functype extern_i128ret, void, i32, i64 diff --git a/test/CodeGen/WebAssembly/inline-asm.ll b/test/CodeGen/WebAssembly/inline-asm.ll index f35042e64f86..d36c32b546d3 100644 --- a/test/CodeGen/WebAssembly/inline-asm.ll +++ b/test/CodeGen/WebAssembly/inline-asm.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false -no-integrated-as | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -no-integrated-as | FileCheck %s ; Test basic inline assembly. Pass -no-integrated-as since these aren't ; actually valid assembly syntax. @@ -59,7 +59,7 @@ entry: ; CHECK-LABEL: X_i16: ; CHECK: foo $1{{$}} -; CHECK: i32.store16 $discard=, 0($0), $1{{$}} +; CHECK: i32.store16 $drop=, 0($0), $1{{$}} define void @X_i16(i16 * %t) { call void asm sideeffect "foo $0", "=*X,~{dirflag},~{fpsr},~{flags},~{memory}"(i16* %t) ret void @@ -67,7 +67,7 @@ define void @X_i16(i16 * %t) { ; CHECK-LABEL: X_ptr: ; CHECK: foo $1{{$}} -; CHECK: i32.store $discard=, 0($0), $1{{$}} +; CHECK: i32.store $drop=, 0($0), $1{{$}} define void @X_ptr(i16 ** %t) { call void asm sideeffect "foo $0", "=*X,~{dirflag},~{fpsr},~{flags},~{memory}"(i16** %t) ret void diff --git a/test/CodeGen/WebAssembly/irreducible-cfg.ll b/test/CodeGen/WebAssembly/irreducible-cfg.ll new file mode 100644 index 000000000000..8fe7d10c5f31 --- /dev/null +++ b/test/CodeGen/WebAssembly/irreducible-cfg.ll @@ -0,0 +1,94 @@ +; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -disable-block-placement | FileCheck %s + +; Test irreducible CFG handling. + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +; A simple loop with two entries. + +; CHECK-LABEL: test0: +; CHECK: f64.load +; CHECK: i32.const $[[REG:[^,]+]]=, 0{{$}} +; CHECK: br_table $[[REG]], +define void @test0(double* %arg, i32 %arg1, i32 %arg2, i32 %arg3) { +bb: + %tmp = icmp eq i32 %arg2, 0 + br i1 %tmp, label %bb6, label %bb3 + +bb3: + %tmp4 = getelementptr double, double* %arg, i32 %arg3 + %tmp5 = load double, double* %tmp4, align 4 + br label %bb13 + +bb6: + %tmp7 = phi i32 [ %tmp18, %bb13 ], [ 0, %bb ] + %tmp8 = icmp slt i32 %tmp7, %arg1 + br i1 %tmp8, label %bb9, label %bb19 + +bb9: + %tmp10 = getelementptr double, double* %arg, i32 %tmp7 + %tmp11 = load double, double* %tmp10, align 4 + %tmp12 = fmul double %tmp11, 2.300000e+00 + store double %tmp12, double* %tmp10, align 4 + br label %bb13 + +bb13: + %tmp14 = phi double [ %tmp5, %bb3 ], [ %tmp12, %bb9 ] + %tmp15 = phi i32 [ undef, %bb3 ], [ %tmp7, %bb9 ] + %tmp16 = getelementptr double, double* %arg, i32 %tmp15 + %tmp17 = fadd double %tmp14, 1.300000e+00 + store double %tmp17, double* %tmp16, align 4 + %tmp18 = add nsw i32 %tmp15, 1 + br label %bb6 + +bb19: + ret void +} + +; A simple loop with two entries and an inner natural loop. + +; CHECK-LABEL: test1: +; CHECK: f64.load +; CHECK: i32.const $[[REG:[^,]+]]=, 0{{$}} +; CHECK: br_table $[[REG]], +define void @test1(double* %arg, i32 %arg1, i32 %arg2, i32 %arg3) { +bb: + %tmp = icmp eq i32 %arg2, 0 + br i1 %tmp, label %bb6, label %bb3 + +bb3: + %tmp4 = getelementptr double, double* %arg, i32 %arg3 + %tmp5 = load double, double* %tmp4, align 4 + br label %bb13 + +bb6: + %tmp7 = phi i32 [ %tmp18, %bb13 ], [ 0, %bb ] + %tmp8 = icmp slt i32 %tmp7, %arg1 + br i1 %tmp8, label %bb9, label %bb19 + +bb9: + %tmp10 = getelementptr double, double* %arg, i32 %tmp7 + %tmp11 = load double, double* %tmp10, align 4 + %tmp12 = fmul double %tmp11, 2.300000e+00 + store double %tmp12, double* %tmp10, align 4 + br label %bb10 + +bb10: + %p = phi i32 [ 0, %bb9 ], [ %pn, %bb10 ] + %pn = add i32 %p, 1 + %c = icmp slt i32 %pn, 256 + br i1 %c, label %bb10, label %bb13 + +bb13: + %tmp14 = phi double [ %tmp5, %bb3 ], [ %tmp12, %bb10 ] + %tmp15 = phi i32 [ undef, %bb3 ], [ %tmp7, %bb10 ] + %tmp16 = getelementptr double, double* %arg, i32 %tmp15 + %tmp17 = fadd double %tmp14, 1.300000e+00 + store double %tmp17, double* %tmp16, align 4 + %tmp18 = add nsw i32 %tmp15, 1 + br label %bb6 + +bb19: + ret void +} diff --git a/test/CodeGen/WebAssembly/legalize.ll b/test/CodeGen/WebAssembly/legalize.ll index 5feb2e8c8c75..5cbfb8ace9ed 100644 --- a/test/CodeGen/WebAssembly/legalize.ll +++ b/test/CodeGen/WebAssembly/legalize.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test various types and operators that need to be legalized. diff --git a/test/CodeGen/WebAssembly/load-ext.ll b/test/CodeGen/WebAssembly/load-ext.ll index d52df3361a38..48a7ce7c4bd2 100644 --- a/test/CodeGen/WebAssembly/load-ext.ll +++ b/test/CodeGen/WebAssembly/load-ext.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test that extending loads are assembled properly. diff --git a/test/CodeGen/WebAssembly/load-store-i1.ll b/test/CodeGen/WebAssembly/load-store-i1.ll index 47e2e8cb254f..2a2318fde10e 100644 --- a/test/CodeGen/WebAssembly/load-store-i1.ll +++ b/test/CodeGen/WebAssembly/load-store-i1.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test that i1 extending loads and truncating stores are assembled properly. @@ -15,10 +15,11 @@ define i32 @load_u_i1_i32(i1* %p) { } ; CHECK-LABEL: load_s_i1_i32: -; CHECK: i32.const $[[NUM1:[0-9]+]]=, 31{{$}} -; CHECK-NEXT: i32.load8_u $push[[NUM0:[0-9]+]]=, 0($0){{$}} -; CHECK-NEXT: shl $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $[[NUM1]]{{$}} -; CHECK-NEXT: shr_s $push[[NUM3:[0-9]+]]=, $pop[[NUM2]], $[[NUM1]]{{$}} +; CHECK: i32.load8_u $push[[NUM0:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: i32.const $push[[NUM1:[0-9]+]]=, 31{{$}} +; CHECK-NEXT: shl $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM1]]{{$}} +; CHECK-NEXT: i32.const $push[[NUM4:[0-9]+]]=, 31{{$}} +; CHECK-NEXT: shr_s $push[[NUM3:[0-9]+]]=, $pop[[NUM2]], $pop[[NUM4]]{{$}} ; CHECK-NEXT: return $pop[[NUM3]]{{$}} define i32 @load_s_i1_i32(i1* %p) { %v = load i1, i1* %p @@ -36,10 +37,11 @@ define i64 @load_u_i1_i64(i1* %p) { } ; CHECK-LABEL: load_s_i1_i64: -; CHECK: i64.const $[[NUM1:[0-9]+]]=, 63{{$}} -; CHECK-NEXT: i64.load8_u $push[[NUM0:[0-9]+]]=, 0($0){{$}} -; CHECK-NEXT: shl $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $[[NUM1]]{{$}} -; CHECK-NEXT: shr_s $push[[NUM3:[0-9]+]]=, $pop[[NUM2]], $[[NUM1]]{{$}} +; CHECK: i64.load8_u $push[[NUM0:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: i64.const $push[[NUM1:[0-9]+]]=, 63{{$}} +; CHECK-NEXT: shl $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM1]]{{$}} +; CHECK-NEXT: i64.const $push[[NUM4:[0-9]+]]=, 63{{$}} +; CHECK-NEXT: shr_s $push[[NUM3:[0-9]+]]=, $pop[[NUM2]], $pop[[NUM4]]{{$}} ; CHECK-NEXT: return $pop[[NUM3]]{{$}} define i64 @load_s_i1_i64(i1* %p) { %v = load i1, i1* %p @@ -50,7 +52,7 @@ define i64 @load_s_i1_i64(i1* %p) { ; CHECK-LABEL: store_i32_i1: ; CHECK: i32.const $push[[NUM0:[0-9]+]]=, 1{{$}} ; CHECK-NEXT: i32.and $push[[NUM1:[0-9]+]]=, $1, $pop[[NUM0]]{{$}} -; CHECK-NEXT: i32.store8 $discard=, 0($0), $pop[[NUM1]]{{$}} +; CHECK-NEXT: i32.store8 $drop=, 0($0), $pop[[NUM1]]{{$}} define void @store_i32_i1(i1* %p, i32 %v) { %t = trunc i32 %v to i1 store i1 %t, i1* %p @@ -60,7 +62,7 @@ define void @store_i32_i1(i1* %p, i32 %v) { ; CHECK-LABEL: store_i64_i1: ; CHECK: i64.const $push[[NUM0:[0-9]+]]=, 1{{$}} ; CHECK-NEXT: i64.and $push[[NUM1:[0-9]+]]=, $1, $pop[[NUM0]]{{$}} -; CHECK-NEXT: i64.store8 $discard=, 0($0), $pop[[NUM1]]{{$}} +; CHECK-NEXT: i64.store8 $drop=, 0($0), $pop[[NUM1]]{{$}} define void @store_i64_i1(i1* %p, i64 %v) { %t = trunc i64 %v to i1 store i1 %t, i1* %p diff --git a/test/CodeGen/WebAssembly/load.ll b/test/CodeGen/WebAssembly/load.ll index 243fa9d50ad6..a8e174e914e1 100644 --- a/test/CodeGen/WebAssembly/load.ll +++ b/test/CodeGen/WebAssembly/load.ll @@ -1,4 +1,5 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -fast-isel -fast-isel-abort=1 | FileCheck %s ; Test that basic loads are assembled properly. diff --git a/test/CodeGen/WebAssembly/loop-idiom.ll b/test/CodeGen/WebAssembly/loop-idiom.ll deleted file mode 100644 index 2a233c406900..000000000000 --- a/test/CodeGen/WebAssembly/loop-idiom.ll +++ /dev/null @@ -1,53 +0,0 @@ -; RUN: opt -loop-idiom -S < %s -march=wasm32 | FileCheck %s - -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" - - -; Make sure loop-idiom doesn't create memcpy or memset. These aren't well -; supported in WebAssembly for now. -; -; TODO Check the patterns are recognized once memcpy / memset are supported. - -; CHECK-LABEL: @cpy( -; CHECK-NOT: llvm.memcpy -; CHECK: load -; CHECK: store -define void @cpy(i64 %Size) { -bb.nph: - %Base = alloca i8, i32 10000 - %Dest = alloca i8, i32 10000 - br label %for.body - -for.body: - %indvar = phi i64 [ 0, %bb.nph ], [ %indvar.next, %for.body ] - %I.0.014 = getelementptr i8, i8* %Base, i64 %indvar - %DestI = getelementptr i8, i8* %Dest, i64 %indvar - %V = load i8, i8* %I.0.014, align 1 - store i8 %V, i8* %DestI, align 1 - %indvar.next = add i64 %indvar, 1 - %exitcond = icmp eq i64 %indvar.next, %Size - br i1 %exitcond, label %for.end, label %for.body - -for.end: - ret void -} - -; CHECK-LABEL: @set( -; CHECK-NOT: llvm.memset -; CHECK: store -define void @set(i8* %Base, i64 %Size) { -bb.nph: - br label %for.body - -for.body: - %indvar = phi i64 [ 0, %bb.nph ], [ %indvar.next, %for.body ] - %I.0.014 = getelementptr i8, i8* %Base, i64 %indvar - store i8 0, i8* %I.0.014, align 1 - %indvar.next = add i64 %indvar, 1 - %exitcond = icmp eq i64 %indvar.next, %Size - br i1 %exitcond, label %for.end, label %for.body - -for.end: - ret void -} diff --git a/test/CodeGen/WebAssembly/mem-intrinsics.ll b/test/CodeGen/WebAssembly/mem-intrinsics.ll new file mode 100644 index 000000000000..71787feb77dc --- /dev/null +++ b/test/CodeGen/WebAssembly/mem-intrinsics.ll @@ -0,0 +1,140 @@ +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s + +; Test memcpy, memmove, and memset intrinsics. + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +declare void @llvm.memcpy.p0i8.p0i8.i32(i8* nocapture, i8* nocapture readonly, i32, i32, i1) +declare void @llvm.memmove.p0i8.p0i8.i32(i8* nocapture, i8* nocapture readonly, i32, i32, i1) +declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i32, i1) + +; Test that return values are optimized. + +; CHECK-LABEL: copy_yes: +; CHECK: i32.call $push0=, memcpy@FUNCTION, $0, $1, $2{{$}} +; CHECK-NEXT: return $pop0{{$}} +define i8* @copy_yes(i8* %dst, i8* %src, i32 %len) { + call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dst, i8* %src, i32 %len, i32 1, i1 false) + ret i8* %dst +} + +; CHECK-LABEL: copy_no: +; CHECK: i32.call $drop=, memcpy@FUNCTION, $0, $1, $2{{$}} +; CHECK-NEXT: return{{$}} +define void @copy_no(i8* %dst, i8* %src, i32 %len) { + call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dst, i8* %src, i32 %len, i32 1, i1 false) + ret void +} + +; CHECK-LABEL: move_yes: +; CHECK: i32.call $push0=, memmove@FUNCTION, $0, $1, $2{{$}} +; CHECK-NEXT: return $pop0{{$}} +define i8* @move_yes(i8* %dst, i8* %src, i32 %len) { + call void @llvm.memmove.p0i8.p0i8.i32(i8* %dst, i8* %src, i32 %len, i32 1, i1 false) + ret i8* %dst +} + +; CHECK-LABEL: move_no: +; CHECK: i32.call $drop=, memmove@FUNCTION, $0, $1, $2{{$}} +; CHECK-NEXT: return{{$}} +define void @move_no(i8* %dst, i8* %src, i32 %len) { + call void @llvm.memmove.p0i8.p0i8.i32(i8* %dst, i8* %src, i32 %len, i32 1, i1 false) + ret void +} + +; CHECK-LABEL: set_yes: +; CHECK: i32.call $push0=, memset@FUNCTION, $0, $1, $2{{$}} +; CHECK-NEXT: return $pop0{{$}} +define i8* @set_yes(i8* %dst, i8 %src, i32 %len) { + call void @llvm.memset.p0i8.i32(i8* %dst, i8 %src, i32 %len, i32 1, i1 false) + ret i8* %dst +} + +; CHECK-LABEL: set_no: +; CHECK: i32.call $drop=, memset@FUNCTION, $0, $1, $2{{$}} +; CHECK-NEXT: return{{$}} +define void @set_no(i8* %dst, i8 %src, i32 %len) { + call void @llvm.memset.p0i8.i32(i8* %dst, i8 %src, i32 %len, i32 1, i1 false) + ret void +} + + +; CHECK-LABEL: frame_index: +; CHECK: i32.call $drop=, memset@FUNCTION, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} +; CHECK: i32.call $push{{[0-9]+}}=, memset@FUNCTION, ${{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} +; CHECK: return{{$}} +define void @frame_index() { +entry: + %a = alloca [2048 x i8], align 16 + %b = alloca [2048 x i8], align 16 + %0 = getelementptr inbounds [2048 x i8], [2048 x i8]* %a, i32 0, i32 0 + %1 = getelementptr inbounds [2048 x i8], [2048 x i8]* %b, i32 0, i32 0 + call void @llvm.memset.p0i8.i32(i8* %0, i8 256, i32 1024, i32 16, i1 false) + call void @llvm.memset.p0i8.i32(i8* %1, i8 256, i32 1024, i32 16, i1 false) + ret void +} + +; If the result value of memset doesn't get stackified, it should be marked +; $drop. Note that we use a call to prevent tail dup so that we can test +; this specific functionality. + +; CHECK-LABEL: drop_result: +; CHECK: i32.call $drop=, memset@FUNCTION, $0, $1, $2 +declare i8* @def() +declare void @block_tail_dup() +define i8* @drop_result(i8* %arg, i8 %arg1, i32 %arg2, i32 %arg3, i32 %arg4) { +bb: + %tmp = icmp eq i32 %arg3, 0 + br i1 %tmp, label %bb5, label %bb9 + +bb5: + %tmp6 = icmp eq i32 %arg4, 0 + br i1 %tmp6, label %bb7, label %bb8 + +bb7: + call void @llvm.memset.p0i8.i32(i8* %arg, i8 %arg1, i32 %arg2, i32 1, i1 false) + br label %bb11 + +bb8: + br label %bb11 + +bb9: + %tmp10 = call i8* @def() + br label %bb11 + +bb11: + %tmp12 = phi i8* [ %arg, %bb7 ], [ %arg, %bb8 ], [ %tmp10, %bb9 ] + call void @block_tail_dup() + ret i8* %tmp12 +} + +; This is the same as drop_result, except we let tail dup happen, so the +; result of the memset *is* stackified. + +; CHECK-LABEL: tail_dup_to_reuse_result: +; CHECK: i32.call $push{{[0-9]+}}=, memset@FUNCTION, $0, $1, $2 +define i8* @tail_dup_to_reuse_result(i8* %arg, i8 %arg1, i32 %arg2, i32 %arg3, i32 %arg4) { +bb: + %tmp = icmp eq i32 %arg3, 0 + br i1 %tmp, label %bb5, label %bb9 + +bb5: + %tmp6 = icmp eq i32 %arg4, 0 + br i1 %tmp6, label %bb7, label %bb8 + +bb7: + call void @llvm.memset.p0i8.i32(i8* %arg, i8 %arg1, i32 %arg2, i32 1, i1 false) + br label %bb11 + +bb8: + br label %bb11 + +bb9: + %tmp10 = call i8* @def() + br label %bb11 + +bb11: + %tmp12 = phi i8* [ %arg, %bb7 ], [ %arg, %bb8 ], [ %tmp10, %bb9 ] + ret i8* %tmp12 +} diff --git a/test/CodeGen/WebAssembly/memory-addr32.ll b/test/CodeGen/WebAssembly/memory-addr32.ll index e6c15633fd63..583201b15f99 100644 --- a/test/CodeGen/WebAssembly/memory-addr32.ll +++ b/test/CodeGen/WebAssembly/memory-addr32.ll @@ -1,19 +1,19 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test that basic memory operations assemble as expected with 32-bit addresses. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" -declare i32 @llvm.wasm.memory.size.i32() nounwind readonly +declare i32 @llvm.wasm.current.memory.i32() nounwind readonly declare void @llvm.wasm.grow.memory.i32(i32) nounwind -; CHECK-LABEL: memory_size: +; CHECK-LABEL: current_memory: ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: memory_size $push0={{$}} +; CHECK-NEXT: current_memory $push0={{$}} ; CHECK-NEXT: return $pop0{{$}} -define i32 @memory_size() { - %a = call i32 @llvm.wasm.memory.size.i32() +define i32 @current_memory() { + %a = call i32 @llvm.wasm.current.memory.i32() ret i32 %a } diff --git a/test/CodeGen/WebAssembly/memory-addr64.ll b/test/CodeGen/WebAssembly/memory-addr64.ll index d504c277f306..dc6da6121718 100644 --- a/test/CodeGen/WebAssembly/memory-addr64.ll +++ b/test/CodeGen/WebAssembly/memory-addr64.ll @@ -1,19 +1,19 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test that basic memory operations assemble as expected with 64-bit addresses. target datalayout = "e-m:e-p:64:64-i64:64-n32:64-S128" target triple = "wasm64-unknown-unknown" -declare i64 @llvm.wasm.memory.size.i64() nounwind readonly +declare i64 @llvm.wasm.current.memory.i64() nounwind readonly declare void @llvm.wasm.grow.memory.i64(i64) nounwind -; CHECK-LABEL: memory_size: +; CHECK-LABEL: current_memory: ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: memory_size $push0={{$}} +; CHECK-NEXT: current_memory $push0={{$}} ; CHECK-NEXT: return $pop0{{$}} -define i64 @memory_size() { - %a = call i64 @llvm.wasm.memory.size.i64() +define i64 @current_memory() { + %a = call i64 @llvm.wasm.current.memory.i64() ret i64 %a } diff --git a/test/CodeGen/WebAssembly/non-executable-stack.ll b/test/CodeGen/WebAssembly/non-executable-stack.ll new file mode 100644 index 000000000000..b81063724e9c --- /dev/null +++ b/test/CodeGen/WebAssembly/non-executable-stack.ll @@ -0,0 +1,9 @@ +; RUN: llc < %s -asm-verbose=false | FileCheck %s + +; Test that we don't emit anything declaring a non-executable stack, +; because wasm's stack is always non-executable. + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +; CHECK-NOT: .note.GNU-stack diff --git a/test/CodeGen/WebAssembly/offset-folding.ll b/test/CodeGen/WebAssembly/offset-folding.ll index 159a25eba358..863549fc20fc 100644 --- a/test/CodeGen/WebAssembly/offset-folding.ll +++ b/test/CodeGen/WebAssembly/offset-folding.ll @@ -5,10 +5,7 @@ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" -; FIXME: make this 'external' and make sure it still works. WebAssembly -; currently only supports linking single files, so 'external' makes -; little sense. -@x = global [0 x i32] zeroinitializer +@x = external global [0 x i32] @y = global [50 x i32] zeroinitializer ; Test basic constant offsets of both defined and external symbols. @@ -46,3 +43,21 @@ define i32* @test2() { define i32* @test3() { ret i32* getelementptr ([50 x i32], [50 x i32]* @y, i32 0, i32 0) } + +; Test negative offsets. + +; CHECK-LABEL: test4: +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.const $push0=, x-188{{$}} +; CHECK=NEXT: return $pop0{{$}} +define i32* @test4() { + ret i32* getelementptr ([0 x i32], [0 x i32]* @x, i32 0, i32 -47) +} + +; CHECK-LABEL: test5: +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.const $push0=, y-188{{$}} +; CHECK=NEXT: return $pop0{{$}} +define i32* @test5() { + ret i32* getelementptr ([50 x i32], [50 x i32]* @y, i32 0, i32 -47) +} diff --git a/test/CodeGen/WebAssembly/offset.ll b/test/CodeGen/WebAssembly/offset.ll index 828f40206a96..fcd8b49758ed 100644 --- a/test/CodeGen/WebAssembly/offset.ll +++ b/test/CodeGen/WebAssembly/offset.ll @@ -125,10 +125,21 @@ define i64 @load_i64_with_unfolded_gep_offset(i64* %p) { ret i64 %t } +; CHECK-LABEL: load_i32_with_folded_or_offset: +; CHECK: i32.load8_s $push{{[0-9]+}}=, 2($pop{{[0-9]+}}){{$}} +define i32 @load_i32_with_folded_or_offset(i32 %x) { + %and = and i32 %x, -4 + %t0 = inttoptr i32 %and to i8* + %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2 + %t1 = load i8, i8* %arrayidx, align 1 + %conv = sext i8 %t1 to i32 + ret i32 %conv +} + ; Same as above but with store. ; CHECK-LABEL: store_i32_with_folded_offset: -; CHECK: i32.store $discard=, 24($0), $pop0{{$}} +; CHECK: i32.store $drop=, 24($0), $pop0{{$}} define void @store_i32_with_folded_offset(i32* %p) { %q = ptrtoint i32* %p to i32 %r = add nuw i32 %q, 24 @@ -140,7 +151,7 @@ define void @store_i32_with_folded_offset(i32* %p) { ; Same as above but with store. ; CHECK-LABEL: store_i32_with_folded_gep_offset: -; CHECK: i32.store $discard=, 24($0), $pop0{{$}} +; CHECK: i32.store $drop=, 24($0), $pop0{{$}} define void @store_i32_with_folded_gep_offset(i32* %p) { %s = getelementptr inbounds i32, i32* %p, i32 6 store i32 0, i32* %s @@ -152,7 +163,7 @@ define void @store_i32_with_folded_gep_offset(i32* %p) { ; CHECK-LABEL: store_i32_with_unfolded_gep_negative_offset: ; CHECK: i32.const $push0=, -24{{$}} ; CHECK: i32.add $push1=, $0, $pop0{{$}} -; CHECK: i32.store $discard=, 0($pop1), $pop2{{$}} +; CHECK: i32.store $drop=, 0($pop1), $pop2{{$}} define void @store_i32_with_unfolded_gep_negative_offset(i32* %p) { %s = getelementptr inbounds i32, i32* %p, i32 -6 store i32 0, i32* %s @@ -164,7 +175,7 @@ define void @store_i32_with_unfolded_gep_negative_offset(i32* %p) { ; CHECK-LABEL: store_i32_with_unfolded_offset: ; CHECK: i32.const $push0=, 24{{$}} ; CHECK: i32.add $push1=, $0, $pop0{{$}} -; CHECK: i32.store $discard=, 0($pop1), $pop2{{$}} +; CHECK: i32.store $drop=, 0($pop1), $pop2{{$}} define void @store_i32_with_unfolded_offset(i32* %p) { %q = ptrtoint i32* %p to i32 %r = add nsw i32 %q, 24 @@ -178,7 +189,7 @@ define void @store_i32_with_unfolded_offset(i32* %p) { ; CHECK-LABEL: store_i32_with_unfolded_gep_offset: ; CHECK: i32.const $push0=, 24{{$}} ; CHECK: i32.add $push1=, $0, $pop0{{$}} -; CHECK: i32.store $discard=, 0($pop1), $pop2{{$}} +; CHECK: i32.store $drop=, 0($pop1), $pop2{{$}} define void @store_i32_with_unfolded_gep_offset(i32* %p) { %s = getelementptr i32, i32* %p, i32 6 store i32 0, i32* %s @@ -188,7 +199,7 @@ define void @store_i32_with_unfolded_gep_offset(i32* %p) { ; Same as above but with store with i64. ; CHECK-LABEL: store_i64_with_folded_offset: -; CHECK: i64.store $discard=, 24($0), $pop0{{$}} +; CHECK: i64.store $drop=, 24($0), $pop0{{$}} define void @store_i64_with_folded_offset(i64* %p) { %q = ptrtoint i64* %p to i32 %r = add nuw i32 %q, 24 @@ -200,7 +211,7 @@ define void @store_i64_with_folded_offset(i64* %p) { ; Same as above but with store with i64. ; CHECK-LABEL: store_i64_with_folded_gep_offset: -; CHECK: i64.store $discard=, 24($0), $pop0{{$}} +; CHECK: i64.store $drop=, 24($0), $pop0{{$}} define void @store_i64_with_folded_gep_offset(i64* %p) { %s = getelementptr inbounds i64, i64* %p, i32 3 store i64 0, i64* %s @@ -212,7 +223,7 @@ define void @store_i64_with_folded_gep_offset(i64* %p) { ; CHECK-LABEL: store_i64_with_unfolded_gep_negative_offset: ; CHECK: i32.const $push0=, -24{{$}} ; CHECK: i32.add $push1=, $0, $pop0{{$}} -; CHECK: i64.store $discard=, 0($pop1), $pop2{{$}} +; CHECK: i64.store $drop=, 0($pop1), $pop2{{$}} define void @store_i64_with_unfolded_gep_negative_offset(i64* %p) { %s = getelementptr inbounds i64, i64* %p, i32 -3 store i64 0, i64* %s @@ -224,7 +235,7 @@ define void @store_i64_with_unfolded_gep_negative_offset(i64* %p) { ; CHECK-LABEL: store_i64_with_unfolded_offset: ; CHECK: i32.const $push0=, 24{{$}} ; CHECK: i32.add $push1=, $0, $pop0{{$}} -; CHECK: i64.store $discard=, 0($pop1), $pop2{{$}} +; CHECK: i64.store $drop=, 0($pop1), $pop2{{$}} define void @store_i64_with_unfolded_offset(i64* %p) { %q = ptrtoint i64* %p to i32 %r = add nsw i32 %q, 24 @@ -238,13 +249,23 @@ define void @store_i64_with_unfolded_offset(i64* %p) { ; CHECK-LABEL: store_i64_with_unfolded_gep_offset: ; CHECK: i32.const $push0=, 24{{$}} ; CHECK: i32.add $push1=, $0, $pop0{{$}} -; CHECK: i64.store $discard=, 0($pop1), $pop2{{$}} +; CHECK: i64.store $drop=, 0($pop1), $pop2{{$}} define void @store_i64_with_unfolded_gep_offset(i64* %p) { %s = getelementptr i64, i64* %p, i32 3 store i64 0, i64* %s ret void } +; CHECK-LABEL: store_i32_with_folded_or_offset: +; CHECK: i32.store8 $drop=, 2($pop{{[0-9]+}}), $pop{{[0-9]+}}{{$}} +define void @store_i32_with_folded_or_offset(i32 %x) { + %and = and i32 %x, -4 + %t0 = inttoptr i32 %and to i8* + %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2 + store i8 0, i8* %arrayidx, align 1 + ret void +} + ; When loading from a fixed address, materialize a zero. ; CHECK-LABEL: load_i32_from_numeric_address @@ -266,8 +287,9 @@ define i32 @load_i32_from_global_address() { } ; CHECK-LABEL: store_i32_to_numeric_address: -; CHECK: i32.const $0=, 0{{$}} -; CHECK: i32.store $discard=, 42($0), $0{{$}} +; CHECK-NEXT: i32.const $push0=, 0{{$}} +; CHECK-NEXT: i32.const $push1=, 0{{$}} +; CHECK-NEXT: i32.store $drop=, 42($pop0), $pop1{{$}} define void @store_i32_to_numeric_address() { %s = inttoptr i32 42 to i32* store i32 0, i32* %s @@ -275,8 +297,9 @@ define void @store_i32_to_numeric_address() { } ; CHECK-LABEL: store_i32_to_global_address: -; CHECK: i32.const $0=, 0{{$}} -; CHECK: i32.store $discard=, gv($0), $0{{$}} +; CHECK: i32.const $push0=, 0{{$}} +; CHECK: i32.const $push1=, 0{{$}} +; CHECK: i32.store $drop=, gv($pop0), $pop1{{$}} define void @store_i32_to_global_address() { store i32 0, i32* @gv ret void @@ -333,7 +356,7 @@ define i32 @load_i8_u_with_folded_gep_offset(i8* %p) { ; Fold an offset into a truncating store. ; CHECK-LABEL: store_i8_with_folded_offset: -; CHECK: i32.store8 $discard=, 24($0), $pop0{{$}} +; CHECK: i32.store8 $drop=, 24($0), $pop0{{$}} define void @store_i8_with_folded_offset(i8* %p) { %q = ptrtoint i8* %p to i32 %r = add nuw i32 %q, 24 @@ -345,7 +368,7 @@ define void @store_i8_with_folded_offset(i8* %p) { ; Fold a gep offset into a truncating store. ; CHECK-LABEL: store_i8_with_folded_gep_offset: -; CHECK: i32.store8 $discard=, 24($0), $pop0{{$}} +; CHECK: i32.store8 $drop=, 24($0), $pop0{{$}} define void @store_i8_with_folded_gep_offset(i8* %p) { %s = getelementptr inbounds i8, i8* %p, i32 24 store i8 0, i8* %s @@ -359,10 +382,10 @@ define void @store_i8_with_folded_gep_offset(i8* %p) { ; CHECK: i32.load $3=, 4($0){{$}} ; CHECK: i32.load $4=, 8($0){{$}} ; CHECK: i32.load $push0=, 12($0){{$}} -; CHECK: i32.store $discard=, 12($1), $pop0{{$}} -; CHECK: i32.store $discard=, 8($1), $4{{$}} -; CHECK: i32.store $discard=, 4($1), $3{{$}} -; CHECK: i32.store $discard=, 0($1), $2{{$}} +; CHECK: i32.store $drop=, 12($1), $pop0{{$}} +; CHECK: i32.store $drop=, 8($1), $4{{$}} +; CHECK: i32.store $drop=, 4($1), $3{{$}} +; CHECK: i32.store $drop=, 0($1), $2{{$}} define void @aggregate_load_store({i32,i32,i32,i32}* %p, {i32,i32,i32,i32}* %q) { ; volatile so that things stay in order for the tests above %t = load volatile {i32,i32,i32,i32}, {i32, i32,i32,i32}* %p @@ -370,14 +393,27 @@ define void @aggregate_load_store({i32,i32,i32,i32}* %p, {i32,i32,i32,i32}* %q) ret void } -; Fold the offsets when lowering aggregate return values. +; Fold the offsets when lowering aggregate return values. The stores get +; merged into i64 stores. ; CHECK-LABEL: aggregate_return: -; CHECK: i32.const $push0=, 0{{$}} -; CHECK: i32.store $push1=, 12($0), $pop0{{$}} -; CHECK: i32.store $push2=, 8($0), $pop1{{$}} -; CHECK: i32.store $push3=, 4($0), $pop2{{$}} -; CHECK: i32.store $discard=, 0($0), $pop3{{$}} +; CHECK: i64.const $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK: i64.store $push[[L1:[0-9]+]]=, 8($0):p2align=2, $pop[[L0]]{{$}} +; CHECK: i64.store $drop=, 0($0):p2align=2, $pop[[L1]]{{$}} define {i32,i32,i32,i32} @aggregate_return() { ret {i32,i32,i32,i32} zeroinitializer } + +; Fold the offsets when lowering aggregate return values. The stores are not +; merged. + +; CHECK-LABEL: aggregate_return_without_merge: +; CHECK: i32.const $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK: i32.store8 $push[[L1:[0-9]+]]=, 14($0), $pop[[L0]]{{$}} +; CHECK: i32.store16 $push[[L2:[0-9]+]]=, 12($0), $pop[[L1]]{{$}} +; CHECK: i32.store $drop=, 8($0), $pop[[L2]]{{$}} +; CHECK: i64.const $push[[L3:[0-9]+]]=, 0{{$}} +; CHECK: i64.store $drop=, 0($0), $pop[[L3]]{{$}} +define {i64,i32,i16,i8} @aggregate_return_without_merge() { + ret {i64,i32,i16,i8} zeroinitializer +} diff --git a/test/CodeGen/WebAssembly/phi.ll b/test/CodeGen/WebAssembly/phi.ll index 00e5859b75cf..747ae5cb15d4 100644 --- a/test/CodeGen/WebAssembly/phi.ll +++ b/test/CodeGen/WebAssembly/phi.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -verify-machineinstrs | FileCheck %s ; Test that phis are lowered. diff --git a/test/CodeGen/WebAssembly/reg-stackify.ll b/test/CodeGen/WebAssembly/reg-stackify.ll index f8cae7f92404..23cbd03aa080 100644 --- a/test/CodeGen/WebAssembly/reg-stackify.ll +++ b/test/CodeGen/WebAssembly/reg-stackify.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -verify-machineinstrs | FileCheck %s ; Test the register stackifier pass. @@ -28,7 +28,7 @@ define i32 @no1(i32* %p, i32* dereferenceable(4) %q) { ; Yes because of invariant load and no side effects. ; CHECK-LABEL: yes0: -; CHECK: return $pop0{{$}} +; CHECK: return $pop{{[0-9]+}}{{$}} define i32 @yes0(i32* %p, i32* dereferenceable(4) %q) { %t = load i32, i32* %q, !invariant.load !0 store i32 0, i32* %p @@ -44,32 +44,67 @@ define i32 @yes1(i32* %q) { ret i32 %t } +; Yes because undefined behavior can be sunk past a store. + +; CHECK-LABEL: sink_trap: +; CHECK: return $pop{{[0-9]+}}{{$}} +define i32 @sink_trap(i32 %x, i32 %y, i32* %p) { + %t = sdiv i32 %x, %y + store volatile i32 0, i32* %p + ret i32 %t +} + +; Yes because the call is readnone. + +; CHECK-LABEL: sink_readnone_call: +; CHECK: return $pop0{{$}} +declare i32 @readnone_callee() readnone nounwind +define i32 @sink_readnone_call(i32 %x, i32 %y, i32* %p) { + %t = call i32 @readnone_callee() + store volatile i32 0, i32* %p + ret i32 %t +} + +; No because the call is readonly and there's an intervening store. + +; CHECK-LABEL: no_sink_readonly_call: +; CHECK: return ${{[0-9]+}}{{$}} +declare i32 @readonly_callee() readonly nounwind +define i32 @no_sink_readonly_call(i32 %x, i32 %y, i32* %p) { + %t = call i32 @readonly_callee() + store i32 0, i32* %p + ret i32 %t +} + ; Don't schedule stack uses into the stack. To reduce register pressure, the ; scheduler might be tempted to move the definition of $2 down. However, this ; would risk getting incorrect liveness if the instructions are later ; rearranged to make the stack contiguous. ; CHECK-LABEL: stack_uses: -; CHECK-NEXT: .param i32, i32, i32, i32{{$}} +; CHECK: .param i32, i32, i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: .local i32, i32{{$}} -; CHECK-NEXT: i32.const $5=, 2{{$}} -; CHECK-NEXT: i32.const $4=, 1{{$}} ; CHECK-NEXT: block{{$}} -; CHECK-NEXT: i32.lt_s $push0=, $0, $4{{$}} -; CHECK-NEXT: i32.lt_s $push1=, $1, $5{{$}} -; CHECK-NEXT: i32.xor $push4=, $pop0, $pop1{{$}} -; CHECK-NEXT: i32.lt_s $push2=, $2, $4{{$}} -; CHECK-NEXT: i32.lt_s $push3=, $3, $5{{$}} -; CHECK-NEXT: i32.xor $push5=, $pop2, $pop3{{$}} -; CHECK-NEXT: i32.xor $push6=, $pop4, $pop5{{$}} -; CHECK-NEXT: i32.ne $push7=, $pop6, $4{{$}} -; CHECK-NEXT: br_if $pop7, 0{{$}} -; CHECK-NEXT: i32.const $push8=, 0{{$}} -; CHECK-NEXT: return $pop8{{$}} -; CHECK-NEXT: .LBB4_2: +; CHECK-NEXT: i32.const $push[[L13:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.lt_s $push[[L0:[0-9]+]]=, $0, $pop[[L13]]{{$}} +; CHECK-NEXT: i32.const $push[[L1:[0-9]+]]=, 2{{$}} +; CHECK-NEXT: i32.lt_s $push[[L2:[0-9]+]]=, $1, $pop[[L1]]{{$}} +; CHECK-NEXT: i32.xor $push[[L5:[0-9]+]]=, $pop[[L0]], $pop[[L2]]{{$}} +; CHECK-NEXT: i32.const $push[[L12:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.lt_s $push[[L3:[0-9]+]]=, $2, $pop[[L12]]{{$}} +; CHECK-NEXT: i32.const $push[[L11:[0-9]+]]=, 2{{$}} +; CHECK-NEXT: i32.lt_s $push[[L4:[0-9]+]]=, $3, $pop[[L11]]{{$}} +; CHECK-NEXT: i32.xor $push[[L6:[0-9]+]]=, $pop[[L3]], $pop[[L4]]{{$}} +; CHECK-NEXT: i32.xor $push[[L7:[0-9]+]]=, $pop[[L5]], $pop[[L6]]{{$}} +; CHECK-NEXT: i32.const $push10=, 1{{$}} +; CHECK-NEXT: i32.ne $push8=, $pop7, $pop10{{$}} +; CHECK-NEXT: br_if 0, $pop8{{$}} +; CHECK-NEXT: i32.const $push9=, 0{{$}} +; CHECK-NEXT: return $pop9{{$}} +; CHECK-NEXT: .LBB7_2: ; CHECK-NEXT: end_block{{$}} -; CHECK-NEXT: return $4{{$}} +; CHECK-NEXT: i32.const $push14=, 1{{$}} +; CHECK-NEXT: return $pop14{{$}} define i32 @stack_uses(i32 %x, i32 %y, i32 %z, i32 %w) { entry: %c = icmp sle i32 %x, 0 @@ -87,19 +122,20 @@ false: } ; Test an interesting case where the load has multiple uses and cannot -; be trivially stackified. +; be trivially stackified. However, it can be stackified with a tee_local. ; CHECK-LABEL: multiple_uses: -; CHECK-NEXT: .param i32, i32, i32{{$}} +; CHECK: .param i32, i32, i32{{$}} ; CHECK-NEXT: .local i32{{$}} -; CHECK-NEXT: i32.load $3=, 0($2){{$}} ; CHECK-NEXT: block{{$}} -; CHECK-NEXT: i32.ge_u $push0=, $3, $1{{$}} -; CHECK-NEXT: br_if $pop0, 0{{$}} -; CHECK-NEXT: i32.lt_u $push1=, $3, $0{{$}} -; CHECK-NEXT: br_if $pop1, 0{{$}} -; CHECK-NEXT: i32.store $discard=, 0($2), $3{{$}} -; CHECK-NEXT: .LBB5_3: +; CHECK-NEXT: i32.load $push[[NUM0:[0-9]+]]=, 0($2){{$}} +; CHECK-NEXT: tee_local $push[[NUM1:[0-9]+]]=, $3=, $pop[[NUM0]]{{$}} +; CHECK-NEXT: i32.ge_u $push[[NUM2:[0-9]+]]=, $pop[[NUM1]], $1{{$}} +; CHECK-NEXT: br_if 0, $pop[[NUM2]]{{$}} +; CHECK-NEXT: i32.lt_u $push[[NUM3:[0-9]+]]=, $3, $0{{$}} +; CHECK-NEXT: br_if 0, $pop[[NUM3]]{{$}} +; CHECK-NEXT: i32.store $drop=, 0($2), $3{{$}} +; CHECK-NEXT: .LBB8_3: ; CHECK-NEXT: end_block{{$}} ; CHECK-NEXT: return{{$}} define void @multiple_uses(i32* %arg0, i32* %arg1, i32* %arg2) nounwind { @@ -125,4 +161,314 @@ return: ret void } -!0 = !{} +; Don't stackify stores effects across other instructions with side effects. + +; CHECK: side_effects: +; CHECK: store +; CHECK-NEXT: call +; CHECK-NEXT: store +; CHECK-NEXT: call +declare void @evoke_side_effects() +define hidden void @stackify_store_across_side_effects(double* nocapture %d) { +entry: + store double 2.0, double* %d + call void @evoke_side_effects() + store double 2.0, double* %d + call void @evoke_side_effects() + ret void +} + +; Div instructions have side effects and can't be reordered, but this entire +; function should still be able to be stackified because it's already in +; tree order. + +; CHECK-LABEL: div_tree: +; CHECK: .param i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.div_s $push[[L0:[0-9]+]]=, $0, $1{{$}} +; CHECK-NEXT: i32.div_s $push[[L1:[0-9]+]]=, $2, $3{{$}} +; CHECK-NEXT: i32.div_s $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: i32.div_s $push[[L3:[0-9]+]]=, $4, $5{{$}} +; CHECK-NEXT: i32.div_s $push[[L4:[0-9]+]]=, $6, $7{{$}} +; CHECK-NEXT: i32.div_s $push[[L5:[0-9]+]]=, $pop[[L3]], $pop[[L4]]{{$}} +; CHECK-NEXT: i32.div_s $push[[L6:[0-9]+]]=, $pop[[L2]], $pop[[L5]]{{$}} +; CHECK-NEXT: i32.div_s $push[[L7:[0-9]+]]=, $8, $9{{$}} +; CHECK-NEXT: i32.div_s $push[[L8:[0-9]+]]=, $10, $11{{$}} +; CHECK-NEXT: i32.div_s $push[[L9:[0-9]+]]=, $pop[[L7]], $pop[[L8]]{{$}} +; CHECK-NEXT: i32.div_s $push[[L10:[0-9]+]]=, $12, $13{{$}} +; CHECK-NEXT: i32.div_s $push[[L11:[0-9]+]]=, $14, $15{{$}} +; CHECK-NEXT: i32.div_s $push[[L12:[0-9]+]]=, $pop[[L10]], $pop[[L11]]{{$}} +; CHECK-NEXT: i32.div_s $push[[L13:[0-9]+]]=, $pop[[L9]], $pop[[L12]]{{$}} +; CHECK-NEXT: i32.div_s $push[[L14:[0-9]+]]=, $pop[[L6]], $pop[[L13]]{{$}} +; CHECK-NEXT: return $pop[[L14]]{{$}} +define i32 @div_tree(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h, i32 %i, i32 %j, i32 %k, i32 %l, i32 %m, i32 %n, i32 %o, i32 %p) { +entry: + %div = sdiv i32 %a, %b + %div1 = sdiv i32 %c, %d + %div2 = sdiv i32 %div, %div1 + %div3 = sdiv i32 %e, %f + %div4 = sdiv i32 %g, %h + %div5 = sdiv i32 %div3, %div4 + %div6 = sdiv i32 %div2, %div5 + %div7 = sdiv i32 %i, %j + %div8 = sdiv i32 %k, %l + %div9 = sdiv i32 %div7, %div8 + %div10 = sdiv i32 %m, %n + %div11 = sdiv i32 %o, %p + %div12 = sdiv i32 %div10, %div11 + %div13 = sdiv i32 %div9, %div12 + %div14 = sdiv i32 %div6, %div13 + ret i32 %div14 +} + +; A simple multiple-use case. + +; CHECK-LABEL: simple_multiple_use: +; CHECK: .param i32, i32{{$}} +; CHECK-NEXT: i32.mul $push[[NUM0:[0-9]+]]=, $1, $0{{$}} +; CHECK-NEXT: tee_local $push[[NUM1:[0-9]+]]=, $[[NUM2:[0-9]+]]=, $pop[[NUM0]]{{$}} +; CHECK-NEXT: call use_a@FUNCTION, $pop[[NUM1]]{{$}} +; CHECK-NEXT: call use_b@FUNCTION, $[[NUM2]]{{$}} +; CHECK-NEXT: return{{$}} +declare void @use_a(i32) +declare void @use_b(i32) +define void @simple_multiple_use(i32 %x, i32 %y) { + %mul = mul i32 %y, %x + call void @use_a(i32 %mul) + call void @use_b(i32 %mul) + ret void +} + +; Multiple uses of the same value in one instruction. + +; CHECK-LABEL: multiple_uses_in_same_insn: +; CHECK: .param i32, i32{{$}} +; CHECK-NEXT: i32.mul $push[[NUM0:[0-9]+]]=, $1, $0{{$}} +; CHECK-NEXT: tee_local $push[[NUM1:[0-9]+]]=, $[[NUM2:[0-9]+]]=, $pop[[NUM0]]{{$}} +; CHECK-NEXT: call use_2@FUNCTION, $pop[[NUM1]], $[[NUM2]]{{$}} +; CHECK-NEXT: return{{$}} +declare void @use_2(i32, i32) +define void @multiple_uses_in_same_insn(i32 %x, i32 %y) { + %mul = mul i32 %y, %x + call void @use_2(i32 %mul, i32 %mul) + ret void +} + +; Commute operands to achieve better stackifying. + +; CHECK-LABEL: commute: +; CHECK-NOT: param +; CHECK: .result i32{{$}} +; CHECK-NEXT: i32.call $push0=, red@FUNCTION{{$}} +; CHECK-NEXT: i32.call $push1=, green@FUNCTION{{$}} +; CHECK-NEXT: i32.add $push2=, $pop0, $pop1{{$}} +; CHECK-NEXT: i32.call $push3=, blue@FUNCTION{{$}} +; CHECK-NEXT: i32.add $push4=, $pop2, $pop3{{$}} +; CHECK-NEXT: return $pop4{{$}} +declare i32 @red() +declare i32 @green() +declare i32 @blue() +define i32 @commute() { + %call = call i32 @red() + %call1 = call i32 @green() + %add = add i32 %call1, %call + %call2 = call i32 @blue() + %add3 = add i32 %add, %call2 + ret i32 %add3 +} + +; Don't stackify a register when it would move a the def of the register past +; an implicit get_local for the register. + +; CHECK-LABEL: no_stackify_past_use: +; CHECK: i32.call $1=, callee@FUNCTION, $0 +; CHECK-NEXT: i32.const $push0=, 1 +; CHECK-NEXT: i32.add $push1=, $0, $pop0 +; CHECK-NEXT: i32.call $push2=, callee@FUNCTION, $pop1 +; CHECK-NEXT: i32.sub $push3=, $pop2, $1 +; CHECK-NEXT: i32.div_s $push4=, $pop3, $1 +; CHECK-NEXT: return $pop4 +declare i32 @callee(i32) +define i32 @no_stackify_past_use(i32 %arg) { + %tmp1 = call i32 @callee(i32 %arg) + %tmp2 = add i32 %arg, 1 + %tmp3 = call i32 @callee(i32 %tmp2) + %tmp5 = sub i32 %tmp3, %tmp1 + %tmp6 = sdiv i32 %tmp5, %tmp1 + ret i32 %tmp6 +} + +; This is the same as no_stackify_past_use, except using a commutative operator, +; so we can reorder the operands and stackify. + +; CHECK-LABEL: commute_to_fix_ordering: +; CHECK: i32.call $push[[L0:.+]]=, callee@FUNCTION, $0 +; CHECK: tee_local $push[[L1:.+]]=, $1=, $pop[[L0]] +; CHECK: i32.const $push0=, 1 +; CHECK: i32.add $push1=, $0, $pop0 +; CHECK: i32.call $push2=, callee@FUNCTION, $pop1 +; CHECK: i32.add $push3=, $1, $pop2 +; CHECK: i32.mul $push4=, $pop[[L1]], $pop3 +; CHECK: return $pop4 +define i32 @commute_to_fix_ordering(i32 %arg) { + %tmp1 = call i32 @callee(i32 %arg) + %tmp2 = add i32 %arg, 1 + %tmp3 = call i32 @callee(i32 %tmp2) + %tmp5 = add i32 %tmp3, %tmp1 + %tmp6 = mul i32 %tmp5, %tmp1 + ret i32 %tmp6 +} + +; Stackify individual defs of virtual registers with multiple defs. + +; CHECK-LABEL: multiple_defs: +; CHECK: f64.add $push[[NUM0:[0-9]+]]=, ${{[0-9]+}}, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: tee_local $push[[NUM1:[0-9]+]]=, $[[NUM2:[0-9]+]]=, $pop[[NUM0]]{{$}} +; CHECK-NEXT: f64.select $push{{[0-9]+}}=, $pop{{[0-9]+}}, $pop[[NUM1]], ${{[0-9]+}}{{$}} +; CHECK: $[[NUM2]]=, +define void @multiple_defs(i32 %arg, i32 %arg1, i1 %arg2, i1 %arg3, i1 %arg4) { +bb: + br label %bb5 + +bb5: ; preds = %bb21, %bb + %tmp = phi double [ 0.000000e+00, %bb ], [ %tmp22, %bb21 ] + %tmp6 = phi double [ 0.000000e+00, %bb ], [ %tmp23, %bb21 ] + %tmp7 = fcmp olt double %tmp6, 2.323450e+01 + br i1 %tmp7, label %bb8, label %bb21 + +bb8: ; preds = %bb17, %bb5 + %tmp9 = phi double [ %tmp19, %bb17 ], [ %tmp, %bb5 ] + %tmp10 = fadd double %tmp6, -1.000000e+00 + %tmp11 = select i1 %arg2, double -1.135357e+04, double %tmp10 + %tmp12 = fadd double %tmp11, %tmp9 + br i1 %arg3, label %bb17, label %bb13 + +bb13: ; preds = %bb8 + %tmp14 = or i32 %arg1, 2 + %tmp15 = icmp eq i32 %tmp14, 14 + %tmp16 = select i1 %tmp15, double -1.135357e+04, double 0xBFCE147AE147B000 + br label %bb17 + +bb17: ; preds = %bb13, %bb8 + %tmp18 = phi double [ %tmp16, %bb13 ], [ %tmp10, %bb8 ] + %tmp19 = fadd double %tmp18, %tmp12 + %tmp20 = fcmp olt double %tmp6, 2.323450e+01 + br i1 %tmp20, label %bb8, label %bb21 + +bb21: ; preds = %bb17, %bb5 + %tmp22 = phi double [ %tmp, %bb5 ], [ %tmp9, %bb17 ] + %tmp23 = fadd double %tmp6, 1.000000e+00 + br label %bb5 +} + +; Don't move calls past loads +; CHECK-LABEL: no_stackify_call_past_load: +; CHECK: i32.call $0=, red +; CHECK: i32.const $push0=, 0 +; CHECK: i32.load $1=, count($pop0) +@count = hidden global i32 0, align 4 +define i32 @no_stackify_call_past_load() { + %a = call i32 @red() + %b = load i32, i32* @count, align 4 + call i32 @callee(i32 %a) + ret i32 %b + ; use of a +} + +; Don't move stores past loads if there may be aliasing +; CHECK-LABEL: no_stackify_store_past_load +; CHECK: i32.store $[[L0:[0-9]+]]=, 0($1), $0 +; CHECK: i32.load {{.*}}, 0($2) +; CHECK: i32.call {{.*}}, callee@FUNCTION, $[[L0]]{{$}} +define i32 @no_stackify_store_past_load(i32 %a, i32* %p1, i32* %p2) { + store i32 %a, i32* %p1 + %b = load i32, i32* %p2, align 4 + call i32 @callee(i32 %a) + ret i32 %b +} + +; Can still stackify past invariant loads. +; CHECK-LABEL: store_past_invar_load +; CHECK: i32.store $push{{.*}}, 0($1), $0 +; CHECK: i32.call {{.*}}, callee@FUNCTION, $pop +; CHECK: i32.load $push{{.*}}, 0($2) +; CHECK: return $pop +define i32 @store_past_invar_load(i32 %a, i32* %p1, i32* dereferenceable(4) %p2) { + store i32 %a, i32* %p1 + %b = load i32, i32* %p2, !invariant.load !0 + call i32 @callee(i32 %a) + ret i32 %b +} + +; CHECK-LABEL: ignore_dbg_value: +; CHECK-NEXT: .Lfunc_begin +; CHECK-NEXT: unreachable +declare void @llvm.dbg.value(metadata, i64, metadata, metadata) +define void @ignore_dbg_value() { + call void @llvm.dbg.value(metadata i32 0, i64 0, metadata !7, metadata !9), !dbg !10 + unreachable +} + +; Don't stackify an expression that might use the stack into a return, since we +; might insert a prologue before the return. + +; CHECK-LABEL: no_stackify_past_epilogue: +; CHECK: return ${{[0-9]+}}{{$}} +declare i32 @use_memory(i32*) +define i32 @no_stackify_past_epilogue() { + %x = alloca i32 + %call = call i32 @use_memory(i32* %x) + ret i32 %call +} + +; Stackify a loop induction variable into a loop comparison. + +; CHECK-LABEL: stackify_indvar: +; CHECK: i32.const $push[[L5:.+]]=, 1{{$}} +; CHECK-NEXT: i32.add $push[[L4:.+]]=, $[[R0:.+]], $pop[[L5]]{{$}} +; CHECK-NEXT: tee_local $push[[L3:.+]]=, $[[R0]]=, $pop[[L4]]{{$}} +; CHECK-NEXT: i32.ne $push[[L2:.+]]=, $0, $pop[[L3]]{{$}} +define void @stackify_indvar(i32 %tmp, i32* %v) #0 { +bb: + br label %bb3 + +bb3: ; preds = %bb3, %bb2 + %tmp4 = phi i32 [ %tmp7, %bb3 ], [ 0, %bb ] + %tmp5 = load volatile i32, i32* %v, align 4 + %tmp6 = add nsw i32 %tmp5, %tmp4 + store volatile i32 %tmp6, i32* %v, align 4 + %tmp7 = add nuw nsw i32 %tmp4, 1 + %tmp8 = icmp eq i32 %tmp7, %tmp + br i1 %tmp8, label %bb10, label %bb3 + +bb10: ; preds = %bb9, %bb + ret void +} + +; Don't stackify a call past a __stack_pointer store. + +; CHECK-LABEL: stackpointer_dependency: +; CHECK: call {{.+}}, stackpointer_callee@FUNCTION, +; CHECK: i32.const $push[[L0:.+]]=, 0 +; CHECK-NEXT: i32.store $drop=, __stack_pointer($pop[[L0]]), +declare i32 @stackpointer_callee(i8* readnone, i8* readnone) +declare i8* @llvm.frameaddress(i32) +define i32 @stackpointer_dependency(i8* readnone) { + %2 = tail call i8* @llvm.frameaddress(i32 0) + %3 = tail call i32 @stackpointer_callee(i8* %0, i8* %2) + ret i32 %3 +} + +!llvm.module.flags = !{!0} +!llvm.dbg.cu = !{!1} + +!0 = !{i32 2, !"Debug Info Version", i32 3} +!1 = distinct !DICompileUnit(language: DW_LANG_C99, file: !2, producer: "clang version 3.9.0 (trunk 266005) (llvm/trunk 266105)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !3) +!2 = !DIFile(filename: "test.c", directory: "/") +!3 = !{} +!5 = distinct !DISubprogram(name: "test", scope: !2, file: !2, line: 10, type: !6, isLocal: false, isDefinition: true, scopeLine: 11, flags: DIFlagPrototyped, isOptimized: true, unit: !1, variables: !3) +!6 = !DISubroutineType(types: !3) +!7 = !DILocalVariable(name: "nzcnt", scope: !5, file: !2, line: 15, type: !8) +!8 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!9 = !DIExpression() +!10 = !DILocation(line: 15, column: 6, scope: !5) diff --git a/test/CodeGen/WebAssembly/return-int32.ll b/test/CodeGen/WebAssembly/return-int32.ll index a93a0f6c438b..9e663b969e14 100644 --- a/test/CodeGen/WebAssembly/return-int32.ll +++ b/test/CodeGen/WebAssembly/return-int32.ll @@ -1,10 +1,34 @@ ; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -fast-isel -fast-isel-abort=1 | FileCheck %s target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" ; CHECK-LABEL: return_i32: -; CHECK: return $0{{$}} +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: copy_local $push0=, $0 +; CHECK-NEXT: .endfunc{{$}} define i32 @return_i32(i32 %p) { ret i32 %p } + +; CHECK-LABEL: return_i32_twice: +; CHECK: store +; CHECK-NEXT: i32.const $push[[L0:[^,]+]]=, 1{{$}} +; CHECK-NEXT: return $pop[[L0]]{{$}} +; CHECK: store +; CHECK-NEXT: i32.const $push{{[^,]+}}=, 3{{$}} +; CHECK-NEXT: .endfunc{{$}} +define i32 @return_i32_twice(i32 %a) { + %b = icmp ne i32 %a, 0 + br i1 %b, label %true, label %false + +true: + store i32 0, i32* null + ret i32 1 + +false: + store i32 2, i32* null + ret i32 3 +} diff --git a/test/CodeGen/WebAssembly/return-void.ll b/test/CodeGen/WebAssembly/return-void.ll index 65ff5f325719..c3a600f7838d 100644 --- a/test/CodeGen/WebAssembly/return-void.ll +++ b/test/CodeGen/WebAssembly/return-void.ll @@ -1,10 +1,29 @@ ; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -fast-isel -fast-isel-abort=1 | FileCheck %s target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" ; CHECK-LABEL: return_void: -; CHECK: return{{$}} +; CHECK-NEXT: .endfunc{{$}} define void @return_void() { ret void } + +; CHECK-LABEL: return_void_twice: +; CHECK: store +; CHECK-NEXT: return{{$}} +; CHECK: store +; CHECK-NEXT: .endfunc{{$}} +define void @return_void_twice(i32 %a) { + %b = icmp ne i32 %a, 0 + br i1 %b, label %true, label %false + +true: + store i32 0, i32* null + ret void + +false: + store i32 1, i32* null + ret void +} diff --git a/test/CodeGen/WebAssembly/returned.ll b/test/CodeGen/WebAssembly/returned.ll index 9c892bb3ecea..a277928ae400 100644 --- a/test/CodeGen/WebAssembly/returned.ll +++ b/test/CodeGen/WebAssembly/returned.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test that the "returned" attribute is optimized effectively. @@ -38,7 +38,7 @@ entry: ; CHECK-LABEL: test_constant_arg: ; CHECK-NEXT: i32.const $push0=, global{{$}} -; CHECK-NEXT: {{^}} i32.call $discard=, returns_arg@FUNCTION, $pop0{{$}} +; CHECK-NEXT: {{^}} i32.call $drop=, returns_arg@FUNCTION, $pop0{{$}} ; CHECK-NEXT: return{{$}} @global = external global i32 @addr = global i32* @global diff --git a/test/CodeGen/WebAssembly/select.ll b/test/CodeGen/WebAssembly/select.ll index 416f58cac0d3..06837e4c2368 100644 --- a/test/CodeGen/WebAssembly/select.ll +++ b/test/CodeGen/WebAssembly/select.ll @@ -1,5 +1,5 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s -; RUN: llc < %s -asm-verbose=false -fast-isel | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -fast-isel -fast-isel-abort=1 | FileCheck %s ; Test that wasm select instruction is selected from LLVM select instruction. @@ -9,7 +9,7 @@ target triple = "wasm32-unknown-unknown" ; CHECK-LABEL: select_i32_bool: ; CHECK-NEXT: .param i32, i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.select $push0=, $0, $1, $2{{$}} +; CHECK-NEXT: i32.select $push0=, $1, $2, $0{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @select_i32_bool(i1 zeroext %a, i32 %b, i32 %c) { %cond = select i1 %a, i32 %b, i32 %c @@ -19,7 +19,7 @@ define i32 @select_i32_bool(i1 zeroext %a, i32 %b, i32 %c) { ; CHECK-LABEL: select_i32_eq: ; CHECK-NEXT: .param i32, i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.select $push0=, $0, $2, $1{{$}} +; CHECK-NEXT: i32.select $push0=, $2, $1, $0{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @select_i32_eq(i32 %a, i32 %b, i32 %c) { %cmp = icmp eq i32 %a, 0 @@ -30,7 +30,7 @@ define i32 @select_i32_eq(i32 %a, i32 %b, i32 %c) { ; CHECK-LABEL: select_i32_ne: ; CHECK-NEXT: .param i32, i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.select $push0=, $0, $1, $2{{$}} +; CHECK-NEXT: i32.select $push0=, $1, $2, $0{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @select_i32_ne(i32 %a, i32 %b, i32 %c) { %cmp = icmp ne i32 %a, 0 @@ -41,7 +41,7 @@ define i32 @select_i32_ne(i32 %a, i32 %b, i32 %c) { ; CHECK-LABEL: select_i64_bool: ; CHECK-NEXT: .param i32, i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.select $push0=, $0, $1, $2{{$}} +; CHECK-NEXT: i64.select $push0=, $1, $2, $0{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @select_i64_bool(i1 zeroext %a, i64 %b, i64 %c) { %cond = select i1 %a, i64 %b, i64 %c @@ -51,7 +51,7 @@ define i64 @select_i64_bool(i1 zeroext %a, i64 %b, i64 %c) { ; CHECK-LABEL: select_i64_eq: ; CHECK-NEXT: .param i32, i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.select $push0=, $0, $2, $1{{$}} +; CHECK-NEXT: i64.select $push0=, $2, $1, $0{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @select_i64_eq(i32 %a, i64 %b, i64 %c) { %cmp = icmp eq i32 %a, 0 @@ -62,7 +62,7 @@ define i64 @select_i64_eq(i32 %a, i64 %b, i64 %c) { ; CHECK-LABEL: select_i64_ne: ; CHECK-NEXT: .param i32, i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.select $push0=, $0, $1, $2{{$}} +; CHECK-NEXT: i64.select $push0=, $1, $2, $0{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @select_i64_ne(i32 %a, i64 %b, i64 %c) { %cmp = icmp ne i32 %a, 0 @@ -73,7 +73,7 @@ define i64 @select_i64_ne(i32 %a, i64 %b, i64 %c) { ; CHECK-LABEL: select_f32_bool: ; CHECK-NEXT: .param i32, f32, f32{{$}} ; CHECK-NEXT: .result f32{{$}} -; CHECK-NEXT: f32.select $push0=, $0, $1, $2{{$}} +; CHECK-NEXT: f32.select $push0=, $1, $2, $0{{$}} ; CHECK-NEXT: return $pop0{{$}} define float @select_f32_bool(i1 zeroext %a, float %b, float %c) { %cond = select i1 %a, float %b, float %c @@ -83,7 +83,7 @@ define float @select_f32_bool(i1 zeroext %a, float %b, float %c) { ; CHECK-LABEL: select_f32_eq: ; CHECK-NEXT: .param i32, f32, f32{{$}} ; CHECK-NEXT: .result f32{{$}} -; CHECK-NEXT: f32.select $push0=, $0, $2, $1{{$}} +; CHECK-NEXT: f32.select $push0=, $2, $1, $0{{$}} ; CHECK-NEXT: return $pop0{{$}} define float @select_f32_eq(i32 %a, float %b, float %c) { %cmp = icmp eq i32 %a, 0 @@ -94,7 +94,7 @@ define float @select_f32_eq(i32 %a, float %b, float %c) { ; CHECK-LABEL: select_f32_ne: ; CHECK-NEXT: .param i32, f32, f32{{$}} ; CHECK-NEXT: .result f32{{$}} -; CHECK-NEXT: f32.select $push0=, $0, $1, $2{{$}} +; CHECK-NEXT: f32.select $push0=, $1, $2, $0{{$}} ; CHECK-NEXT: return $pop0{{$}} define float @select_f32_ne(i32 %a, float %b, float %c) { %cmp = icmp ne i32 %a, 0 @@ -105,7 +105,7 @@ define float @select_f32_ne(i32 %a, float %b, float %c) { ; CHECK-LABEL: select_f64_bool: ; CHECK-NEXT: .param i32, f64, f64{{$}} ; CHECK-NEXT: .result f64{{$}} -; CHECK-NEXT: f64.select $push0=, $0, $1, $2{{$}} +; CHECK-NEXT: f64.select $push0=, $1, $2, $0{{$}} ; CHECK-NEXT: return $pop0{{$}} define double @select_f64_bool(i1 zeroext %a, double %b, double %c) { %cond = select i1 %a, double %b, double %c @@ -115,7 +115,7 @@ define double @select_f64_bool(i1 zeroext %a, double %b, double %c) { ; CHECK-LABEL: select_f64_eq: ; CHECK-NEXT: .param i32, f64, f64{{$}} ; CHECK-NEXT: .result f64{{$}} -; CHECK-NEXT: f64.select $push0=, $0, $2, $1{{$}} +; CHECK-NEXT: f64.select $push0=, $2, $1, $0{{$}} ; CHECK-NEXT: return $pop0{{$}} define double @select_f64_eq(i32 %a, double %b, double %c) { %cmp = icmp eq i32 %a, 0 @@ -126,7 +126,7 @@ define double @select_f64_eq(i32 %a, double %b, double %c) { ; CHECK-LABEL: select_f64_ne: ; CHECK-NEXT: .param i32, f64, f64{{$}} ; CHECK-NEXT: .result f64{{$}} -; CHECK-NEXT: f64.select $push0=, $0, $1, $2{{$}} +; CHECK-NEXT: f64.select $push0=, $1, $2, $0{{$}} ; CHECK-NEXT: return $pop0{{$}} define double @select_f64_ne(i32 %a, double %b, double %c) { %cmp = icmp ne i32 %a, 0 diff --git a/test/CodeGen/WebAssembly/signext-zeroext.ll b/test/CodeGen/WebAssembly/signext-zeroext.ll index f6f56363c1af..f9561da5363d 100644 --- a/test/CodeGen/WebAssembly/signext-zeroext.ll +++ b/test/CodeGen/WebAssembly/signext-zeroext.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test zeroext and signext ABI keywords @@ -8,10 +8,10 @@ target triple = "wasm32-unknown-unknown" ; CHECK-LABEL: z2s_func: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: .local i32{{$}} -; CHECK-NEXT: i32.const $[[NUM0:[0-9]+]]=, 24{{$}} -; CHECK-NEXT: i32.shl $push[[NUM2:[0-9]+]]=, $0, $[[NUM0]]{{$}} -; CHECK-NEXT: i32.shr_s $push[[NUM3:[0-9]+]]=, $pop[[NUM2]], $[[NUM0]]{{$}} +; CHECK-NEXT: i32.const $push[[NUM0:[0-9]+]]=, 24{{$}} +; CHECK-NEXT: i32.shl $push[[NUM2:[0-9]+]]=, $0, $pop[[NUM0]]{{$}} +; CHECK-NEXT: i32.const $push[[NUM1:[0-9]+]]=, 24{{$}} +; CHECK-NEXT: i32.shr_s $push[[NUM3:[0-9]+]]=, $pop[[NUM2]], $pop[[NUM1]]{{$}} ; CHECK-NEXT: return $pop[[NUM3]]{{$}} define signext i8 @z2s_func(i8 zeroext %t) { ret i8 %t @@ -44,13 +44,15 @@ define i32 @z2s_call(i32 %t) { ; CHECK-LABEL: s2z_call: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: .local i32{{$}} -; CHECK-NEXT: i32.const $[[NUM0:[0-9]+]]=, 24{{$}} -; CHECK-NEXT: i32.shl $push[[NUM1:[0-9]+]]=, $0, $[[NUM0]]{{$}} -; CHECK-NEXT: i32.shr_s $push[[NUM2:[0-9]+]]=, $pop[[NUM1]], $[[NUM0]]{{$}} +; CHECK-NEXT: i32.const $push[[NUM0:[0-9]+]]=, 24{{$}} +; CHECK-NEXT: i32.shl $push[[NUM1:[0-9]+]]=, $0, $pop[[NUM0]]{{$}} +; CHECK-NEXT: i32.const $push[[NUM6:[0-9]+]]=, 24{{$}} +; CHECK-NEXT: i32.shr_s $push[[NUM2:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM6]]{{$}} ; CHECK-NEXT: call $push[[NUM3:[0-9]]]=, s2z_func@FUNCTION, $pop[[NUM2]]{{$}} -; CHECK-NEXT: i32.shl $push[[NUM4:[0-9]+]]=, $pop[[NUM3]], $[[NUM0]]{{$}} -; CHECK-NEXT: i32.shr_s $push[[NUM5:[0-9]+]]=, $pop[[NUM4]], $[[NUM0]]{{$}} +; CHECK-NEXT: i32.const $push[[NUM7:[0-9]+]]=, 24{{$}} +; CHECK-NEXT: i32.shl $push[[NUM4:[0-9]+]]=, $pop[[NUM3]], $pop[[NUM7]]{{$}} +; CHECK-NEXT: i32.const $push[[NUM8:[0-9]+]]=, 24{{$}} +; CHECK-NEXT: i32.shr_s $push[[NUM5:[0-9]+]]=, $pop[[NUM4]], $pop[[NUM8]]{{$}} ; CHECK-NEXT: return $pop[[NUM5]]{{$}} define i32 @s2z_call(i32 %t) { %s = trunc i32 %t to i8 diff --git a/test/CodeGen/WebAssembly/store-results.ll b/test/CodeGen/WebAssembly/store-results.ll index ae74133fe386..121ee910f853 100644 --- a/test/CodeGen/WebAssembly/store-results.ll +++ b/test/CodeGen/WebAssembly/store-results.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Test that the wasm-store-results pass makes users of stored values use the ; result of store expressions to reduce get_local/set_local traffic. @@ -7,7 +7,7 @@ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" ; CHECK-LABEL: single_block: -; CHECK-NOT: .local +; CHECK-NOT: local ; CHECK: i32.const $push{{[0-9]+}}=, 0{{$}} ; CHECK: i32.store $push[[STORE:[0-9]+]]=, 0($0), $pop{{[0-9]+}}{{$}} ; CHECK: return $pop[[STORE]]{{$}} @@ -26,7 +26,7 @@ entry: @pos = global %class.Vec3 zeroinitializer, align 4 ; CHECK-LABEL: foo: -; CHECK: i32.store $discard=, pos($0), $0{{$}} +; CHECK: i32.store $drop=, pos($pop{{[0-9]+}}), $pop{{[0-9]+}}{{$}} define void @foo() { for.body.i: br label %for.body5.i @@ -44,7 +44,7 @@ for.cond.cleanup4.i: } ; CHECK-LABEL: bar: -; CHECK: i32.store $discard=, pos($0), $0{{$}} +; CHECK: i32.store $drop=, pos($pop{{[0-9]+}}), $pop{{[0-9]+}}{{$}} define void @bar() { for.body.i: br label %for.body5.i @@ -59,3 +59,14 @@ for.body5.i: for.cond.cleanup4.i: ret void } + +; CHECK-LABEL: fi_ret: +; CHECK: i32.store $push0=, +; CHECK: return $pop0{{$}} +define hidden i8* @fi_ret(i8** %addr) { +entry: + %buf = alloca [27 x i8], align 16 + %0 = getelementptr inbounds [27 x i8], [27 x i8]* %buf, i32 0, i32 0 + store i8* %0, i8** %addr + ret i8* %0 +} diff --git a/test/CodeGen/WebAssembly/store-trunc.ll b/test/CodeGen/WebAssembly/store-trunc.ll index d069af1da7bc..75d87ef45b4d 100644 --- a/test/CodeGen/WebAssembly/store-trunc.ll +++ b/test/CodeGen/WebAssembly/store-trunc.ll @@ -6,7 +6,7 @@ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" ; CHECK-LABEL: trunc_i8_i32: -; CHECK: i32.store8 $discard=, 0($0), $1{{$}} +; CHECK: i32.store8 $drop=, 0($0), $1{{$}} define void @trunc_i8_i32(i8 *%p, i32 %v) { %t = trunc i32 %v to i8 store i8 %t, i8* %p @@ -14,7 +14,7 @@ define void @trunc_i8_i32(i8 *%p, i32 %v) { } ; CHECK-LABEL: trunc_i16_i32: -; CHECK: i32.store16 $discard=, 0($0), $1{{$}} +; CHECK: i32.store16 $drop=, 0($0), $1{{$}} define void @trunc_i16_i32(i16 *%p, i32 %v) { %t = trunc i32 %v to i16 store i16 %t, i16* %p @@ -22,7 +22,7 @@ define void @trunc_i16_i32(i16 *%p, i32 %v) { } ; CHECK-LABEL: trunc_i8_i64: -; CHECK: i64.store8 $discard=, 0($0), $1{{$}} +; CHECK: i64.store8 $drop=, 0($0), $1{{$}} define void @trunc_i8_i64(i8 *%p, i64 %v) { %t = trunc i64 %v to i8 store i8 %t, i8* %p @@ -30,7 +30,7 @@ define void @trunc_i8_i64(i8 *%p, i64 %v) { } ; CHECK-LABEL: trunc_i16_i64: -; CHECK: i64.store16 $discard=, 0($0), $1{{$}} +; CHECK: i64.store16 $drop=, 0($0), $1{{$}} define void @trunc_i16_i64(i16 *%p, i64 %v) { %t = trunc i64 %v to i16 store i16 %t, i16* %p @@ -38,7 +38,7 @@ define void @trunc_i16_i64(i16 *%p, i64 %v) { } ; CHECK-LABEL: trunc_i32_i64: -; CHECK: i64.store32 $discard=, 0($0), $1{{$}} +; CHECK: i64.store32 $drop=, 0($0), $1{{$}} define void @trunc_i32_i64(i32 *%p, i64 %v) { %t = trunc i64 %v to i32 store i32 %t, i32* %p diff --git a/test/CodeGen/WebAssembly/store.ll b/test/CodeGen/WebAssembly/store.ll index dc93ebbbadb4..3ff84889a712 100644 --- a/test/CodeGen/WebAssembly/store.ll +++ b/test/CodeGen/WebAssembly/store.ll @@ -1,4 +1,5 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -fast-isel -fast-isel-abort=1 | FileCheck %s ; Test that basic stores are assembled properly. @@ -7,7 +8,7 @@ target triple = "wasm32-unknown-unknown" ; CHECK-LABEL: sti32: ; CHECK-NEXT: .param i32, i32{{$}} -; CHECK-NEXT: i32.store $discard=, 0($0), $1{{$}} +; CHECK-NEXT: i32.store $drop=, 0($0), $1{{$}} ; CHECK-NEXT: return{{$}} define void @sti32(i32 *%p, i32 %v) { store i32 %v, i32* %p @@ -16,7 +17,7 @@ define void @sti32(i32 *%p, i32 %v) { ; CHECK-LABEL: sti64: ; CHECK-NEXT: .param i32, i64{{$}} -; CHECK-NEXT: i64.store $discard=, 0($0), $1{{$}} +; CHECK-NEXT: i64.store $drop=, 0($0), $1{{$}} ; CHECK-NEXT: return{{$}} define void @sti64(i64 *%p, i64 %v) { store i64 %v, i64* %p @@ -25,7 +26,7 @@ define void @sti64(i64 *%p, i64 %v) { ; CHECK-LABEL: stf32: ; CHECK-NEXT: .param i32, f32{{$}} -; CHECK-NEXT: f32.store $discard=, 0($0), $1{{$}} +; CHECK-NEXT: f32.store $drop=, 0($0), $1{{$}} ; CHECK-NEXT: return{{$}} define void @stf32(float *%p, float %v) { store float %v, float* %p @@ -34,7 +35,7 @@ define void @stf32(float *%p, float %v) { ; CHECK-LABEL: stf64: ; CHECK-NEXT: .param i32, f64{{$}} -; CHECK-NEXT: f64.store $discard=, 0($0), $1{{$}} +; CHECK-NEXT: f64.store $drop=, 0($0), $1{{$}} ; CHECK-NEXT: return{{$}} define void @stf64(double *%p, double %v) { store double %v, double* %p diff --git a/test/CodeGen/WebAssembly/switch.ll b/test/CodeGen/WebAssembly/switch.ll index 3df5e7f9cf6f..8355bc8562d6 100644 --- a/test/CodeGen/WebAssembly/switch.ll +++ b/test/CodeGen/WebAssembly/switch.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false -disable-block-placement -verify-machineinstrs | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-block-placement -verify-machineinstrs | FileCheck %s ; Test switch instructions. Block placement is disabled because it reorders ; the blocks in a way that isn't interesting here. @@ -21,7 +21,7 @@ declare void @foo5() ; CHECK: block{{$}} ; CHECK: block{{$}} ; CHECK: block{{$}} -; CHECK: tableswitch {{[^,]*}}, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 4, 5{{$}} +; CHECK: br_table {{[^,]+}}, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 4, 5, 0{{$}} ; CHECK: .LBB0_2: ; CHECK: call foo0@FUNCTION{{$}} ; CHECK: .LBB0_3: @@ -101,7 +101,7 @@ sw.epilog: ; preds = %entry, %sw.bb.5, %s ; CHECK: block{{$}} ; CHECK: block{{$}} ; CHECK: block{{$}} -; CHECK: tableswitch {{[^,]*}}, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 4, 5{{$}} +; CHECK: br_table {{[^,]+}}, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 4, 5, 0{{$}} ; CHECK: .LBB1_2: ; CHECK: call foo0@FUNCTION{{$}} ; CHECK: .LBB1_3: diff --git a/test/CodeGen/WebAssembly/unreachable.ll b/test/CodeGen/WebAssembly/unreachable.ll index 7b23bf3cecfb..77fda44d5ff3 100644 --- a/test/CodeGen/WebAssembly/unreachable.ll +++ b/test/CodeGen/WebAssembly/unreachable.ll @@ -1,5 +1,5 @@ ; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s -; RUN: llc < %s -asm-verbose=false -fast-isel -verify-machineinstrs | FileCheck %s +; RUN: llc < %s -asm-verbose=false -fast-isel -fast-isel-abort=1 -verify-machineinstrs | FileCheck %s ; Test that LLVM unreachable instruction and trap intrinsic are lowered to ; wasm unreachable diff --git a/test/CodeGen/WebAssembly/unused-argument.ll b/test/CodeGen/WebAssembly/unused-argument.ll index 00dea769ee86..ff943b215438 100644 --- a/test/CodeGen/WebAssembly/unused-argument.ll +++ b/test/CodeGen/WebAssembly/unused-argument.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s ; Make sure that argument offsets are correct even if some arguments are unused. @@ -22,7 +22,7 @@ define i32 @unused_second(i32 %x, i32 %y) { } ; CHECK-LABEL: call_something: -; CHECK-NEXT: {{^}} i32.call $discard=, return_something@FUNCTION{{$}} +; CHECK-NEXT: {{^}} i32.call $drop=, return_something@FUNCTION{{$}} ; CHECK-NEXT: return{{$}} declare i32 @return_something() define void @call_something() { diff --git a/test/CodeGen/WebAssembly/userstack.ll b/test/CodeGen/WebAssembly/userstack.ll index cc50192b66db..66ac2cce7079 100644 --- a/test/CodeGen/WebAssembly/userstack.ll +++ b/test/CodeGen/WebAssembly/userstack.ll @@ -1,102 +1,255 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s -; RUN: llc < %s -asm-verbose=false -fast-isel | FileCheck %s - +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" +declare void @ext_func(i64* %ptr) +declare void @ext_func_i32(i32* %ptr) + ; CHECK-LABEL: alloca32: ; Check that there is an extra local for the stack pointer. -; CHECK: .local i32, i32, i32, i32{{$}} -define void @alloca32() { - ; CHECK: i32.const [[L1:.+]]=, __stack_pointer - ; CHECK-NEXT: i32.load [[L1]]=, 0([[L1]]) - ; CHECK-NEXT: i32.const [[L2:.+]]=, 16 - ; CHECK-NEXT: i32.sub [[SP:.+]]=, [[L1]], [[L2]] +; CHECK: .local i32{{$}} +define void @alloca32() noredzone { + ; CHECK: i32.const $push[[L4:.+]]=, 0{{$}} + ; CHECK: i32.const $push[[L1:.+]]=, 0{{$}} + ; CHECK-NEXT: i32.load $push[[L2:.+]]=, __stack_pointer($pop[[L1]]) + ; CHECK-NEXT: i32.const $push[[L3:.+]]=, 16 + ; CHECK-NEXT: i32.sub $push[[L8:.+]]=, $pop[[L2]], $pop[[L3]] + ; CHECK-NEXT: i32.store $push[[L10:.+]]=, __stack_pointer($pop[[L4]]), $pop[[L8]]{{$}} + ; CHECK-NEXT: tee_local $push[[L9:.+]]=, $[[SP:.+]]=, $pop[[L10]]{{$}} %retval = alloca i32 - ; CHECK: i32.const $push[[L3:.+]]=, 0 - ; CHECK: i32.store {{.*}}=, 12([[SP]]), $pop[[L3]] + ; CHECK: i32.const $push[[L0:.+]]=, 0 + ; CHECK: i32.store {{.*}}=, 12($pop[[L9]]), $pop[[L0]] store i32 0, i32* %retval - ; CHECK: i32.const [[L4:.+]]=, 16 - ; CHECK-NEXT: i32.add [[SP]]=, [[SP]], [[L4]] - ; CHECK-NEXT: i32.const [[L5:.+]]=, __stack_pointer - ; CHECK-NEXT: i32.store [[SP]]=, 0([[L5]]), [[SP]] + ; CHECK: i32.const $push[[L6:.+]]=, 0 + ; CHECK-NEXT: i32.const $push[[L5:.+]]=, 16 + ; CHECK-NEXT: i32.add $push[[L7:.+]]=, $[[SP]], $pop[[L5]] + ; CHECK-NEXT: i32.store $drop=, __stack_pointer($pop[[L6]]), $pop[[L7]] ret void } ; CHECK-LABEL: alloca3264: -; CHECK: .local i32, i32, i32, i32{{$}} +; CHECK: .local i32{{$}} define void @alloca3264() { - ; CHECK: i32.const [[L1:.+]]=, __stack_pointer - ; CHECK-NEXT: i32.load [[L1]]=, 0([[L1]]) - ; CHECK-NEXT: i32.const [[L2:.+]]=, 16 - ; CHECK-NEXT: i32.sub [[SP:.+]]=, [[L1]], [[L2]] + ; CHECK: i32.const $push[[L2:.+]]=, 0{{$}} + ; CHECK-NEXT: i32.load $push[[L3:.+]]=, __stack_pointer($pop[[L2]]) + ; CHECK-NEXT: i32.const $push[[L4:.+]]=, 16 + ; CHECK-NEXT: i32.sub $push[[L6:.+]]=, $pop[[L3]], $pop[[L4]] + ; CHECK-NEXT: tee_local $push[[L5:.+]]=, $[[SP:.+]]=, $pop[[L6]] %r1 = alloca i32 %r2 = alloca double - ; CHECK: i32.const $push[[L3:.+]]=, 0 - ; CHECK: i32.store {{.*}}=, 12([[SP]]), $pop[[L3]] + ; CHECK-NEXT: i32.const $push[[L0:.+]]=, 0 + ; CHECK-NEXT: i32.store $drop=, 12($pop[[L5]]), $pop[[L0]] store i32 0, i32* %r1 - ; CHECK: i64.const $push[[L4:.+]]=, 0 - ; CHECK: i64.store {{.*}}=, 0([[SP]]), $pop[[L4]] + ; CHECK-NEXT: i64.const $push[[L1:.+]]=, 0 + ; CHECK-NEXT: i64.store $drop=, 0($[[SP]]), $pop[[L1]] store double 0.0, double* %r2 - ; CHECK: i32.const [[L4:.+]]=, 16 - ; CHECK-NEXT: i32.add [[SP]]=, [[SP]], [[L4]] - ; CHECK-NEXT: i32.const [[L5:.+]]=, __stack_pointer - ; CHECK-NEXT: i32.store [[SP]]=, 0([[L5]]), [[SP]] + ; CHECK-NEXT: return ret void } ; CHECK-LABEL: allocarray: -; CHECK: .local i32, i32, i32, i32, i32, i32{{$}} +; CHECK: .local i32{{$}} define void @allocarray() { - ; CHECK: i32.const [[L1:.+]]=, __stack_pointer - ; CHECK-NEXT: i32.load [[L1]]=, 0([[L1]]) - ; CHECK-NEXT: i32.const [[L2:.+]]=, 32 - ; CHECK-NEXT: i32.sub [[SP:.+]]=, [[L1]], [[L2]] - %r = alloca [5 x i32] - ; CHECK: i32.const $push[[L3:.+]]=, 1 - ; CHECK: i32.store {{.*}}=, 12([[SP]]), $pop[[L3]] - %p = getelementptr [5 x i32], [5 x i32]* %r, i32 0, i32 0 + ; CHECK: i32.const $push[[L7:.+]]=, 0{{$}} + ; CHECK: i32.const $push[[L4:.+]]=, 0{{$}} + ; CHECK-NEXT: i32.load $push[[L5:.+]]=, __stack_pointer($pop[[L4]]) + ; CHECK-NEXT: i32.const $push[[L6:.+]]=, 144{{$}} + ; CHECK-NEXT: i32.sub $push[[L11:.+]]=, $pop[[L5]], $pop[[L6]] + ; CHECK-NEXT: i32.store ${{.+}}=, __stack_pointer($pop[[L7]]), $pop[[L11]] + %r = alloca [33 x i32] + + ; CHECK: i32.const $push{{.+}}=, 24 + ; CHECK-NEXT: i32.add $push[[L3:.+]]=, $[[SP]], $pop{{.+}} + ; CHECK-NEXT: i32.const $push[[L1:.+]]=, 1{{$}} + ; CHECK-NEXT: i32.store $push[[L0:.+]]=, 0($pop[[L3]]), $pop[[L1]]{{$}} + ; CHECK-NEXT: i32.store $drop=, 12(${{.+}}), $pop[[L0]]{{$}} + %p = getelementptr [33 x i32], [33 x i32]* %r, i32 0, i32 0 store i32 1, i32* %p - ; CHECK: i32.const $push[[L4:.+]]=, 4 - ; CHECK: i32.const [[L5:.+]]=, 12 - ; CHECK: i32.add [[L5]]=, [[SP]], [[L5]] - ; CHECK: i32.add $push[[L6:.+]]=, [[L5]], $pop[[L4]] - ; CHECK: i32.store {{.*}}=, 0($pop[[L6]]), ${{.+}} - %p2 = getelementptr [5 x i32], [5 x i32]* %r, i32 0, i32 1 + %p2 = getelementptr [33 x i32], [33 x i32]* %r, i32 0, i32 3 store i32 1, i32* %p2 - ; CHECK: i32.const [[L7:.+]]=, 32 - ; CHECK-NEXT: i32.add [[SP]]=, [[SP]], [[L7]] - ; CHECK-NEXT: i32.const [[L8:.+]]=, __stack_pointer - ; CHECK-NEXT: i32.store [[SP]]=, 0([[L7]]), [[SP]] + + ; CHECK: i32.const $push[[L10:.+]]=, 0{{$}} + ; CHECK-NEXT: i32.const $push[[L8:.+]]=, 144 + ; CHECK-NEXT: i32.add $push[[L19:.+]]=, $[[SP]], $pop[[L8]] + ; CHECK-NEXT: i32.store $drop=, __stack_pointer($pop[[L10]]), $pop[[L9]] + ret void +} + +; CHECK-LABEL: non_mem_use +define void @non_mem_use(i8** %addr) { + ; CHECK: i32.const $push[[L1:.+]]=, 48 + ; CHECK-NEXT: i32.sub $push[[L11:.+]]=, {{.+}}, $pop[[L1]] + ; CHECK-NEXT: i32.store $[[SP:.+]]=, {{.+}}, $pop[[L11]] + %buf = alloca [27 x i8], align 16 + %r = alloca i64 + %r2 = alloca i64 + ; %r is at SP+8 + ; CHECK: tee_local $push[[L12:.+]]=, $[[SP:.+]]=, $pop{{.+}} + ; CHECK: i32.const $push[[OFF:.+]]=, 8 + ; CHECK-NEXT: i32.add $push[[ARG1:.+]]=, $pop[[L12]], $pop[[OFF]] + ; CHECK-NEXT: call ext_func@FUNCTION, $pop[[ARG1]] + call void @ext_func(i64* %r) + ; %r2 is at SP+0, no add needed + ; CHECK-NEXT: call ext_func@FUNCTION, $[[SP]] + call void @ext_func(i64* %r2) + ; Use as a value, but in a store + ; %buf is at SP+16 + ; CHECK: i32.const $push[[OFF:.+]]=, 16 + ; CHECK-NEXT: i32.add $push[[VAL:.+]]=, $[[SP]], $pop[[OFF]] + ; CHECK-NEXT: i32.store {{.*}}=, 0($0), $pop[[VAL]] + %gep = getelementptr inbounds [27 x i8], [27 x i8]* %buf, i32 0, i32 0 + store i8* %gep, i8** %addr ret void } +; CHECK-LABEL: allocarray_inbounds: +; CHECK: .local i32{{$}} define void @allocarray_inbounds() { - ; CHECK: i32.const [[L1:.+]]=, __stack_pointer - ; CHECK-NEXT: i32.load [[L1]]=, 0([[L1]]) - ; CHECK-NEXT: i32.const [[L2:.+]]=, 32 - ; CHECK-NEXT: i32.sub [[SP:.+]]=, [[L1]], [[L2]] + ; CHECK: i32.const $push[[L6:.+]]=, 0{{$}} + ; CHECK: i32.const $push[[L3:.+]]=, 0{{$}} + ; CHECK-NEXT: i32.load $push[[L4:.+]]=, __stack_pointer($pop[[L3]]) + ; CHECK-NEXT: i32.const $push[[L5:.+]]=, 32{{$}} + ; CHECK-NEXT: i32.sub $push[[L10:.+]]=, $pop[[L4]], $pop[[L5]] + ; CHECK-NEXT: i32.store ${{.+}}=, __stack_pointer($pop[[L6]]), $pop[[L10]]{{$}} %r = alloca [5 x i32] ; CHECK: i32.const $push[[L3:.+]]=, 1 - ; CHECK: i32.store {{.*}}=, 12([[SP]]), $pop[[L3]] + ; CHECK-DAG: i32.store $push{{.*}}=, 24(${{.+}}), $pop[[L3]] %p = getelementptr inbounds [5 x i32], [5 x i32]* %r, i32 0, i32 0 store i32 1, i32* %p ; This store should have both the GEP and the FI folded into it. - ; CHECK-NEXT: i32.store {{.*}}=, 16([[SP]]), $pop - %p2 = getelementptr inbounds [5 x i32], [5 x i32]* %r, i32 0, i32 1 + ; CHECK-DAG: i32.store {{.*}}=, 12(${{.+}}), $pop + %p2 = getelementptr inbounds [5 x i32], [5 x i32]* %r, i32 0, i32 3 store i32 1, i32* %p2 - ; CHECK: i32.const [[L7:.+]]=, 32 - ; CHECK-NEXT: i32.add [[SP]]=, [[SP]], [[L7]] - ; CHECK-NEXT: i32.const [[L8:.+]]=, __stack_pointer - ; CHECK-NEXT: i32.store [[SP]]=, 0([[L7]]), [[SP]] + call void @ext_func(i64* null); + ; CHECK: call ext_func + ; CHECK: i32.const $push[[L6:.+]]=, 0{{$}} + ; CHECK-NEXT: i32.const $push[[L5:.+]]=, 32{{$}} + ; CHECK-NEXT: i32.add $push[[L7:.+]]=, ${{.+}}, $pop[[L5]] + ; CHECK-NEXT: i32.store $drop=, __stack_pointer($pop[[L6]]), $pop[[L7]] ret void } +; CHECK-LABEL: dynamic_alloca: define void @dynamic_alloca(i32 %alloc) { - ; TODO: Support frame pointers - ;%r = alloca i32, i32 %alloc - ;store i32 0, i32* %r + ; CHECK: i32.const $push[[L7:.+]]=, 0{{$}} + ; CHECK: i32.const $push[[L1:.+]]=, 0{{$}} + ; CHECK-NEXT: i32.load $push[[L13:.+]]=, __stack_pointer($pop[[L1]]) + ; CHECK-NEXT: tee_local $push[[L12:.+]]=, [[SP:.+]], $pop[[L13]]{{$}} + ; Target independent codegen bumps the stack pointer. + ; CHECK: i32.sub + ; Check that SP is written back to memory after decrement + ; CHECK: i32.store $drop=, __stack_pointer($pop{{.+}}), + %r = alloca i32, i32 %alloc + ; Target-independent codegen also calculates the store addr + ; CHECK: call ext_func_i32@FUNCTION + call void @ext_func_i32(i32* %r) + ; CHECK: i32.const $push[[L3:.+]]=, 0{{$}} + ; CHECK: i32.store $drop=, __stack_pointer($pop[[L3]]), $pop{{.+}} + ret void +} + +; CHECK-LABEL: dynamic_alloca_redzone: +define void @dynamic_alloca_redzone(i32 %alloc) { + ; CHECK: i32.const $push[[L8:.+]]=, 0{{$}} + ; CHECK-NEXT: i32.load $push[[L13:.+]]=, __stack_pointer($pop[[L1]]) + ; CHECK-NEXT: tee_local $push[[L12:.+]]=, [[SP:.+]], $pop[[L13]]{{$}} + ; CHECK-NEXT: copy_local [[FP:.+]]=, $pop[[L12]]{{$}} + ; Target independent codegen bumps the stack pointer + ; CHECK: i32.sub + %r = alloca i32, i32 %alloc + ; CHECK-NEXT: tee_local $push[[L8:.+]]=, $0=, $pop + ; CHECK-NEXT: copy_local $drop=, $pop[[L8]]{{$}} + ; CHECK-NEXT: i32.const $push[[L6:.+]]=, 0{{$}} + ; CHECK-NEXT: i32.store $drop=, 0($0), $pop[[L6]]{{$}} + store i32 0, i32* %r + ; CHECK-NEXT: return + ret void +} + +; CHECK-LABEL: dynamic_static_alloca: +define void @dynamic_static_alloca(i32 %alloc) noredzone { + ; Decrement SP in the prolog by the static amount and writeback to memory. + ; CHECK: i32.const $push[[L7:.+]]=, 0{{$}} + ; CHECK: i32.const $push[[L8:.+]]=, 0{{$}} + ; CHECK: i32.const $push[[L9:.+]]=, 0{{$}} + ; CHECK-NEXT: i32.load $push[[L10:.+]]=, __stack_pointer($pop[[L9]]) + ; CHECK-NEXT: i32.const $push[[L11:.+]]=, 16 + ; CHECK-NEXT: i32.sub $push[[L20:.+]]=, $pop[[L10]], $pop[[L11]] + ; CHECK-NEXT: tee_local $push[[L19:.+]]=, $[[FP:.+]]=, $pop[[L20]] + ; CHECK: i32.store $push[[L0:.+]]=, __stack_pointer($pop{{.+}}), $pop{{.+}} + ; Decrement SP in the body by the dynamic amount. + ; CHECK: i32.sub + ; Writeback to memory. + ; CHECK: i32.store $drop=, __stack_pointer($pop{{.+}}), $pop{{.+}} + %r1 = alloca i32 + %r = alloca i32, i32 %alloc + store i32 0, i32* %r + ; CHEC: i32.store $drop=, 0($pop{{.+}}), $pop{{.+}} ret void } -; TODO: test aligned alloc + +; The use of the alloca in a phi causes a CopyToReg DAG node to be generated, +; which has to have special handling because CopyToReg can't have a FI operand +; CHECK-LABEL: copytoreg_fi: +define void @copytoreg_fi(i1 %cond, i32* %b) { +entry: + ; CHECK: i32.const $push[[L1:.+]]=, 16 + ; CHECK-NEXT: i32.sub $push[[L3:.+]]=, {{.+}}, $pop[[L1]] + %addr = alloca i32 + ; CHECK: i32.const $push[[OFF:.+]]=, 12 + ; CHECK-NEXT: i32.add $push[[ADDR:.+]]=, $pop[[L3]], $pop[[OFF]] + ; CHECK-NEXT: copy_local [[COPY:.+]]=, $pop[[ADDR]] + br label %body +body: + %a = phi i32* [%addr, %entry], [%b, %body] + store i32 1, i32* %a + ; CHECK: i32.store {{.*}}, 0([[COPY]]), + br i1 %cond, label %body, label %exit +exit: + ret void +} + +declare void @use_i8_star(i8*) +declare i8* @llvm.frameaddress(i32) + +; Test __builtin_frame_address(0). +; CHECK-LABEL: frameaddress_0: +; CHECK: i32.const $push[[L0:.+]]=, 0{{$}} +; CHECK-NEXT: i32.load $push[[L3:.+]]=, __stack_pointer($pop[[L0]]) +; CHECK-NEXT: copy_local $push[[L4:.+]]=, $pop[[L3]]{{$}} +; CHECK-NEXT: tee_local $push[[L2:.+]]=, $[[FP:.+]]=, $pop[[L4]]{{$}} +; CHECK-NEXT: call use_i8_star@FUNCTION, $pop[[L2]] +; CHECK-NEXT: i32.const $push[[L1:.+]]=, 0{{$}} +; CHECK-NEXT: i32.store $drop=, __stack_pointer($pop[[L1]]), $[[FP]] +define void @frameaddress_0() { + %t = call i8* @llvm.frameaddress(i32 0) + call void @use_i8_star(i8* %t) + ret void +} + +; Test __builtin_frame_address(1). + +; CHECK-LABEL: frameaddress_1: +; CHECK-NEXT: i32.const $push0=, 0{{$}} +; CHECK-NEXT: call use_i8_star@FUNCTION, $pop0{{$}} +; CHECK-NEXT: return{{$}} +define void @frameaddress_1() { + %t = call i8* @llvm.frameaddress(i32 1) + call void @use_i8_star(i8* %t) + ret void +} + +; Test a stack address passed to an inline asm. +; CHECK-LABEL: inline_asm: +; CHECK: __stack_pointer +; CHECK: #APP +; CHECK-NEXT: # %{{[0-9]+}}{{$}} +; CHECK-NEXT: #NO_APP +define void @inline_asm() { + %tmp = alloca i8 + call void asm sideeffect "# %0", "r"(i8* %tmp) + ret void +} + +; TODO: test over-aligned alloca diff --git a/test/CodeGen/WebAssembly/varargs.ll b/test/CodeGen/WebAssembly/varargs.ll index c12264625c37..483d452624a8 100644 --- a/test/CodeGen/WebAssembly/varargs.ll +++ b/test/CodeGen/WebAssembly/varargs.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -verify-machineinstrs | FileCheck %s ; Test varargs constructs. @@ -8,13 +8,17 @@ target triple = "wasm32-unknown-unknown" ; Test va_start. ; TODO: Test va_start. - -;define void @start(i8** %ap, ...) { -;entry: -; %0 = bitcast i8** %ap to i8* -; call void @llvm.va_start(i8* %0) -; ret void -;} +; CHECK-LABEL: start: +; CHECK-NEXT: .param i32, i32 +; CHECK-NOT: __stack_pointer +define void @start(i8** %ap, ...) { +entry: + %0 = bitcast i8** %ap to i8* +; Store the second argument (the hidden vararg buffer pointer) into ap +; CHECK: i32.store $drop=, 0($0), $1 + call void @llvm.va_start(i8* %0) + ret void +} ; Test va_end. @@ -33,7 +37,7 @@ entry: ; CHECK-LABEL: copy: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: i32.load $push0=, 0($1){{$}} -; CHECK-NEXT: i32.store $discard=, 0($0), $pop0{{$}} +; CHECK-NEXT: i32.store $drop=, 0($0), $pop0{{$}} ; CHECK-NEXT: return{{$}} define void @copy(i8** %ap, i8** %bp) { entry: @@ -49,12 +53,13 @@ entry: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} ; CHECK-NEXT: .local i32{{$}} -; CHECK-NEXT: i32.load $1=, 0($0){{$}} -; CHECK-NEXT: i32.const $push0=, 4{{$}} -; CHECK-NEXT: i32.add $push1=, $1, $pop0{{$}} -; CHECK-NEXT: i32.store $discard=, 0($0), $pop1{{$}} -; CHECK-NEXT: i32.load $push2=, 0($1){{$}} -; CHECK-NEXT: return $pop2{{$}} +; CHECK-NEXT: i32.load $push[[NUM0:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: tee_local $push[[NUM1:[0-9]+]]=, $1=, $pop[[NUM0]]{{$}} +; CHECK-NEXT: i32.const $push[[NUM2:[0-9]+]]=, 4{{$}} +; CHECK-NEXT: i32.add $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}} +; CHECK-NEXT: i32.store $drop=, 0($0), $pop[[NUM3]]{{$}} +; CHECK-NEXT: i32.load $push[[NUM4:[0-9]+]]=, 0($1){{$}} +; CHECK-NEXT: return $pop[[NUM4]]{{$}} define i8 @arg_i8(i8** %ap) { entry: %t = va_arg i8** %ap, i8 @@ -67,16 +72,17 @@ entry: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} ; CHECK-NEXT: .local i32{{$}} -; CHECK-NEXT: i32.load $push0=, 0($0){{$}} -; CHECK-NEXT: i32.const $push1=, 3{{$}} -; CHECK-NEXT: i32.add $push2=, $pop0, $pop1{{$}} -; CHECK-NEXT: i32.const $push3=, -4{{$}} -; CHECK-NEXT: i32.and $1=, $pop2, $pop3{{$}} -; CHECK-NEXT: i32.const $push4=, 4{{$}} -; CHECK-NEXT: i32.add $push5=, $1, $pop4{{$}} -; CHECK-NEXT: i32.store $discard=, 0($0), $pop5{{$}} -; CHECK-NEXT: i32.load $push6=, 0($1){{$}} -; CHECK-NEXT: return $pop6{{$}} +; CHECK-NEXT: i32.load $push[[NUM0:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: i32.const $push[[NUM1:[0-9]+]]=, 3{{$}} +; CHECK-NEXT: i32.add $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM1]]{{$}} +; CHECK-NEXT: i32.const $push[[NUM3:[0-9]+]]=, -4{{$}} +; CHECK-NEXT: i32.and $push[[NUM4:[0-9]+]]=, $pop[[NUM2]], $pop[[NUM3]]{{$}} +; CHECK-NEXT: tee_local $push[[NUM5:[0-9]+]]=, $1=, $pop[[NUM4]]{{$}} +; CHECK-NEXT: i32.const $push[[NUM6:[0-9]+]]=, 4{{$}} +; CHECK-NEXT: i32.add $push[[NUM7:[0-9]+]]=, $pop[[NUM5]], $pop[[NUM6]]{{$}} +; CHECK-NEXT: i32.store $drop=, 0($0), $pop[[NUM7]]{{$}} +; CHECK-NEXT: i32.load $push[[NUM8:[0-9]+]]=, 0($1){{$}} +; CHECK-NEXT: return $pop[[NUM8]]{{$}} define i32 @arg_i32(i8** %ap) { entry: %t = va_arg i8** %ap, i32 @@ -103,21 +109,44 @@ entry: declare void @callee(...) ; CHECK-LABEL: caller_none: -; CHECK-NEXT: call callee@FUNCTION{{$}} +; CHECK-NEXT: i32.const $push0=, 0 +; CHECK-NEXT: call callee@FUNCTION, $pop0 ; CHECK-NEXT: return{{$}} define void @caller_none() { call void (...) @callee() ret void } +; Test a varargs call with some actual arguments. +; Note that the store of 2.0 is converted to an i64 store; this optimization +; is not needed on WebAssembly, but there isn't currently a convenient hook for +; disabling it. + ; CHECK-LABEL: caller_some +; CHECK: i32.store +; CHECK: i64.store define void @caller_some() { - ; TODO: Fix interaction between register coalescer and reg stackifier, - ; or disable coalescer. - ;call void (...) @callee(i32 0, double 2.0) + call void (...) @callee(i32 0, double 2.0) ret void } +; Test a va_start call in a non-entry block +; CHECK-LABEL: startbb: +; CHECK: .param i32, i32, i32 +define void @startbb(i1 %cond, i8** %ap, ...) { +entry: + br i1 %cond, label %bb0, label %bb1 +bb0: + ret void +bb1: + %0 = bitcast i8** %ap to i8* +; Store the second argument (the hidden vararg buffer pointer) into ap +; CHECK: i32.store $drop=, 0($1), $2 + call void @llvm.va_start(i8* %0) + ret void +} + + declare void @llvm.va_start(i8*) declare void @llvm.va_end(i8*) declare void @llvm.va_copy(i8*, i8*) |
