aboutsummaryrefslogtreecommitdiff
path: root/test/CodeGen/WebAssembly
diff options
context:
space:
mode:
Diffstat (limited to 'test/CodeGen/WebAssembly')
-rw-r--r--test/CodeGen/WebAssembly/address-offsets.ll672
-rw-r--r--test/CodeGen/WebAssembly/byval.ll131
-rw-r--r--test/CodeGen/WebAssembly/call.ll5
-rw-r--r--test/CodeGen/WebAssembly/cfg-stackify.ll571
-rw-r--r--test/CodeGen/WebAssembly/comparisons_f32.ll2
-rw-r--r--test/CodeGen/WebAssembly/comparisons_f64.ll2
-rw-r--r--test/CodeGen/WebAssembly/comparisons_i32.ll3
-rw-r--r--test/CodeGen/WebAssembly/comparisons_i64.ll3
-rw-r--r--test/CodeGen/WebAssembly/conv.ll2
-rw-r--r--test/CodeGen/WebAssembly/cpus.ll4
-rw-r--r--test/CodeGen/WebAssembly/dead-vreg.ll2
-rw-r--r--test/CodeGen/WebAssembly/divrem-constant.ll62
-rw-r--r--test/CodeGen/WebAssembly/f32.ll2
-rw-r--r--test/CodeGen/WebAssembly/f64.ll2
-rw-r--r--test/CodeGen/WebAssembly/fast-isel.ll28
-rw-r--r--test/CodeGen/WebAssembly/frem.ll2
-rw-r--r--test/CodeGen/WebAssembly/func.ll5
-rw-r--r--test/CodeGen/WebAssembly/global.ll48
-rw-r--r--test/CodeGen/WebAssembly/i128.ll280
-rw-r--r--test/CodeGen/WebAssembly/i32-load-store-alignment.ll212
-rw-r--r--test/CodeGen/WebAssembly/i32.ll67
-rw-r--r--test/CodeGen/WebAssembly/i64-load-store-alignment.ll325
-rw-r--r--test/CodeGen/WebAssembly/i64.ll67
-rw-r--r--test/CodeGen/WebAssembly/immediates.ll37
-rw-r--r--test/CodeGen/WebAssembly/indirect-import.ll73
-rw-r--r--test/CodeGen/WebAssembly/inline-asm.ll6
-rw-r--r--test/CodeGen/WebAssembly/irreducible-cfg.ll94
-rw-r--r--test/CodeGen/WebAssembly/legalize.ll2
-rw-r--r--test/CodeGen/WebAssembly/load-ext.ll2
-rw-r--r--test/CodeGen/WebAssembly/load-store-i1.ll24
-rw-r--r--test/CodeGen/WebAssembly/load.ll3
-rw-r--r--test/CodeGen/WebAssembly/loop-idiom.ll53
-rw-r--r--test/CodeGen/WebAssembly/mem-intrinsics.ll140
-rw-r--r--test/CodeGen/WebAssembly/memory-addr32.ll12
-rw-r--r--test/CodeGen/WebAssembly/memory-addr64.ll12
-rw-r--r--test/CodeGen/WebAssembly/non-executable-stack.ll9
-rw-r--r--test/CodeGen/WebAssembly/offset-folding.ll23
-rw-r--r--test/CodeGen/WebAssembly/offset.ll88
-rw-r--r--test/CodeGen/WebAssembly/phi.ll2
-rw-r--r--test/CodeGen/WebAssembly/reg-stackify.ll404
-rw-r--r--test/CodeGen/WebAssembly/return-int32.ll26
-rw-r--r--test/CodeGen/WebAssembly/return-void.ll21
-rw-r--r--test/CodeGen/WebAssembly/returned.ll4
-rw-r--r--test/CodeGen/WebAssembly/select.ll28
-rw-r--r--test/CodeGen/WebAssembly/signext-zeroext.ll24
-rw-r--r--test/CodeGen/WebAssembly/store-results.ll19
-rw-r--r--test/CodeGen/WebAssembly/store-trunc.ll10
-rw-r--r--test/CodeGen/WebAssembly/store.ll11
-rw-r--r--test/CodeGen/WebAssembly/switch.ll6
-rw-r--r--test/CodeGen/WebAssembly/unreachable.ll2
-rw-r--r--test/CodeGen/WebAssembly/unused-argument.ll4
-rw-r--r--test/CodeGen/WebAssembly/userstack.ll277
-rw-r--r--test/CodeGen/WebAssembly/varargs.ll87
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*)