summaryrefslogtreecommitdiff
path: root/test/CodeGen/WebAssembly
diff options
context:
space:
mode:
Diffstat (limited to 'test/CodeGen/WebAssembly')
-rw-r--r--test/CodeGen/WebAssembly/call.ll127
-rw-r--r--test/CodeGen/WebAssembly/cfg-stackify.ll1102
-rw-r--r--test/CodeGen/WebAssembly/comparisons_f32.ll181
-rw-r--r--test/CodeGen/WebAssembly/comparisons_f64.ll181
-rw-r--r--test/CodeGen/WebAssembly/comparisons_i32.ll98
-rw-r--r--test/CodeGen/WebAssembly/comparisons_i64.ll98
-rw-r--r--test/CodeGen/WebAssembly/conv.ll255
-rw-r--r--test/CodeGen/WebAssembly/copysign-casts.ll28
-rw-r--r--test/CodeGen/WebAssembly/cpus.ll17
-rw-r--r--test/CodeGen/WebAssembly/dead-vreg.ll51
-rw-r--r--test/CodeGen/WebAssembly/f32.ll154
-rw-r--r--test/CodeGen/WebAssembly/f64.ll154
-rw-r--r--test/CodeGen/WebAssembly/fast-isel.ll20
-rw-r--r--test/CodeGen/WebAssembly/frem.ll26
-rw-r--r--test/CodeGen/WebAssembly/func.ll62
-rw-r--r--test/CodeGen/WebAssembly/global.ll177
-rw-r--r--test/CodeGen/WebAssembly/globl.ll10
-rw-r--r--test/CodeGen/WebAssembly/i32.ll190
-rw-r--r--test/CodeGen/WebAssembly/i64.ll190
-rw-r--r--test/CodeGen/WebAssembly/ident.ll12
-rw-r--r--test/CodeGen/WebAssembly/immediates.ll198
-rw-r--r--test/CodeGen/WebAssembly/inline-asm.ll94
-rw-r--r--test/CodeGen/WebAssembly/legalize.ll62
-rw-r--r--test/CodeGen/WebAssembly/load-ext.ll96
-rw-r--r--test/CodeGen/WebAssembly/load-store-i1.ll68
-rw-r--r--test/CodeGen/WebAssembly/load.ll46
-rw-r--r--test/CodeGen/WebAssembly/loop-idiom.ll53
-rw-r--r--test/CodeGen/WebAssembly/memory-addr32.ll27
-rw-r--r--test/CodeGen/WebAssembly/memory-addr64.ll27
-rw-r--r--test/CodeGen/WebAssembly/offset-folding.ll48
-rw-r--r--test/CodeGen/WebAssembly/offset.ll185
-rw-r--r--test/CodeGen/WebAssembly/phi.ll47
-rw-r--r--test/CodeGen/WebAssembly/reg-stackify.ll126
-rw-r--r--test/CodeGen/WebAssembly/return-int32.ll10
-rw-r--r--test/CodeGen/WebAssembly/return-void.ll10
-rw-r--r--test/CodeGen/WebAssembly/returned.ll49
-rw-r--r--test/CodeGen/WebAssembly/select.ll135
-rw-r--r--test/CodeGen/WebAssembly/signext-zeroext.ll60
-rw-r--r--test/CodeGen/WebAssembly/store-results.ll61
-rw-r--r--test/CodeGen/WebAssembly/store-trunc.ll46
-rw-r--r--test/CodeGen/WebAssembly/store.ll42
-rw-r--r--test/CodeGen/WebAssembly/switch.ll174
-rw-r--r--test/CodeGen/WebAssembly/unreachable.ll34
-rw-r--r--test/CodeGen/WebAssembly/unused-argument.ll31
-rw-r--r--test/CodeGen/WebAssembly/userstack.ll81
-rw-r--r--test/CodeGen/WebAssembly/varargs.ll123
-rw-r--r--test/CodeGen/WebAssembly/vtable.ll171
47 files changed, 5237 insertions, 0 deletions
diff --git a/test/CodeGen/WebAssembly/call.ll b/test/CodeGen/WebAssembly/call.ll
new file mode 100644
index 000000000000..9158ccec0979
--- /dev/null
+++ b/test/CodeGen/WebAssembly/call.ll
@@ -0,0 +1,127 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that basic call operations assemble as expected.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+declare i32 @i32_nullary()
+declare i32 @i32_unary(i32)
+declare i32 @i32_binary(i32, i32)
+declare i64 @i64_nullary()
+declare float @float_nullary()
+declare double @double_nullary()
+declare void @void_nullary()
+
+; CHECK-LABEL: call_i32_nullary:
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: {{^}} i32.call $push[[NUM:[0-9]+]]=, i32_nullary{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @call_i32_nullary() {
+ %r = call i32 @i32_nullary()
+ ret i32 %r
+}
+
+; CHECK-LABEL: call_i64_nullary:
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: {{^}} i64.call $push[[NUM:[0-9]+]]=, i64_nullary{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i64 @call_i64_nullary() {
+ %r = call i64 @i64_nullary()
+ ret i64 %r
+}
+
+; CHECK-LABEL: call_float_nullary:
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: {{^}} f32.call $push[[NUM:[0-9]+]]=, float_nullary{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define float @call_float_nullary() {
+ %r = call float @float_nullary()
+ ret float %r
+}
+
+; CHECK-LABEL: call_double_nullary:
+; CHECK-NEXT: .result f64{{$}}
+; CHECK-NEXT: {{^}} f64.call $push[[NUM:[0-9]+]]=, double_nullary{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define double @call_double_nullary() {
+ %r = call double @double_nullary()
+ ret double %r
+}
+
+; CHECK-LABEL: call_void_nullary:
+; CHECK-NEXT: {{^}} call void_nullary{{$}}
+; CHECK-NEXT: return{{$}}
+define void @call_void_nullary() {
+ call void @void_nullary()
+ ret void
+}
+
+; CHECK-LABEL: call_i32_unary:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: {{^}} i32.call $push[[NUM:[0-9]+]]=, i32_unary, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @call_i32_unary(i32 %a) {
+ %r = call i32 @i32_unary(i32 %a)
+ ret i32 %r
+}
+
+; CHECK-LABEL: call_i32_binary:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: {{^}} i32.call $push[[NUM:[0-9]+]]=, i32_binary, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @call_i32_binary(i32 %a, i32 %b) {
+ %r = call i32 @i32_binary(i32 %a, i32 %b)
+ ret i32 %r
+}
+
+; CHECK-LABEL: call_indirect_void:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: {{^}} call_indirect $0{{$}}
+; CHECK-NEXT: return{{$}}
+define void @call_indirect_void(void ()* %callee) {
+ call void %callee()
+ ret void
+}
+
+; CHECK-LABEL: call_indirect_i32:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: {{^}} i32.call_indirect $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @call_indirect_i32(i32 ()* %callee) {
+ %t = call i32 %callee()
+ ret i32 %t
+}
+
+; CHECK-LABEL: tail_call_void_nullary:
+; CHECK-NEXT: {{^}} call void_nullary{{$}}
+; CHECK-NEXT: return{{$}}
+define void @tail_call_void_nullary() {
+ tail call void @void_nullary()
+ ret void
+}
+
+; CHECK-LABEL: fastcc_tail_call_void_nullary:
+; CHECK-NEXT: {{^}} call void_nullary{{$}}
+; CHECK-NEXT: return{{$}}
+define void @fastcc_tail_call_void_nullary() {
+ tail call fastcc void @void_nullary()
+ ret void
+}
+
+; CHECK-LABEL: coldcc_tail_call_void_nullary:
+; CHECK-NEXT: {{^}} call void_nullary
+; CHECK-NEXT: return{{$}}
+define void @coldcc_tail_call_void_nullary() {
+ tail call coldcc void @void_nullary()
+ ret void
+}
+
+; FIXME test the following:
+; - More argument combinations.
+; - Tail call.
+; - Interesting returns (struct, multiple).
+; - Vararg.
diff --git a/test/CodeGen/WebAssembly/cfg-stackify.ll b/test/CodeGen/WebAssembly/cfg-stackify.ll
new file mode 100644
index 000000000000..71f3551347bf
--- /dev/null
+++ b/test/CodeGen/WebAssembly/cfg-stackify.ll
@@ -0,0 +1,1102 @@
+; 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
+
+; Test the CFG stackifier pass.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+declare void @something()
+
+; Test that loops are made contiguous, even in the presence of split backedges.
+
+; CHECK-LABEL: test0:
+; CHECK: loop
+; CHECK-NOT: br
+; CHECK: i32.add
+; CHECK-NEXT: i32.ge_s
+; CHECK-NEXT: br_if
+; CHECK-NOT: br
+; CHECK: call
+; CHECK: br BB0_1{{$}}
+; CHECK: return{{$}}
+; OPT-LABEL: test0:
+; OPT: loop
+; OPT-NOT: br
+; OPT: i32.add
+; OPT-NEXT: i32.ge_s
+; OPT-NEXT: br_if
+; OPT-NOT: br
+; OPT: call
+; OPT: br BB0_1{{$}}
+; OPT: return{{$}}
+define void @test0(i32 %n) {
+entry:
+ br label %header
+
+header:
+ %i = phi i32 [ 0, %entry ], [ %i.next, %back ]
+ %i.next = add i32 %i, 1
+
+ %c = icmp slt i32 %i.next, %n
+ br i1 %c, label %back, label %exit
+
+exit:
+ ret void
+
+back:
+ call void @something()
+ br label %header
+}
+
+; Same as test0, but the branch condition is reversed.
+
+; CHECK-LABEL: test1:
+; CHECK: loop
+; CHECK-NOT: br
+; CHECK: i32.add
+; CHECK-NEXT: i32.ge_s
+; CHECK-NEXT: br_if
+; CHECK-NOT: br
+; CHECK: call
+; CHECK: br BB1_1{{$}}
+; CHECK: return{{$}}
+; OPT-LABEL: test1:
+; OPT: loop
+; OPT-NOT: br
+; OPT: i32.add
+; OPT-NEXT: i32.ge_s
+; OPT-NEXT: br_if
+; OPT-NOT: br
+; OPT: call
+; OPT: br BB1_1{{$}}
+; OPT: return{{$}}
+define void @test1(i32 %n) {
+entry:
+ br label %header
+
+header:
+ %i = phi i32 [ 0, %entry ], [ %i.next, %back ]
+ %i.next = add i32 %i, 1
+
+ %c = icmp sge i32 %i.next, %n
+ br i1 %c, label %exit, label %back
+
+exit:
+ ret void
+
+back:
+ call void @something()
+ br label %header
+}
+
+; Test that a simple loop is handled as expected.
+
+; CHECK-LABEL: test2:
+; CHECK: block BB2_2{{$}}
+; CHECK: br_if {{[^,]*}}, BB2_2{{$}}
+; CHECK: BB2_1:
+; CHECK: br_if ${{[0-9]+}}, BB2_1{{$}}
+; CHECK: BB2_2:
+; CHECK: return{{$}}
+; OPT-LABEL: test2:
+; OPT: block BB2_2{{$}}
+; OPT: br_if {{[^,]*}}, BB2_2{{$}}
+; OPT: BB2_1:
+; OPT: br_if ${{[0-9]+}}, BB2_1{{$}}
+; OPT: BB2_2:
+; OPT: return{{$}}
+define void @test2(double* nocapture %p, i32 %n) {
+entry:
+ %cmp.4 = icmp sgt i32 %n, 0
+ br i1 %cmp.4, label %for.body.preheader, label %for.end
+
+for.body.preheader:
+ br label %for.body
+
+for.body:
+ %i.05 = phi i32 [ %inc, %for.body ], [ 0, %for.body.preheader ]
+ %arrayidx = getelementptr inbounds double, double* %p, i32 %i.05
+ %0 = load double, double* %arrayidx, align 8
+ %mul = fmul double %0, 3.200000e+00
+ store double %mul, double* %arrayidx, align 8
+ %inc = add nuw nsw i32 %i.05, 1
+ %exitcond = icmp eq i32 %inc, %n
+ br i1 %exitcond, label %for.end.loopexit, label %for.body
+
+for.end.loopexit:
+ br label %for.end
+
+for.end:
+ ret void
+}
+
+; CHECK-LABEL: doublediamond:
+; CHECK: block BB3_5{{$}}
+; CHECK: block BB3_2{{$}}
+; CHECK: br_if $0, BB3_2{{$}}
+; CHECK: block BB3_4{{$}}
+; CHECK: br_if $1, BB3_4{{$}}
+; CHECK: br BB3_5{{$}}
+; CHECK: BB3_4:
+; CHECK: BB3_5:
+; CHECK: return ${{[0-9]+}}{{$}}
+; OPT-LABEL: doublediamond:
+; OPT: block BB3_5{{$}}
+; OPT: block BB3_4{{$}}
+; OPT: br_if {{[^,]*}}, BB3_4{{$}}
+; OPT: block BB3_3{{$}}
+; OPT: br_if {{[^,]*}}, BB3_3{{$}}
+; OPT: br BB3_5{{$}}
+; OPT: BB3_4:
+; OPT: BB3_5:
+; OPT: return ${{[0-9]+}}{{$}}
+define i32 @doublediamond(i32 %a, i32 %b, i32* %p) {
+entry:
+ %c = icmp eq i32 %a, 0
+ %d = icmp eq i32 %b, 0
+ store volatile i32 0, i32* %p
+ br i1 %c, label %true, label %false
+true:
+ store volatile i32 1, i32* %p
+ br label %exit
+false:
+ store volatile i32 2, i32* %p
+ br i1 %d, label %ft, label %ff
+ft:
+ store volatile i32 3, i32* %p
+ br label %exit
+ff:
+ store volatile i32 4, i32* %p
+ br label %exit
+exit:
+ store volatile i32 5, i32* %p
+ ret i32 0
+}
+
+; CHECK-LABEL: triangle:
+; CHECK: block BB4_2{{$}}
+; CHECK: br_if $1, BB4_2{{$}}
+; CHECK: BB4_2:
+; CHECK: return ${{[0-9]+}}{{$}}
+; OPT-LABEL: triangle:
+; OPT: block BB4_2{{$}}
+; OPT: br_if $1, BB4_2{{$}}
+; OPT: BB4_2:
+; OPT: return ${{[0-9]+}}{{$}}
+define i32 @triangle(i32* %p, i32 %a) {
+entry:
+ %c = icmp eq i32 %a, 0
+ store volatile i32 0, i32* %p
+ br i1 %c, label %true, label %exit
+true:
+ store volatile i32 1, i32* %p
+ br label %exit
+exit:
+ store volatile i32 2, i32* %p
+ ret i32 0
+}
+
+; CHECK-LABEL: diamond:
+; CHECK: block BB5_3{{$}}
+; CHECK: block BB5_2{{$}}
+; CHECK: br_if $1, BB5_2{{$}}
+; CHECK: br BB5_3{{$}}
+; CHECK: BB5_2:
+; CHECK: BB5_3:
+; CHECK: return ${{[0-9]+}}{{$}}
+; OPT-LABEL: diamond:
+; OPT: block BB5_3{{$}}
+; OPT: block BB5_2{{$}}
+; OPT: br_if {{[^,]*}}, BB5_2{{$}}
+; OPT: br BB5_3{{$}}
+; OPT: BB5_2:
+; OPT: BB5_3:
+; OPT: return ${{[0-9]+}}{{$}}
+define i32 @diamond(i32* %p, i32 %a) {
+entry:
+ %c = icmp eq i32 %a, 0
+ store volatile i32 0, i32* %p
+ br i1 %c, label %true, label %false
+true:
+ store volatile i32 1, i32* %p
+ br label %exit
+false:
+ store volatile i32 2, i32* %p
+ br label %exit
+exit:
+ store volatile i32 3, i32* %p
+ ret i32 0
+}
+
+; CHECK-LABEL: single_block:
+; CHECK-NOT: br
+; CHECK: return $pop{{[0-9]+}}{{$}}
+; OPT-LABEL: single_block:
+; OPT-NOT: br
+; OPT: return $pop{{[0-9]+}}{{$}}
+define i32 @single_block(i32* %p) {
+entry:
+ store volatile i32 0, i32* %p
+ ret i32 0
+}
+
+; CHECK-LABEL: minimal_loop:
+; CHECK-NOT: br
+; CHECK: BB7_1:
+; CHECK: i32.store $discard=, 0($0), $pop{{[0-9]+}}{{$}}
+; CHECK: br BB7_1{{$}}
+; CHECK: BB7_2:
+; OPT-LABEL: minimal_loop:
+; OPT-NOT: br
+; OPT: BB7_1:
+; OPT: i32.store $discard=, 0($0), $pop{{[0-9]+}}{{$}}
+; OPT: br BB7_1{{$}}
+; OPT: BB7_2:
+define i32 @minimal_loop(i32* %p) {
+entry:
+ store volatile i32 0, i32* %p
+ br label %loop
+loop:
+ store volatile i32 1, i32* %p
+ br label %loop
+}
+
+; CHECK-LABEL: simple_loop:
+; CHECK-NOT: br
+; CHECK: BB8_1:
+; CHECK: loop BB8_2{{$}}
+; CHECK: br_if $pop{{[0-9]+}}, BB8_1{{$}}
+; CHECK: BB8_2:
+; CHECK: return ${{[0-9]+}}{{$}}
+; OPT-LABEL: simple_loop:
+; OPT-NOT: br
+; OPT: BB8_1:
+; OPT: loop BB8_2{{$}}
+; OPT: br_if {{[^,]*}}, BB8_1{{$}}
+; OPT: BB8_2:
+; OPT: return ${{[0-9]+}}{{$}}
+define i32 @simple_loop(i32* %p, i32 %a) {
+entry:
+ %c = icmp eq i32 %a, 0
+ store volatile i32 0, i32* %p
+ br label %loop
+loop:
+ store volatile i32 1, i32* %p
+ br i1 %c, label %loop, label %exit
+exit:
+ store volatile i32 2, i32* %p
+ ret i32 0
+}
+
+; CHECK-LABEL: doubletriangle:
+; CHECK: block BB9_4{{$}}
+; CHECK: br_if $0, BB9_4{{$}}
+; CHECK: block BB9_3{{$}}
+; CHECK: br_if $1, BB9_3{{$}}
+; CHECK: BB9_3:
+; CHECK: BB9_4:
+; CHECK: return ${{[0-9]+}}{{$}}
+; OPT-LABEL: doubletriangle:
+; OPT: block BB9_4{{$}}
+; OPT: br_if $0, BB9_4{{$}}
+; OPT: block BB9_3{{$}}
+; OPT: br_if $1, BB9_3{{$}}
+; OPT: BB9_3:
+; OPT: BB9_4:
+; OPT: return ${{[0-9]+}}{{$}}
+define i32 @doubletriangle(i32 %a, i32 %b, i32* %p) {
+entry:
+ %c = icmp eq i32 %a, 0
+ %d = icmp eq i32 %b, 0
+ store volatile i32 0, i32* %p
+ br i1 %c, label %true, label %exit
+true:
+ store volatile i32 2, i32* %p
+ br i1 %d, label %tt, label %tf
+tt:
+ store volatile i32 3, i32* %p
+ br label %tf
+tf:
+ store volatile i32 4, i32* %p
+ br label %exit
+exit:
+ store volatile i32 5, i32* %p
+ ret i32 0
+}
+
+; CHECK-LABEL: ifelse_earlyexits:
+; CHECK: block BB10_4{{$}}
+; CHECK: block BB10_2{{$}}
+; CHECK: br_if $0, BB10_2{{$}}
+; CHECK: br BB10_4{{$}}
+; CHECK: BB10_2:
+; CHECK: br_if $1, BB10_4{{$}}
+; CHECK: BB10_4:
+; CHECK: return ${{[0-9]+}}{{$}}
+; OPT-LABEL: ifelse_earlyexits:
+; OPT: block BB10_4{{$}}
+; OPT: block BB10_3{{$}}
+; OPT: br_if {{[^,]*}}, BB10_3{{$}}
+; OPT: br_if $1, BB10_4{{$}}
+; OPT: br BB10_4{{$}}
+; OPT: BB10_3:
+; OPT: BB10_4:
+; OPT: return ${{[0-9]+}}{{$}}
+define i32 @ifelse_earlyexits(i32 %a, i32 %b, i32* %p) {
+entry:
+ %c = icmp eq i32 %a, 0
+ %d = icmp eq i32 %b, 0
+ store volatile i32 0, i32* %p
+ br i1 %c, label %true, label %false
+true:
+ store volatile i32 1, i32* %p
+ br label %exit
+false:
+ store volatile i32 2, i32* %p
+ br i1 %d, label %ft, label %exit
+ft:
+ store volatile i32 3, i32* %p
+ br label %exit
+exit:
+ store volatile i32 4, i32* %p
+ ret i32 0
+}
+
+; CHECK-LABEL: doublediamond_in_a_loop:
+; CHECK: BB11_1:
+; CHECK: loop BB11_7{{$}}
+; CHECK: block BB11_6{{$}}
+; CHECK: block BB11_3{{$}}
+; CHECK: br_if $0, BB11_3{{$}}
+; CHECK: br BB11_6{{$}}
+; CHECK: BB11_3:
+; CHECK: block BB11_5{{$}}
+; CHECK: br_if $1, BB11_5{{$}}
+; CHECK: br BB11_6{{$}}
+; CHECK: BB11_5:
+; CHECK: BB11_6:
+; CHECK: br BB11_1{{$}}
+; CHECK: BB11_7:
+; OPT-LABEL: doublediamond_in_a_loop:
+; OPT: BB11_1:
+; OPT: loop BB11_7{{$}}
+; OPT: block BB11_6{{$}}
+; OPT: block BB11_5{{$}}
+; OPT: br_if {{[^,]*}}, BB11_5{{$}}
+; OPT: block BB11_4{{$}}
+; OPT: br_if {{[^,]*}}, BB11_4{{$}}
+; OPT: br BB11_6{{$}}
+; OPT: BB11_4:
+; OPT: br BB11_6{{$}}
+; OPT: BB11_5:
+; OPT: BB11_6:
+; OPT: br BB11_1{{$}}
+; OPT: BB11_7:
+define i32 @doublediamond_in_a_loop(i32 %a, i32 %b, i32* %p) {
+entry:
+ br label %header
+header:
+ %c = icmp eq i32 %a, 0
+ %d = icmp eq i32 %b, 0
+ store volatile i32 0, i32* %p
+ br i1 %c, label %true, label %false
+true:
+ store volatile i32 1, i32* %p
+ br label %exit
+false:
+ store volatile i32 2, i32* %p
+ br i1 %d, label %ft, label %ff
+ft:
+ store volatile i32 3, i32* %p
+ br label %exit
+ff:
+ store volatile i32 4, i32* %p
+ br label %exit
+exit:
+ store volatile i32 5, i32* %p
+ br label %header
+}
+
+; Test that nested loops are handled.
+
+; CHECK-LABEL: test3:
+; CHECK: loop
+; CHECK-NEXT: br_if
+; CHECK-NEXT: BB{{[0-9]+}}_{{[0-9]+}}:
+; CHECK-NEXT: loop
+; OPT-LABEL: test3:
+; OPT: loop
+; OPT-NEXT: br_if
+; OPT-NEXT: BB{{[0-9]+}}_{{[0-9]+}}:
+; OPT-NEXT: loop
+declare void @bar()
+define void @test3(i32 %w) {
+entry:
+ br i1 undef, label %outer.ph, label %exit
+
+outer.ph:
+ br label %outer
+
+outer:
+ %tobool = icmp eq i32 undef, 0
+ br i1 %tobool, label %inner, label %unreachable
+
+unreachable:
+ unreachable
+
+inner:
+ %c = icmp eq i32 undef, %w
+ br i1 %c, label %if.end, label %inner
+
+exit:
+ ret void
+
+if.end:
+ call void @bar()
+ br label %outer
+}
+
+; Test switch lowering and block placement.
+
+; CHECK-LABEL: test4:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK: block BB13_8{{$}}
+; CHECK-NEXT: block BB13_7{{$}}
+; CHECK-NEXT: block BB13_4{{$}}
+; CHECK: br_if $pop{{[0-9]*}}, BB13_4{{$}}
+; CHECK-NEXT: block BB13_3{{$}}
+; CHECK: br_if $pop{{[0-9]*}}, BB13_3{{$}}
+; CHECK: br_if $pop{{[0-9]*}}, BB13_7{{$}}
+; CHECK-NEXT: BB13_3:
+; CHECK-NEXT: return{{$}}
+; CHECK-NEXT: BB13_4:
+; CHECK: br_if $pop{{[0-9]*}}, BB13_8{{$}}
+; CHECK: br_if $pop{{[0-9]*}}, BB13_7{{$}}
+; CHECK-NEXT: return{{$}}
+; CHECK-NEXT: BB13_7:
+; CHECK-NEXT: return{{$}}
+; CHECK-NEXT: BB13_8:
+; CHECK-NEXT: return{{$}}
+; OPT-LABEL: test4:
+; OPT-NEXT: .param i32{{$}}
+; OPT: block BB13_8{{$}}
+; OPT-NEXT: block BB13_7{{$}}
+; OPT-NEXT: block BB13_4{{$}}
+; OPT: br_if $pop{{[0-9]*}}, BB13_4{{$}}
+; OPT-NEXT: block BB13_3{{$}}
+; OPT: br_if $pop{{[0-9]*}}, BB13_3{{$}}
+; OPT: br_if $pop{{[0-9]*}}, BB13_7{{$}}
+; OPT-NEXT: BB13_3:
+; OPT-NEXT: return{{$}}
+; OPT-NEXT: BB13_4:
+; OPT: br_if $pop{{[0-9]*}}, BB13_8{{$}}
+; OPT: br_if $pop{{[0-9]*}}, BB13_7{{$}}
+; OPT-NEXT: return{{$}}
+; OPT-NEXT: BB13_7:
+; OPT-NEXT: return{{$}}
+; OPT-NEXT: BB13_8:
+; OPT-NEXT: return{{$}}
+define void @test4(i32 %t) {
+entry:
+ switch i32 %t, label %default [
+ i32 0, label %bb2
+ i32 2, label %bb2
+ i32 4, label %bb1
+ i32 622, label %bb0
+ ]
+
+bb0:
+ ret void
+
+bb1:
+ ret void
+
+bb2:
+ ret void
+
+default:
+ ret void
+}
+
+; Test a case where the BLOCK needs to be placed before the LOOP in the
+; same basic block.
+
+; CHECK-LABEL: test5:
+; CHECK: BB14_1:
+; CHECK-NEXT: block BB14_4{{$}}
+; CHECK-NEXT: loop BB14_3{{$}}
+; CHECK: br_if {{[^,]*}}, BB14_4{{$}}
+; CHECK: br_if {{[^,]*}}, BB14_1{{$}}
+; CHECK-NEXT: BB14_3:
+; CHECK: return{{$}}
+; CHECK-NEXT: BB14_4:
+; CHECK: return{{$}}
+; OPT-LABEL: test5:
+; OPT: BB14_1:
+; OPT-NEXT: block BB14_4{{$}}
+; OPT-NEXT: loop BB14_3{{$}}
+; OPT: br_if {{[^,]*}}, BB14_4{{$}}
+; OPT: br_if {{[^,]*}}, BB14_1{{$}}
+; OPT-NEXT: BB14_3:
+; OPT: return{{$}}
+; OPT-NEXT: BB14_4:
+; OPT: return{{$}}
+define void @test5(i1 %p, i1 %q) {
+entry:
+ br label %header
+
+header:
+ store volatile i32 0, i32* null
+ br i1 %p, label %more, label %alt
+
+more:
+ store volatile i32 1, i32* null
+ br i1 %q, label %header, label %return
+
+alt:
+ store volatile i32 2, i32* null
+ ret void
+
+return:
+ store volatile i32 3, i32* null
+ ret void
+}
+
+; Test an interesting case of a loop with multiple exits, which
+; aren't to layout successors of the loop, and one of which is to a successors
+; which has another predecessor.
+
+; CHECK-LABEL: test6:
+; CHECK: BB15_1:
+; CHECK-NEXT: block BB15_6{{$}}
+; CHECK-NEXT: block BB15_5{{$}}
+; CHECK-NEXT: loop BB15_4{{$}}
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB15_6{{$}}
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB15_5{{$}}
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB15_1{{$}}
+; CHECK-NEXT: BB15_4:
+; CHECK-NOT: block
+; CHECK: return{{$}}
+; CHECK-NEXT: BB15_5:
+; CHECK-NOT: block
+; CHECK: BB15_6:
+; CHECK-NOT: block
+; CHECK: return{{$}}
+; OPT-LABEL: test6:
+; OPT: BB15_1:
+; OPT-NEXT: block BB15_6{{$}}
+; OPT-NEXT: block BB15_5{{$}}
+; OPT-NEXT: loop BB15_4{{$}}
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB15_6{{$}}
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB15_5{{$}}
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB15_1{{$}}
+; OPT-NEXT: BB15_4:
+; OPT-NOT: block
+; OPT: return{{$}}
+; OPT-NEXT: BB15_5:
+; OPT-NOT: block
+; OPT: BB15_6:
+; OPT-NOT: block
+; OPT: return{{$}}
+define void @test6(i1 %p, i1 %q) {
+entry:
+ br label %header
+
+header:
+ store volatile i32 0, i32* null
+ br i1 %p, label %more, label %second
+
+more:
+ store volatile i32 1, i32* null
+ br i1 %q, label %evenmore, label %first
+
+evenmore:
+ store volatile i32 1, i32* null
+ br i1 %q, label %header, label %return
+
+return:
+ store volatile i32 2, i32* null
+ ret void
+
+first:
+ store volatile i32 3, i32* null
+ br label %second
+
+second:
+ store volatile i32 4, i32* null
+ ret void
+}
+
+; Test a case where there are multiple backedges and multiple loop exits
+; that end in unreachable.
+
+; CHECK-LABEL: test7:
+; CHECK: BB16_1:
+; CHECK-NEXT: loop BB16_5{{$}}
+; CHECK-NOT: block
+; CHECK: block BB16_4{{$}}
+; CHECK: br_if {{[^,]*}}, BB16_4{{$}}
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB16_1{{$}}
+; CHECK-NOT: block
+; CHECK: unreachable
+; CHECK_NEXT: BB16_4:
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB16_1{{$}}
+; CHECK-NEXT: BB16_5:
+; CHECK-NOT: block
+; CHECK: unreachable
+; OPT-LABEL: test7:
+; OPT: BB16_1:
+; OPT-NEXT: loop BB16_5{{$}}
+; OPT-NOT: block
+; OPT: block BB16_4{{$}}
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB16_4{{$}}
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB16_1{{$}}
+; OPT-NOT: block
+; OPT: unreachable
+; OPT_NEXT: BB16_4:
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB16_1{{$}}
+; OPT-NEXT: BB16_5:
+; OPT-NOT: block
+; OPT: unreachable
+define void @test7(i1 %tobool2, i1 %tobool9) {
+entry:
+ store volatile i32 0, i32* null
+ br label %loop
+
+loop:
+ store volatile i32 1, i32* null
+ br i1 %tobool2, label %l1, label %l0
+
+l0:
+ store volatile i32 2, i32* null
+ br i1 %tobool9, label %loop, label %u0
+
+l1:
+ store volatile i32 3, i32* null
+ br i1 %tobool9, label %loop, label %u1
+
+u0:
+ store volatile i32 4, i32* null
+ unreachable
+
+u1:
+ store volatile i32 5, i32* null
+ unreachable
+}
+
+; Test an interesting case using nested loops and switches.
+
+; CHECK-LABEL: test8:
+; CHECK: BB17_1:
+; CHECK-NEXT: loop BB17_4{{$}}
+; CHECK-NEXT: block BB17_3{{$}}
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB17_3{{$}}
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB17_1{{$}}
+; CHECK-NEXT: BB17_3:
+; CHECK-NEXT: loop BB17_4{{$}}
+; CHECK-NEXT: br_if {{[^,]*}}, BB17_3{{$}}
+; CHECK-NEXT: br BB17_1{{$}}
+; CHECK-NEXT: BB17_4:
+; OPT-LABEL: test8:
+; OPT: BB17_1:
+; OPT-NEXT: loop BB17_4{{$}}
+; OPT-NEXT: block BB17_3{{$}}
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB17_3{{$}}
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB17_1{{$}}
+; OPT-NEXT: BB17_3:
+; OPT-NEXT: loop BB17_4{{$}}
+; OPT-NEXT: br_if {{[^,]*}}, BB17_3{{$}}
+; OPT-NEXT: br BB17_1{{$}}
+; OPT-NEXT: BB17_4:
+define i32 @test8() {
+bb:
+ br label %bb1
+
+bb1:
+ br i1 undef, label %bb2, label %bb3
+
+bb2:
+ switch i8 undef, label %bb1 [
+ i8 44, label %bb2
+ ]
+
+bb3:
+ switch i8 undef, label %bb1 [
+ i8 44, label %bb2
+ ]
+}
+
+; Test an interesting case using nested loops that share a bottom block.
+
+; CHECK-LABEL: test9:
+; CHECK: BB18_1:
+; CHECK-NEXT: loop BB18_5{{$}}
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB18_5{{$}}
+; CHECK-NEXT: BB18_2:
+; CHECK-NEXT: loop BB18_5{{$}}
+; CHECK-NOT: block
+; CHECK: block BB18_4{{$}}
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB18_4{{$}}
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB18_2{{$}}
+; CHECK-NEXT: br BB18_1{{$}}
+; CHECK-NEXT: BB18_4:
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB18_2{{$}}
+; CHECK-NEXT: br BB18_1{{$}}
+; CHECK-NEXT: BB18_5:
+; CHECK-NOT: block
+; CHECK: return{{$}}
+; OPT-LABEL: test9:
+; OPT: BB18_1:
+; OPT-NEXT: loop BB18_5{{$}}
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB18_5{{$}}
+; OPT-NEXT: BB18_2:
+; OPT-NEXT: loop BB18_5{{$}}
+; OPT-NOT: block
+; OPT: block BB18_4{{$}}
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB18_4{{$}}
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB18_2{{$}}
+; OPT-NEXT: br BB18_1{{$}}
+; OPT-NEXT: BB18_4:
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB18_2{{$}}
+; OPT-NEXT: br BB18_1{{$}}
+; OPT-NEXT: BB18_5:
+; OPT-NOT: block
+; OPT: return{{$}}
+declare i1 @a()
+define void @test9() {
+entry:
+ store volatile i32 0, i32* null
+ br label %header
+
+header:
+ store volatile i32 1, i32* null
+ %call4 = call i1 @a()
+ br i1 %call4, label %header2, label %end
+
+header2:
+ store volatile i32 2, i32* null
+ %call = call i1 @a()
+ br i1 %call, label %if.then, label %if.else
+
+if.then:
+ store volatile i32 3, i32* null
+ %call3 = call i1 @a()
+ br i1 %call3, label %header2, label %header
+
+if.else:
+ store volatile i32 4, i32* null
+ %call2 = call i1 @a()
+ br i1 %call2, label %header2, label %header
+
+end:
+ store volatile i32 5, i32* null
+ ret void
+}
+
+; Test an interesting case involving nested loops sharing a loop bottom,
+; and loop exits to a block with unreachable.
+
+; CHECK-LABEL: test10:
+; CHECK: BB19_1:
+; CHECK-NEXT: loop BB19_7{{$}}
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB19_1{{$}}
+; CHECK-NEXT: BB19_2:
+; CHECK-NEXT: block BB19_6{{$}}
+; CHECK-NEXT: loop BB19_5{{$}}
+; CHECK-NOT: block
+; CHECK: BB19_3:
+; CHECK-NEXT: loop BB19_5{{$}}
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB19_1{{$}}
+; CHECK-NOT: block
+; CHECK: tableswitch {{[^,]*}}, BB19_3, BB19_3, BB19_5, BB19_1, BB19_2, BB19_6{{$}}
+; CHECK-NEXT: BB19_5:
+; CHECK-NEXT: return{{$}}
+; CHECK-NEXT: BB19_6:
+; CHECK-NOT: block
+; CHECK: br BB19_1{{$}}
+; CHECK-NEXT: BB19_7:
+; OPT-LABEL: test10:
+; OPT: BB19_1:
+; OPT-NEXT: loop BB19_7{{$}}
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB19_1{{$}}
+; OPT-NEXT: BB19_2:
+; OPT-NEXT: block BB19_6{{$}}
+; OPT-NEXT: loop BB19_5{{$}}
+; OPT-NOT: block
+; OPT: BB19_3:
+; OPT-NEXT: loop BB19_5{{$}}
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB19_1{{$}}
+; OPT-NOT: block
+; OPT: tableswitch {{[^,]*}}, BB19_3, BB19_3, BB19_5, BB19_1, BB19_2, BB19_6{{$}}
+; OPT-NEXT: BB19_5:
+; OPT-NEXT: return{{$}}
+; OPT-NEXT: BB19_6:
+; OPT-NOT: block
+; OPT: br BB19_1{{$}}
+; OPT-NEXT: BB19_7:
+define void @test10() {
+bb0:
+ br label %bb1
+
+bb1:
+ %tmp = phi i32 [ 2, %bb0 ], [ 3, %bb3 ]
+ %tmp3 = phi i32 [ undef, %bb0 ], [ %tmp11, %bb3 ]
+ %tmp4 = icmp eq i32 %tmp3, 0
+ br i1 %tmp4, label %bb4, label %bb2
+
+bb2:
+ br label %bb3
+
+bb3:
+ %tmp11 = phi i32 [ 1, %bb5 ], [ 0, %bb2 ]
+ br label %bb1
+
+bb4:
+ %tmp6 = phi i32 [ %tmp9, %bb5 ], [ 4, %bb1 ]
+ %tmp7 = phi i32 [ %tmp6, %bb5 ], [ %tmp, %bb1 ]
+ br label %bb5
+
+bb5:
+ %tmp9 = phi i32 [ %tmp6, %bb5 ], [ %tmp7, %bb4 ]
+ switch i32 %tmp9, label %bb2 [
+ i32 0, label %bb5
+ i32 1, label %bb6
+ i32 3, label %bb4
+ i32 4, label %bb3
+ ]
+
+bb6:
+ ret void
+}
+
+; Test a CFG DAG with interesting merging.
+
+; CHECK-LABEL: test11:
+; CHECK: block BB20_8{{$}}
+; CHECK-NEXT: block BB20_7{{$}}
+; CHECK-NEXT: block BB20_6{{$}}
+; CHECK-NEXT: block BB20_4{{$}}
+; CHECK-NEXT: br_if {{[^,]*}}, BB20_4{{$}}
+; CHECK-NOT: block
+; CHECK: block BB20_3{{$}}
+; CHECK: br_if {{[^,]*}}, BB20_3{{$}}
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB20_6{{$}}
+; CHECK-NEXT: BB20_3:
+; CHECK-NOT: block
+; CHECK: return{{$}}
+; CHECK-NEXT: BB20_4:
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB20_8{{$}}
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB20_7{{$}}
+; CHECK-NEXT: BB20_6:
+; CHECK-NOT: block
+; CHECK: return{{$}}
+; CHECK-NEXT: BB20_7:
+; CHECK-NOT: block
+; CHECK: return{{$}}
+; CHECK-NEXT: BB20_8:
+; CHECK-NOT: block
+; CHECK: return{{$}}
+; OPT-LABEL: test11:
+; OPT: block BB20_8{{$}}
+; OPT-NEXT: block BB20_4{{$}}
+; OPT-NEXT: br_if $0, BB20_4{{$}}
+; OPT-NOT: block
+; OPT: block BB20_3{{$}}
+; OPT: br_if $0, BB20_3{{$}}
+; OPT-NOT: block
+; OPT: br_if $0, BB20_8{{$}}
+; OPT-NEXT: BB20_3:
+; OPT-NOT: block
+; OPT: return{{$}}
+; OPT-NEXT: BB20_4:
+; OPT-NOT: block
+; OPT: block BB20_6{{$}}
+; OPT-NOT: block
+; OPT: br_if $pop9, BB20_6{{$}}
+; OPT-NOT: block
+; OPT: return{{$}}
+; OPT-NEXT: BB20_6:
+; OPT-NOT: block
+; OPT: br_if $0, BB20_8{{$}}
+; OPT-NOT: block
+; OPT: return{{$}}
+; OPT-NEXT: BB20_8:
+; OPT-NOT: block
+; OPT: return{{$}}
+define void @test11() {
+bb0:
+ store volatile i32 0, i32* null
+ br i1 undef, label %bb1, label %bb4
+bb1:
+ store volatile i32 1, i32* null
+ br i1 undef, label %bb3, label %bb2
+bb2:
+ store volatile i32 2, i32* null
+ br i1 undef, label %bb3, label %bb7
+bb3:
+ store volatile i32 3, i32* null
+ ret void
+bb4:
+ store volatile i32 4, i32* null
+ br i1 undef, label %bb8, label %bb5
+bb5:
+ store volatile i32 5, i32* null
+ br i1 undef, label %bb6, label %bb7
+bb6:
+ store volatile i32 6, i32* null
+ ret void
+bb7:
+ store volatile i32 7, i32* null
+ ret void
+bb8:
+ store volatile i32 8, i32* null
+ ret void
+}
+
+; CHECK-LABEL: test12:
+; CHECK: BB21_1:
+; CHECK-NEXT: loop BB21_8{{$}}
+; CHECK-NOT: block
+; CHECK: block BB21_7{{$}}
+; CHECK-NEXT: block BB21_6{{$}}
+; CHECK-NEXT: block BB21_4{{$}}
+; CHECK: br_if {{[^,]*}}, BB21_4{{$}}
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB21_7{{$}}
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB21_7{{$}}
+; CHECK-NEXT: br BB21_6{{$}}
+; CHECK-NEXT: BB21_4:
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB21_7{{$}}
+; CHECK-NOT: block
+; CHECK: br_if {{[^,]*}}, BB21_7{{$}}
+; CHECK-NEXT: BB21_6:
+; CHECK-NEXT: return{{$}}
+; CHECK-NEXT: BB21_7:
+; CHECK-NOT: block
+; CHECK: br BB21_1{{$}}
+; CHECK-NEXT: BB21_8:
+; OPT-LABEL: test12:
+; OPT: BB21_1:
+; OPT-NEXT: loop BB21_8{{$}}
+; OPT-NOT: block
+; OPT: block BB21_7{{$}}
+; OPT-NEXT: block BB21_6{{$}}
+; OPT-NEXT: block BB21_4{{$}}
+; OPT: br_if {{[^,]*}}, BB21_4{{$}}
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB21_7{{$}}
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB21_7{{$}}
+; OPT-NEXT: br BB21_6{{$}}
+; OPT-NEXT: BB21_4:
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB21_7{{$}}
+; OPT-NOT: block
+; OPT: br_if {{[^,]*}}, BB21_7{{$}}
+; OPT-NEXT: BB21_6:
+; OPT-NEXT: return{{$}}
+; OPT-NEXT: BB21_7:
+; OPT-NOT: block
+; OPT: br BB21_1{{$}}
+; OPT-NEXT: BB21_8:
+define void @test12(i8* %arg) {
+bb:
+ br label %bb1
+
+bb1:
+ %tmp = phi i32 [ 0, %bb ], [ %tmp5, %bb4 ]
+ %tmp2 = getelementptr i8, i8* %arg, i32 %tmp
+ %tmp3 = load i8, i8* %tmp2
+ switch i8 %tmp3, label %bb7 [
+ i8 42, label %bb4
+ i8 76, label %bb4
+ i8 108, label %bb4
+ i8 104, label %bb4
+ ]
+
+bb4:
+ %tmp5 = add i32 %tmp, 1
+ br label %bb1
+
+bb7:
+ ret void
+}
+
+; A block can be "branched to" from another even if it is also reachable via
+; fallthrough from the other. This would normally be optimized away, so use
+; optnone to disable optimizations to test this case.
+
+; CHECK-LABEL: test13:
+; CHECK-NEXT: .local i32{{$}}
+; CHECK: block BB22_2{{$}}
+; CHECK: br_if $pop4, BB22_2{{$}}
+; CHECK-NEXT: return{{$}}
+; CHECK-NEXT: BB22_2:
+; CHECK: block BB22_4{{$}}
+; CHECK-NEXT: br_if $0, BB22_4{{$}}
+; CHECK: BB22_4:
+; CHECK: block BB22_5{{$}}
+; CHECK: br_if $pop6, BB22_5{{$}}
+; CHECK-NEXT: BB22_5:
+; CHECK-NEXT: unreachable{{$}}
+; OPT-LABEL: test13:
+; OPT-NEXT: .local i32{{$}}
+; OPT: block BB22_2{{$}}
+; OPT: br_if $pop4, BB22_2{{$}}
+; OPT-NEXT: return{{$}}
+; OPT-NEXT: BB22_2:
+; OPT: block BB22_4{{$}}
+; OPT-NEXT: br_if $0, BB22_4{{$}}
+; OPT: BB22_4:
+; OPT: block BB22_5{{$}}
+; OPT: br_if $pop6, BB22_5{{$}}
+; OPT-NEXT: BB22_5:
+; OPT-NEXT: unreachable{{$}}
+define void @test13() noinline optnone {
+bb:
+ br i1 undef, label %bb5, label %bb2
+bb1:
+ unreachable
+bb2:
+ br i1 undef, label %bb3, label %bb4
+bb3:
+ br label %bb4
+bb4:
+ %tmp = phi i1 [ false, %bb2 ], [ false, %bb3 ]
+ br i1 %tmp, label %bb1, label %bb1
+bb5:
+ ret void
+}
diff --git a/test/CodeGen/WebAssembly/comparisons_f32.ll b/test/CodeGen/WebAssembly/comparisons_f32.ll
new file mode 100644
index 000000000000..6df37ea1c6dd
--- /dev/null
+++ b/test/CodeGen/WebAssembly/comparisons_f32.ll
@@ -0,0 +1,181 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that basic 32-bit floating-point comparison operations assemble as
+; expected.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: ord_f32:
+; CHECK-NEXT: .param f32, f32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f32.eq $push[[NUM0:[0-9]+]]=, $0, $0{{$}}
+; CHECK-NEXT: f32.eq $push[[NUM1:[0-9]+]]=, $1, $1{{$}}
+; CHECK-NEXT: i32.and $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM1]]{{$}}
+; CHECK-NEXT: return $pop[[NUM2]]{{$}}
+define i32 @ord_f32(float %x, float %y) {
+ %a = fcmp ord float %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: uno_f32:
+; CHECK-NEXT: .param f32, f32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f32.ne $push[[NUM0:[0-9]+]]=, $0, $0{{$}}
+; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $1, $1{{$}}
+; CHECK-NEXT: i32.or $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM1]]{{$}}
+; CHECK-NEXT: return $pop[[NUM2]]{{$}}
+define i32 @uno_f32(float %x, float %y) {
+ %a = fcmp uno float %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: oeq_f32:
+; CHECK-NEXT: .param f32, f32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f32.eq $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @oeq_f32(float %x, float %y) {
+ %a = fcmp oeq float %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: une_f32:
+; CHECK: f32.ne $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @une_f32(float %x, float %y) {
+ %a = fcmp une float %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: olt_f32:
+; CHECK: f32.lt $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @olt_f32(float %x, float %y) {
+ %a = fcmp olt float %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ole_f32:
+; CHECK: f32.le $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @ole_f32(float %x, float %y) {
+ %a = fcmp ole float %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ogt_f32:
+; CHECK: f32.gt $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @ogt_f32(float %x, float %y) {
+ %a = fcmp ogt float %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: oge_f32:
+; CHECK: f32.ge $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @oge_f32(float %x, float %y) {
+ %a = fcmp oge float %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; Expanded comparisons, which also check for NaN.
+
+; CHECK-LABEL: ueq_f32:
+; CHECK-NEXT: .param f32, f32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f32.eq $push[[NUM0:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}}
+; CHECK-NEXT: f32.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}}
+; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}}
+; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}}
+; CHECK-NEXT: return $pop[[NUM4]]{{$}}
+define i32 @ueq_f32(float %x, float %y) {
+ %a = fcmp ueq float %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: one_f32:
+; CHECK-NEXT: .param f32, f32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f32.ne $push[[NUM0:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: f32.eq $push[[NUM1:[0-9]+]]=, $0, $0{{$}}
+; CHECK-NEXT: f32.eq $push[[NUM2:[0-9]+]]=, $1, $1{{$}}
+; CHECK-NEXT: i32.and $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}}
+; CHECK-NEXT: i32.and $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}}
+; CHECK-NEXT: return $pop[[NUM4]]
+define i32 @one_f32(float %x, float %y) {
+ %a = fcmp one float %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ult_f32:
+; CHECK-NEXT: .param f32, f32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f32.lt $push[[NUM0:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}}
+; CHECK-NEXT: f32.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}}
+; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}}
+; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}}
+; CHECK-NEXT: return $pop[[NUM4]]{{$}}
+define i32 @ult_f32(float %x, float %y) {
+ %a = fcmp ult float %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ule_f32:
+; CHECK-NEXT: .param f32, f32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f32.le $push[[NUM0:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}}
+; CHECK-NEXT: f32.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}}
+; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}}
+; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}}
+; CHECK-NEXT: return $pop[[NUM4]]{{$}}
+define i32 @ule_f32(float %x, float %y) {
+ %a = fcmp ule float %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ugt_f32:
+; CHECK-NEXT: .param f32, f32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f32.gt $push[[NUM0:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}}
+; CHECK-NEXT: f32.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}}
+; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}}
+; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}}
+; CHECK-NEXT: return $pop[[NUM4]]{{$}}
+define i32 @ugt_f32(float %x, float %y) {
+ %a = fcmp ugt float %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: uge_f32:
+; CHECK-NEXT: .param f32, f32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f32.ge $push[[NUM0:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}}
+; CHECK-NEXT: f32.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}}
+; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}}
+; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}}
+; CHECK-NEXT: return $pop[[NUM4]]{{$}}
+define i32 @uge_f32(float %x, float %y) {
+ %a = fcmp uge float %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
diff --git a/test/CodeGen/WebAssembly/comparisons_f64.ll b/test/CodeGen/WebAssembly/comparisons_f64.ll
new file mode 100644
index 000000000000..f5acc64b667c
--- /dev/null
+++ b/test/CodeGen/WebAssembly/comparisons_f64.ll
@@ -0,0 +1,181 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that basic 64-bit floating-point comparison operations assemble as
+; expected.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: ord_f64:
+; CHECK-NEXT: .param f64, f64{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f64.eq $push[[NUM0:[0-9]+]]=, $0, $0{{$}}
+; CHECK-NEXT: f64.eq $push[[NUM1:[0-9]+]]=, $1, $1{{$}}
+; CHECK-NEXT: i32.and $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM1]]{{$}}
+; CHECK-NEXT: return $pop[[NUM2]]{{$}}
+define i32 @ord_f64(double %x, double %y) {
+ %a = fcmp ord double %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: uno_f64:
+; CHECK-NEXT: .param f64, f64{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f64.ne $push[[NUM0:[0-9]+]]=, $0, $0{{$}}
+; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $1, $1{{$}}
+; CHECK-NEXT: i32.or $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM1]]{{$}}
+; CHECK-NEXT: return $pop[[NUM2]]{{$}}
+define i32 @uno_f64(double %x, double %y) {
+ %a = fcmp uno double %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: oeq_f64:
+; CHECK-NEXT: .param f64, f64{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f64.eq $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @oeq_f64(double %x, double %y) {
+ %a = fcmp oeq double %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: une_f64:
+; CHECK: f64.ne $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @une_f64(double %x, double %y) {
+ %a = fcmp une double %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: olt_f64:
+; CHECK: f64.lt $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @olt_f64(double %x, double %y) {
+ %a = fcmp olt double %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ole_f64:
+; CHECK: f64.le $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @ole_f64(double %x, double %y) {
+ %a = fcmp ole double %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ogt_f64:
+; CHECK: f64.gt $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @ogt_f64(double %x, double %y) {
+ %a = fcmp ogt double %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: oge_f64:
+; CHECK: f64.ge $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @oge_f64(double %x, double %y) {
+ %a = fcmp oge double %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; Expanded comparisons, which also check for NaN.
+
+; CHECK-LABEL: ueq_f64:
+; CHECK-NEXT: .param f64, f64{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f64.eq $push[[NUM0:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}}
+; CHECK-NEXT: f64.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}}
+; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}}
+; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}}
+; CHECK-NEXT: return $pop[[NUM4]]{{$}}
+define i32 @ueq_f64(double %x, double %y) {
+ %a = fcmp ueq double %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: one_f64:
+; CHECK-NEXT: .param f64, f64{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f64.ne $push[[NUM0:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: f64.eq $push[[NUM1:[0-9]+]]=, $0, $0{{$}}
+; CHECK-NEXT: f64.eq $push[[NUM2:[0-9]+]]=, $1, $1{{$}}
+; CHECK-NEXT: i32.and $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}}
+; CHECK-NEXT: i32.and $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}}
+; CHECK-NEXT: return $pop[[NUM4]]
+define i32 @one_f64(double %x, double %y) {
+ %a = fcmp one double %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ult_f64:
+; CHECK-NEXT: .param f64, f64{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f64.lt $push[[NUM0:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}}
+; CHECK-NEXT: f64.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}}
+; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}}
+; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}}
+; CHECK-NEXT: return $pop[[NUM4]]{{$}}
+define i32 @ult_f64(double %x, double %y) {
+ %a = fcmp ult double %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ule_f64:
+; CHECK-NEXT: .param f64, f64{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f64.le $push[[NUM0:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}}
+; CHECK-NEXT: f64.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}}
+; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}}
+; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}}
+; CHECK-NEXT: return $pop[[NUM4]]{{$}}
+define i32 @ule_f64(double %x, double %y) {
+ %a = fcmp ule double %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ugt_f64:
+; CHECK-NEXT: .param f64, f64{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f64.gt $push[[NUM0:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}}
+; CHECK-NEXT: f64.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}}
+; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}}
+; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}}
+; CHECK-NEXT: return $pop[[NUM4]]{{$}}
+define i32 @ugt_f64(double %x, double %y) {
+ %a = fcmp ugt double %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: uge_f64:
+; CHECK-NEXT: .param f64, f64{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: f64.ge $push[[NUM0:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}}
+; CHECK-NEXT: f64.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}}
+; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}}
+; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}}
+; CHECK-NEXT: return $pop[[NUM4]]{{$}}
+define i32 @uge_f64(double %x, double %y) {
+ %a = fcmp uge double %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
diff --git a/test/CodeGen/WebAssembly/comparisons_i32.ll b/test/CodeGen/WebAssembly/comparisons_i32.ll
new file mode 100644
index 000000000000..b724cec1cc63
--- /dev/null
+++ b/test/CodeGen/WebAssembly/comparisons_i32.ll
@@ -0,0 +1,98 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that basic 32-bit integer comparison operations assemble as expected.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: eq_i32:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.eq $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @eq_i32(i32 %x, i32 %y) {
+ %a = icmp eq i32 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ne_i32:
+; CHECK: i32.ne $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @ne_i32(i32 %x, i32 %y) {
+ %a = icmp ne i32 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: slt_i32:
+; CHECK: i32.lt_s $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @slt_i32(i32 %x, i32 %y) {
+ %a = icmp slt i32 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: sle_i32:
+; CHECK: i32.le_s $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @sle_i32(i32 %x, i32 %y) {
+ %a = icmp sle i32 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ult_i32:
+; CHECK: i32.lt_u $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @ult_i32(i32 %x, i32 %y) {
+ %a = icmp ult i32 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ule_i32:
+; CHECK: i32.le_u $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @ule_i32(i32 %x, i32 %y) {
+ %a = icmp ule i32 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: sgt_i32:
+; CHECK: i32.gt_s $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @sgt_i32(i32 %x, i32 %y) {
+ %a = icmp sgt i32 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: sge_i32:
+; CHECK: i32.ge_s $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @sge_i32(i32 %x, i32 %y) {
+ %a = icmp sge i32 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ugt_i32:
+; CHECK: i32.gt_u $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @ugt_i32(i32 %x, i32 %y) {
+ %a = icmp ugt i32 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: uge_i32:
+; CHECK: i32.ge_u $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @uge_i32(i32 %x, i32 %y) {
+ %a = icmp uge i32 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
diff --git a/test/CodeGen/WebAssembly/comparisons_i64.ll b/test/CodeGen/WebAssembly/comparisons_i64.ll
new file mode 100644
index 000000000000..898591999bec
--- /dev/null
+++ b/test/CodeGen/WebAssembly/comparisons_i64.ll
@@ -0,0 +1,98 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that basic 64-bit integer comparison operations assemble as expected.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: eq_i64:
+; CHECK-NEXT: .param i64, i64{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i64.eq $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @eq_i64(i64 %x, i64 %y) {
+ %a = icmp eq i64 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ne_i64:
+; CHECK: i64.ne $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @ne_i64(i64 %x, i64 %y) {
+ %a = icmp ne i64 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: slt_i64:
+; CHECK: i64.lt_s $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @slt_i64(i64 %x, i64 %y) {
+ %a = icmp slt i64 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: sle_i64:
+; CHECK: i64.le_s $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @sle_i64(i64 %x, i64 %y) {
+ %a = icmp sle i64 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ult_i64:
+; CHECK: i64.lt_u $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @ult_i64(i64 %x, i64 %y) {
+ %a = icmp ult i64 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ule_i64:
+; CHECK: i64.le_u $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @ule_i64(i64 %x, i64 %y) {
+ %a = icmp ule i64 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: sgt_i64:
+; CHECK: i64.gt_s $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @sgt_i64(i64 %x, i64 %y) {
+ %a = icmp sgt i64 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: sge_i64:
+; CHECK: i64.ge_s $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @sge_i64(i64 %x, i64 %y) {
+ %a = icmp sge i64 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: ugt_i64:
+; CHECK: i64.gt_u $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @ugt_i64(i64 %x, i64 %y) {
+ %a = icmp ugt i64 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
+
+; CHECK-LABEL: uge_i64:
+; CHECK: i64.ge_u $push[[NUM:[0-9]+]]=, $0, $1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @uge_i64(i64 %x, i64 %y) {
+ %a = icmp uge i64 %x, %y
+ %b = zext i1 %a to i32
+ ret i32 %b
+}
diff --git a/test/CodeGen/WebAssembly/conv.ll b/test/CodeGen/WebAssembly/conv.ll
new file mode 100644
index 000000000000..e1acaca2c9ec
--- /dev/null
+++ b/test/CodeGen/WebAssembly/conv.ll
@@ -0,0 +1,255 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that basic conversion operations assemble as expected.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: i32_wrap_i64:
+; CHECK-NEXT: .param i64{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.wrap/i64 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @i32_wrap_i64(i64 %x) {
+ %a = trunc i64 %x to i32
+ ret i32 %a
+}
+
+; CHECK-LABEL: i64_extend_s_i32:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.extend_s/i32 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i64 @i64_extend_s_i32(i32 %x) {
+ %a = sext i32 %x to i64
+ ret i64 %a
+}
+
+; CHECK-LABEL: i64_extend_u_i32:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.extend_u/i32 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i64 @i64_extend_u_i32(i32 %x) {
+ %a = zext i32 %x to i64
+ ret i64 %a
+}
+
+; CHECK-LABEL: i32_trunc_s_f32:
+; CHECK-NEXT: .param f32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.trunc_s/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @i32_trunc_s_f32(float %x) {
+ %a = fptosi float %x to i32
+ ret i32 %a
+}
+
+; CHECK-LABEL: i32_trunc_u_f32:
+; CHECK-NEXT: .param f32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.trunc_u/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @i32_trunc_u_f32(float %x) {
+ %a = fptoui float %x to i32
+ ret i32 %a
+}
+
+; CHECK-LABEL: i32_trunc_s_f64:
+; CHECK-NEXT: .param f64{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.trunc_s/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @i32_trunc_s_f64(double %x) {
+ %a = fptosi double %x to i32
+ ret i32 %a
+}
+
+; CHECK-LABEL: i32_trunc_u_f64:
+; CHECK-NEXT: .param f64{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.trunc_u/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @i32_trunc_u_f64(double %x) {
+ %a = fptoui double %x to i32
+ ret i32 %a
+}
+
+; CHECK-LABEL: i64_trunc_s_f32:
+; CHECK-NEXT: .param f32{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.trunc_s/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i64 @i64_trunc_s_f32(float %x) {
+ %a = fptosi float %x to i64
+ ret i64 %a
+}
+
+; CHECK-LABEL: i64_trunc_u_f32:
+; CHECK-NEXT: .param f32{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.trunc_u/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i64 @i64_trunc_u_f32(float %x) {
+ %a = fptoui float %x to i64
+ ret i64 %a
+}
+
+; CHECK-LABEL: i64_trunc_s_f64:
+; CHECK-NEXT: .param f64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.trunc_s/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i64 @i64_trunc_s_f64(double %x) {
+ %a = fptosi double %x to i64
+ ret i64 %a
+}
+
+; CHECK-LABEL: i64_trunc_u_f64:
+; CHECK-NEXT: .param f64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.trunc_u/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i64 @i64_trunc_u_f64(double %x) {
+ %a = fptoui double %x to i64
+ ret i64 %a
+}
+
+; CHECK-LABEL: f32_convert_s_i32:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: f32.convert_s/i32 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define float @f32_convert_s_i32(i32 %x) {
+ %a = sitofp i32 %x to float
+ ret float %a
+}
+
+; CHECK-LABEL: f32_convert_u_i32:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: f32.convert_u/i32 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define float @f32_convert_u_i32(i32 %x) {
+ %a = uitofp i32 %x to float
+ ret float %a
+}
+
+; CHECK-LABEL: f64_convert_s_i32:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result f64{{$}}
+; CHECK-NEXT: f64.convert_s/i32 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define double @f64_convert_s_i32(i32 %x) {
+ %a = sitofp i32 %x to double
+ ret double %a
+}
+
+; CHECK-LABEL: f64_convert_u_i32:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result f64{{$}}
+; CHECK-NEXT: f64.convert_u/i32 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define double @f64_convert_u_i32(i32 %x) {
+ %a = uitofp i32 %x to double
+ ret double %a
+}
+
+; CHECK-LABEL: f32_convert_s_i64:
+; CHECK-NEXT: .param i64{{$}}
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: f32.convert_s/i64 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define float @f32_convert_s_i64(i64 %x) {
+ %a = sitofp i64 %x to float
+ ret float %a
+}
+
+; CHECK-LABEL: f32_convert_u_i64:
+; CHECK-NEXT: .param i64{{$}}
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: f32.convert_u/i64 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define float @f32_convert_u_i64(i64 %x) {
+ %a = uitofp i64 %x to float
+ ret float %a
+}
+
+; CHECK-LABEL: f64_convert_s_i64:
+; CHECK-NEXT: .param i64{{$}}
+; CHECK-NEXT: .result f64{{$}}
+; CHECK-NEXT: f64.convert_s/i64 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define double @f64_convert_s_i64(i64 %x) {
+ %a = sitofp i64 %x to double
+ ret double %a
+}
+
+; CHECK-LABEL: f64_convert_u_i64:
+; CHECK-NEXT: .param i64{{$}}
+; CHECK-NEXT: .result f64{{$}}
+; CHECK-NEXT: f64.convert_u/i64 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define double @f64_convert_u_i64(i64 %x) {
+ %a = uitofp i64 %x to double
+ ret double %a
+}
+
+; CHECK-LABEL: f64_promote_f32:
+; CHECK-NEXT: .param f32{{$}}
+; CHECK-NEXT: .result f64{{$}}
+; CHECK-NEXT: f64.promote/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define double @f64_promote_f32(float %x) {
+ %a = fpext float %x to double
+ ret double %a
+}
+
+; CHECK-LABEL: f32_demote_f64:
+; CHECK-NEXT: .param f64{{$}}
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: f32.demote/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define float @f32_demote_f64(double %x) {
+ %a = fptrunc double %x to float
+ ret float %a
+}
+
+; If the high its are unused, LLVM will optimize sext/zext into anyext, which
+; we need to patterm-match back to a specific instruction.
+
+; CHECK-LABEL: anyext:
+; CHECK: i64.extend_u/i32 $push0=, $0{{$}}
+define i64 @anyext(i32 %x) {
+ %y = sext i32 %x to i64
+ %w = shl i64 %y, 32
+ ret i64 %w
+}
+
+; CHECK-LABEL: bitcast_i32_to_float:
+; CHECK: f32.reinterpret/i32 $push0=, $0{{$}}
+define float @bitcast_i32_to_float(i32 %a) {
+ %t = bitcast i32 %a to float
+ ret float %t
+}
+
+; CHECK-LABEL: bitcast_float_to_i32:
+; CHECK: i32.reinterpret/f32 $push0=, $0{{$}}
+define i32 @bitcast_float_to_i32(float %a) {
+ %t = bitcast float %a to i32
+ ret i32 %t
+}
+
+; CHECK-LABEL: bitcast_i64_to_double:
+; CHECK: f64.reinterpret/i64 $push0=, $0{{$}}
+define double @bitcast_i64_to_double(i64 %a) {
+ %t = bitcast i64 %a to double
+ ret double %t
+}
+
+; CHECK-LABEL: bitcast_double_to_i64:
+; CHECK: i64.reinterpret/f64 $push0=, $0{{$}}
+define i64 @bitcast_double_to_i64(double %a) {
+ %t = bitcast double %a to i64
+ ret i64 %t
+}
diff --git a/test/CodeGen/WebAssembly/copysign-casts.ll b/test/CodeGen/WebAssembly/copysign-casts.ll
new file mode 100644
index 000000000000..760e49133018
--- /dev/null
+++ b/test/CodeGen/WebAssembly/copysign-casts.ll
@@ -0,0 +1,28 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; DAGCombiner oddly folds casts into the rhs of copysign. Test that they get
+; unfolded.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+declare double @copysign(double, double) nounwind readnone
+declare float @copysignf(float, float) nounwind readnone
+
+; CHECK-LABEL: fold_promote:
+; CHECK: f64.promote/f32 $push0=, $1{{$}}
+; CHECK: f64.copysign $push1=, $0, $pop0{{$}}
+define double @fold_promote(double %a, float %b) {
+ %c = fpext float %b to double
+ %t = call double @copysign(double %a, double %c)
+ ret double %t
+}
+
+; CHECK-LABEL: fold_demote:{{$}}
+; CHECK: f32.demote/f64 $push0=, $1{{$}}
+; CHECK: f32.copysign $push1=, $0, $pop0{{$}}
+define float @fold_demote(float %a, double %b) {
+ %c = fptrunc double %b to float
+ %t = call float @copysignf(float %a, float %c)
+ ret float %t
+}
diff --git a/test/CodeGen/WebAssembly/cpus.ll b/test/CodeGen/WebAssembly/cpus.ll
new file mode 100644
index 000000000000..2b77c5f475c8
--- /dev/null
+++ b/test/CodeGen/WebAssembly/cpus.ll
@@ -0,0 +1,17 @@
+; This tests that llc accepts all valid WebAssembly CPUs.
+
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown -mcpu=mvp 2>&1 | FileCheck %s
+; RUN: llc < %s -mtriple=wasm64-unknown-unknown -mcpu=mvp 2>&1 | FileCheck %s
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown -mcpu=generic 2>&1 | FileCheck %s
+; RUN: llc < %s -mtriple=wasm64-unknown-unknown -mcpu=generic 2>&1 | FileCheck %s
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown -mcpu=bleeding-edge 2>&1 | FileCheck %s
+; RUN: llc < %s -mtriple=wasm64-unknown-unknown -mcpu=bleeding-edge 2>&1 | FileCheck %s
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown -mcpu=invalidcpu 2>&1 | FileCheck %s --check-prefix=INVALID
+; RUN: llc < %s -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
+
+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
new file mode 100644
index 000000000000..b03e1569fde6
--- /dev/null
+++ b/test/CodeGen/WebAssembly/dead-vreg.ll
@@ -0,0 +1,51 @@
+; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s
+
+; Check that unused vregs aren't assigned registers.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+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{{$}}
+entry:
+ %cmp.19 = icmp sgt i32 %h, 0
+ br i1 %cmp.19, label %for.cond.1.preheader.lr.ph, label %for.end.7
+
+for.cond.1.preheader.lr.ph:
+ %cmp2.17 = icmp sgt i32 %w, 0
+ br label %for.cond.1.preheader
+
+for.cond.1.preheader:
+ %y.020 = phi i32 [ 0, %for.cond.1.preheader.lr.ph ], [ %inc6, %for.inc.5 ]
+ br i1 %cmp2.17, label %for.body.3.lr.ph, label %for.inc.5
+
+for.body.3.lr.ph:
+ %mul4 = mul nsw i32 %y.020, %w
+ br label %for.body.3
+
+for.body.3:
+ %x.018 = phi i32 [ 0, %for.body.3.lr.ph ], [ %inc, %for.body.3 ]
+ %mul = mul nsw i32 %x.018, %y.020
+ %add = add nsw i32 %x.018, %mul4
+ %arrayidx = getelementptr inbounds i32, i32* %a, i32 %add
+ store i32 %mul, i32* %arrayidx, align 4
+ %inc = add nuw nsw i32 %x.018, 1
+ %exitcond = icmp eq i32 %inc, %w
+ br i1 %exitcond, label %for.inc.5.loopexit, label %for.body.3
+
+for.inc.5.loopexit:
+ br label %for.inc.5
+
+for.inc.5:
+ %inc6 = add nuw nsw i32 %y.020, 1
+ %exitcond22 = icmp eq i32 %inc6, %h
+ br i1 %exitcond22, label %for.end.7.loopexit, label %for.cond.1.preheader
+
+for.end.7.loopexit:
+ br label %for.end.7
+
+for.end.7:
+ ret void
+}
diff --git a/test/CodeGen/WebAssembly/f32.ll b/test/CodeGen/WebAssembly/f32.ll
new file mode 100644
index 000000000000..777010064cdb
--- /dev/null
+++ b/test/CodeGen/WebAssembly/f32.ll
@@ -0,0 +1,154 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that basic 32-bit floating-point operations assemble as expected.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+declare float @llvm.fabs.f32(float)
+declare float @llvm.copysign.f32(float, float)
+declare float @llvm.sqrt.f32(float)
+declare float @llvm.ceil.f32(float)
+declare float @llvm.floor.f32(float)
+declare float @llvm.trunc.f32(float)
+declare float @llvm.nearbyint.f32(float)
+declare float @llvm.rint.f32(float)
+declare float @llvm.fma.f32(float, float, float)
+
+; CHECK-LABEL: fadd32:
+; CHECK-NEXT: .param f32, f32{{$}}
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: f32.add $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define float @fadd32(float %x, float %y) {
+ %a = fadd float %x, %y
+ ret float %a
+}
+
+; CHECK-LABEL: fsub32:
+; CHECK: f32.sub $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define float @fsub32(float %x, float %y) {
+ %a = fsub float %x, %y
+ ret float %a
+}
+
+; CHECK-LABEL: fmul32:
+; CHECK: f32.mul $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define float @fmul32(float %x, float %y) {
+ %a = fmul float %x, %y
+ ret float %a
+}
+
+; CHECK-LABEL: fdiv32:
+; CHECK: f32.div $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define float @fdiv32(float %x, float %y) {
+ %a = fdiv float %x, %y
+ ret float %a
+}
+
+; CHECK-LABEL: fabs32:
+; CHECK: f32.abs $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define float @fabs32(float %x) {
+ %a = call float @llvm.fabs.f32(float %x)
+ ret float %a
+}
+
+; CHECK-LABEL: fneg32:
+; CHECK: f32.neg $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define float @fneg32(float %x) {
+ %a = fsub float -0., %x
+ ret float %a
+}
+
+; CHECK-LABEL: copysign32:
+; CHECK: f32.copysign $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define float @copysign32(float %x, float %y) {
+ %a = call float @llvm.copysign.f32(float %x, float %y)
+ ret float %a
+}
+
+; CHECK-LABEL: sqrt32:
+; CHECK: f32.sqrt $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define float @sqrt32(float %x) {
+ %a = call float @llvm.sqrt.f32(float %x)
+ ret float %a
+}
+
+; CHECK-LABEL: ceil32:
+; CHECK: f32.ceil $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define float @ceil32(float %x) {
+ %a = call float @llvm.ceil.f32(float %x)
+ ret float %a
+}
+
+; CHECK-LABEL: floor32:
+; CHECK: f32.floor $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define float @floor32(float %x) {
+ %a = call float @llvm.floor.f32(float %x)
+ ret float %a
+}
+
+; CHECK-LABEL: trunc32:
+; CHECK: f32.trunc $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define float @trunc32(float %x) {
+ %a = call float @llvm.trunc.f32(float %x)
+ ret float %a
+}
+
+; CHECK-LABEL: nearest32:
+; CHECK: f32.nearest $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define float @nearest32(float %x) {
+ %a = call float @llvm.nearbyint.f32(float %x)
+ ret float %a
+}
+
+; CHECK-LABEL: nearest32_via_rint:
+; CHECK: f32.nearest $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define float @nearest32_via_rint(float %x) {
+ %a = call float @llvm.rint.f32(float %x)
+ ret float %a
+}
+
+; Min and max tests. LLVM currently only forms fminnan and fmaxnan nodes in
+; cases where there's a single fcmp with a select and it can prove that one
+; of the arms is never NaN, so we only test that case. In the future if LLVM
+; learns to form fminnan/fmaxnan in more cases, we can write more general
+; tests.
+
+; CHECK-LABEL: fmin32:
+; CHECK: f32.min $push1=, $0, $pop0{{$}}
+; CHECK-NEXT: return $pop1{{$}}
+define float @fmin32(float %x) {
+ %a = fcmp ult float %x, 0.0
+ %b = select i1 %a, float %x, float 0.0
+ ret float %b
+}
+
+; CHECK-LABEL: fmax32:
+; CHECK: f32.max $push1=, $0, $pop0{{$}}
+; CHECK-NEXT: return $pop1{{$}}
+define float @fmax32(float %x) {
+ %a = fcmp ugt float %x, 0.0
+ %b = select i1 %a, float %x, float 0.0
+ ret float %b
+}
+
+; CHECK-LABEL: fma32:
+; CHECK: {{^}} f32.call $push0=, fmaf, $0, $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define float @fma32(float %a, float %b, float %c) {
+ %d = call float @llvm.fma.f32(float %a, float %b, float %c)
+ ret float %d
+}
diff --git a/test/CodeGen/WebAssembly/f64.ll b/test/CodeGen/WebAssembly/f64.ll
new file mode 100644
index 000000000000..302ee79389b3
--- /dev/null
+++ b/test/CodeGen/WebAssembly/f64.ll
@@ -0,0 +1,154 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that basic 64-bit floating-point operations assemble as expected.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+declare double @llvm.fabs.f64(double)
+declare double @llvm.copysign.f64(double, double)
+declare double @llvm.sqrt.f64(double)
+declare double @llvm.ceil.f64(double)
+declare double @llvm.floor.f64(double)
+declare double @llvm.trunc.f64(double)
+declare double @llvm.nearbyint.f64(double)
+declare double @llvm.rint.f64(double)
+declare double @llvm.fma.f64(double, double, double)
+
+; CHECK-LABEL: fadd64:
+; CHECK-NEXT: .param f64, f64{{$}}
+; CHECK-NEXT: .result f64{{$}}
+; CHECK-NEXT: f64.add $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define double @fadd64(double %x, double %y) {
+ %a = fadd double %x, %y
+ ret double %a
+}
+
+; CHECK-LABEL: fsub64:
+; CHECK: f64.sub $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define double @fsub64(double %x, double %y) {
+ %a = fsub double %x, %y
+ ret double %a
+}
+
+; CHECK-LABEL: fmul64:
+; CHECK: f64.mul $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define double @fmul64(double %x, double %y) {
+ %a = fmul double %x, %y
+ ret double %a
+}
+
+; CHECK-LABEL: fdiv64:
+; CHECK: f64.div $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define double @fdiv64(double %x, double %y) {
+ %a = fdiv double %x, %y
+ ret double %a
+}
+
+; CHECK-LABEL: fabs64:
+; CHECK: f64.abs $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define double @fabs64(double %x) {
+ %a = call double @llvm.fabs.f64(double %x)
+ ret double %a
+}
+
+; CHECK-LABEL: fneg64:
+; CHECK: f64.neg $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define double @fneg64(double %x) {
+ %a = fsub double -0., %x
+ ret double %a
+}
+
+; CHECK-LABEL: copysign64:
+; CHECK: f64.copysign $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define double @copysign64(double %x, double %y) {
+ %a = call double @llvm.copysign.f64(double %x, double %y)
+ ret double %a
+}
+
+; CHECK-LABEL: sqrt64:
+; CHECK: f64.sqrt $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define double @sqrt64(double %x) {
+ %a = call double @llvm.sqrt.f64(double %x)
+ ret double %a
+}
+
+; CHECK-LABEL: ceil64:
+; CHECK: f64.ceil $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define double @ceil64(double %x) {
+ %a = call double @llvm.ceil.f64(double %x)
+ ret double %a
+}
+
+; CHECK-LABEL: floor64:
+; CHECK: f64.floor $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define double @floor64(double %x) {
+ %a = call double @llvm.floor.f64(double %x)
+ ret double %a
+}
+
+; CHECK-LABEL: trunc64:
+; CHECK: f64.trunc $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define double @trunc64(double %x) {
+ %a = call double @llvm.trunc.f64(double %x)
+ ret double %a
+}
+
+; CHECK-LABEL: nearest64:
+; CHECK: f64.nearest $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define double @nearest64(double %x) {
+ %a = call double @llvm.nearbyint.f64(double %x)
+ ret double %a
+}
+
+; CHECK-LABEL: nearest64_via_rint:
+; CHECK: f64.nearest $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define double @nearest64_via_rint(double %x) {
+ %a = call double @llvm.rint.f64(double %x)
+ ret double %a
+}
+
+; Min and max tests. LLVM currently only forms fminnan and fmaxnan nodes in
+; cases where there's a single fcmp with a select and it can prove that one
+; of the arms is never NaN, so we only test that case. In the future if LLVM
+; learns to form fminnan/fmaxnan in more cases, we can write more general
+; tests.
+
+; CHECK-LABEL: fmin64:
+; CHECK: f64.min $push1=, $0, $pop0{{$}}
+; CHECK-NEXT: return $pop1{{$}}
+define double @fmin64(double %x) {
+ %a = fcmp ult double %x, 0.0
+ %b = select i1 %a, double %x, double 0.0
+ ret double %b
+}
+
+; CHECK-LABEL: fmax64:
+; CHECK: f64.max $push1=, $0, $pop0{{$}}
+; CHECK-NEXT: return $pop1{{$}}
+define double @fmax64(double %x) {
+ %a = fcmp ugt double %x, 0.0
+ %b = select i1 %a, double %x, double 0.0
+ ret double %b
+}
+
+; CHECK-LABEL: fma64:
+; CHECK: {{^}} f64.call $push0=, fma, $0, $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define double @fma64(double %a, double %b, double %c) {
+ %d = call double @llvm.fma.f64(double %a, double %b, double %c)
+ ret double %d
+}
diff --git a/test/CodeGen/WebAssembly/fast-isel.ll b/test/CodeGen/WebAssembly/fast-isel.ll
new file mode 100644
index 000000000000..07d78c1415e5
--- /dev/null
+++ b/test/CodeGen/WebAssembly/fast-isel.ll
@@ -0,0 +1,20 @@
+; RUN: llc < %s -asm-verbose=false \
+; RUN: -fast-isel -fast-isel-abort=1 -verify-machineinstrs \
+; RUN: | FileCheck %s
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; This tests very minimal fast-isel functionality.
+
+; CHECK-LABEL: immediate_f32:
+; CHECK: f32.const $push{{[0-9]+}}=, 0x1.4p1{{$}}
+define float @immediate_f32() {
+ ret float 2.5
+}
+
+; CHECK-LABEL: immediate_f64:
+; CHECK: f64.const $push{{[0-9]+}}=, 0x1.4p1{{$}}
+define double @immediate_f64() {
+ ret double 2.5
+}
diff --git a/test/CodeGen/WebAssembly/frem.ll b/test/CodeGen/WebAssembly/frem.ll
new file mode 100644
index 000000000000..688370313b48
--- /dev/null
+++ b/test/CodeGen/WebAssembly/frem.ll
@@ -0,0 +1,26 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that the frem instruction works.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: frem32:
+; CHECK-NEXT: .param f32, f32{{$}}
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: {{^}} f32.call $push0=, fmodf, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define float @frem32(float %x, float %y) {
+ %a = frem float %x, %y
+ ret float %a
+}
+
+; CHECK-LABEL: frem64:
+; CHECK-NEXT: .param f64, f64{{$}}
+; CHECK-NEXT: .result f64{{$}}
+; CHECK-NEXT: {{^}} f64.call $push0=, fmod, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define double @frem64(double %x, double %y) {
+ %a = frem double %x, %y
+ ret double %a
+}
diff --git a/test/CodeGen/WebAssembly/func.ll b/test/CodeGen/WebAssembly/func.ll
new file mode 100644
index 000000000000..6f42dc744ac7
--- /dev/null
+++ b/test/CodeGen/WebAssembly/func.ll
@@ -0,0 +1,62 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that basic functions assemble as expected.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: f0:
+; CHECK: return{{$}}
+; CHECK: .size f0,
+define void @f0() {
+ ret void
+}
+
+; CHECK-LABEL: f1:
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.const $push[[NUM:[0-9]+]]=, 0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+; CHECK: .size f1,
+define i32 @f1() {
+ ret i32 0
+}
+
+; CHECK-LABEL: f2:
+; CHECK-NEXT: .param i32, f32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.const $push[[NUM:[0-9]+]]=, 0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+; CHECK: .size f2,
+define i32 @f2(i32 %p1, float %p2) {
+ ret i32 0
+}
+
+; CHECK-LABEL: f3:
+; CHECK-NEXT: .param i32, f32{{$}}
+; CHECK-NOT: local
+; CHECK-NEXT: return{{$}}
+; CHECK: .size f3,
+define void @f3(i32 %p1, float %p2) {
+ ret void
+}
+
+; CHECK-LABEL: f4:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: local
+define i32 @f4(i32 %x) {
+entry:
+ %c = trunc i32 %x to i1
+ br i1 %c, label %true, label %false
+true:
+ ret i32 0
+false:
+ ret i32 1
+}
+
+; CHECK-LABEL: f5:
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: unreachable
+define float @f5() {
+ unreachable
+}
diff --git a/test/CodeGen/WebAssembly/global.ll b/test/CodeGen/WebAssembly/global.ll
new file mode 100644
index 000000000000..5f149ed067c8
--- /dev/null
+++ b/test/CodeGen/WebAssembly/global.ll
@@ -0,0 +1,177 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that globals assemble as expected.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-NOT: llvm.used
+; CHECK-NOT: llvm.metadata
+@llvm.used = appending global [1 x i32*] [i32* @g], section "llvm.metadata"
+
+; CHECK: foo:
+; CHECK: i32.const $push0=, 0{{$}}
+; CHECK-NEXT: i32.load $push1=, answer($pop0){{$}}
+; CHECK-NEXT: return $pop1{{$}}
+define i32 @foo() {
+ %a = load i32, i32* @answer
+ ret i32 %a
+}
+
+; CHECK-LABEL: call_memcpy:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: call memcpy, $0, $1, $2{{$}}
+; CHECK-NEXT: return $0{{$}}
+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)
+ ret i8* %p
+}
+
+; CHECK: .type g,@object
+; CHECK: .align 2{{$}}
+; CHECK-NEXT: g:
+; CHECK-NEXT: .int32 1337{{$}}
+; CHECK-NEXT: .size g, 4{{$}}
+@g = private global i32 1337
+
+; CHECK-LABEL: ud:
+; CHECK-NEXT: .zero 4{{$}}
+; CHECK-NEXT: .size ud, 4{{$}}
+@ud = internal global i32 undef
+
+; CHECK: .type nil,@object
+; CHECK-NEXT: .lcomm nil,4,2{{$}}
+@nil = internal global i32 zeroinitializer
+
+; CHECK: .type z,@object
+; CHECK-NEXT: .lcomm z,4,2{{$}}
+@z = internal global i32 0
+
+; CHECK-NEXT: .type one,@object
+; CHECK-NEXT: .align 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-NEXT: answer:
+; CHECK-NEXT: .int32 42{{$}}
+; CHECK-NEXT: .size answer, 4{{$}}
+@answer = internal global i32 42
+
+; CHECK: .type u32max,@object
+; CHECK: .align 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-NEXT: ud64:
+; CHECK-NEXT: .zero 8{{$}}
+; CHECK-NEXT: .size ud64, 8{{$}}
+@ud64 = internal global i64 undef
+
+; CHECK: .type nil64,@object
+; CHECK: .lcomm nil64,8,3{{$}}
+@nil64 = internal global i64 zeroinitializer
+
+; CHECK: .type z64,@object
+; CHECK: .lcomm z64,8,3{{$}}
+@z64 = internal global i64 0
+
+; CHECK: .type twoP32,@object
+; CHECK: .align 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-NEXT: u64max:
+; CHECK-NEXT: .int64 -1{{$}}
+; CHECK-NEXT: .size u64max, 8{{$}}
+@u64max = internal global i64 -1
+
+; CHECK: .type f32ud,@object
+; CHECK: .align 2{{$}}
+; CHECK-NEXT: f32ud:
+; CHECK-NEXT: .zero 4{{$}}
+; CHECK-NEXT: .size f32ud, 4{{$}}
+@f32ud = internal global float undef
+
+; CHECK: .type f32nil,@object
+; CHECK: .lcomm f32nil,4,2{{$}}
+@f32nil = internal global float zeroinitializer
+
+; CHECK: .type f32z,@object
+; CHECK: .lcomm f32z,4,2{{$}}
+@f32z = internal global float 0.0
+
+; CHECK: .type f32nz,@object
+; CHECK: .align 2{{$}}
+; CHECK: f32nz:
+; CHECK: .int32 2147483648{{$}}
+; CHECK: .size f32nz, 4{{$}}
+@f32nz = internal global float -0.0
+
+; CHECK: .type f32two,@object
+; CHECK: .align 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-NEXT: f64ud:
+; CHECK-NEXT: .zero 8{{$}}
+; CHECK-NEXT: .size f64ud, 8{{$}}
+@f64ud = internal global double undef
+
+; CHECK: .type f64nil,@object
+; CHECK: .lcomm f64nil,8,3{{$}}
+@f64nil = internal global double zeroinitializer
+
+; CHECK: .type f64z,@object
+; CHECK: .lcomm f64z,8,3{{$}}
+@f64z = internal global double 0.0
+
+; CHECK: .type f64nz,@object
+; CHECK: .align 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-NEXT: f64two:
+; CHECK-NEXT: .int64 4611686018427387904{{$}}
+; CHECK-NEXT: .size f64two, 8{{$}}
+@f64two = internal global double 2.0
+
+; Indexing into a global array produces a relocation.
+; CHECK: .type arr,@object
+; CHECK: .type ptr,@object
+; CHECK: ptr:
+; CHECK-NEXT: .int32 arr+80
+; CHECK-NEXT: .size ptr, 4
+@arr = global [128 x i32] zeroinitializer, align 16
+@ptr = global i32* getelementptr inbounds ([128 x i32], [128 x i32]* @arr, i32 0, i32 20), align 4
+
+; Constant global.
+; CHECK: .type rom,@object{{$}}
+; CHECK: .section .rodata,"a",@progbits{{$}}
+; CHECK: .globl rom{{$}}
+; CHECK: .align 4{{$}}
+; CHECK: rom:
+; CHECK: .zero 512{{$}}
+; CHECK: .size rom, 512{{$}}
+@rom = constant [128 x i32] zeroinitializer, align 16
diff --git a/test/CodeGen/WebAssembly/globl.ll b/test/CodeGen/WebAssembly/globl.ll
new file mode 100644
index 000000000000..a5dc028c1db4
--- /dev/null
+++ b/test/CodeGen/WebAssembly/globl.ll
@@ -0,0 +1,10 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK: .globl foo
+; CHECK-LABEL: foo:
+define void @foo() {
+ ret void
+}
diff --git a/test/CodeGen/WebAssembly/i32.ll b/test/CodeGen/WebAssembly/i32.ll
new file mode 100644
index 000000000000..ab29b0472bf2
--- /dev/null
+++ b/test/CodeGen/WebAssembly/i32.ll
@@ -0,0 +1,190 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that basic 32-bit integer operations assemble as expected.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+declare i32 @llvm.ctlz.i32(i32, i1)
+declare i32 @llvm.cttz.i32(i32, i1)
+declare i32 @llvm.ctpop.i32(i32)
+
+; CHECK-LABEL: add32:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.add $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @add32(i32 %x, i32 %y) {
+ %a = add i32 %x, %y
+ ret i32 %a
+}
+
+; CHECK-LABEL: sub32:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.sub $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @sub32(i32 %x, i32 %y) {
+ %a = sub i32 %x, %y
+ ret i32 %a
+}
+
+; CHECK-LABEL: mul32:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.mul $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @mul32(i32 %x, i32 %y) {
+ %a = mul i32 %x, %y
+ ret i32 %a
+}
+
+; CHECK-LABEL: sdiv32:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.div_s $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @sdiv32(i32 %x, i32 %y) {
+ %a = sdiv i32 %x, %y
+ ret i32 %a
+}
+
+; CHECK-LABEL: udiv32:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.div_u $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @udiv32(i32 %x, i32 %y) {
+ %a = udiv i32 %x, %y
+ ret i32 %a
+}
+
+; CHECK-LABEL: srem32:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.rem_s $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @srem32(i32 %x, i32 %y) {
+ %a = srem i32 %x, %y
+ ret i32 %a
+}
+
+; CHECK-LABEL: urem32:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.rem_u $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @urem32(i32 %x, i32 %y) {
+ %a = urem i32 %x, %y
+ ret i32 %a
+}
+
+; CHECK-LABEL: and32:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.and $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @and32(i32 %x, i32 %y) {
+ %a = and i32 %x, %y
+ ret i32 %a
+}
+
+; CHECK-LABEL: or32:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.or $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @or32(i32 %x, i32 %y) {
+ %a = or i32 %x, %y
+ ret i32 %a
+}
+
+; CHECK-LABEL: xor32:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.xor $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @xor32(i32 %x, i32 %y) {
+ %a = xor i32 %x, %y
+ ret i32 %a
+}
+
+; CHECK-LABEL: shl32:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.shl $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @shl32(i32 %x, i32 %y) {
+ %a = shl i32 %x, %y
+ ret i32 %a
+}
+
+; CHECK-LABEL: shr32:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.shr_u $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @shr32(i32 %x, i32 %y) {
+ %a = lshr i32 %x, %y
+ ret i32 %a
+}
+
+; CHECK-LABEL: sar32:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.shr_s $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @sar32(i32 %x, i32 %y) {
+ %a = ashr i32 %x, %y
+ ret i32 %a
+}
+
+; CHECK-LABEL: clz32:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.clz $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @clz32(i32 %x) {
+ %a = call i32 @llvm.ctlz.i32(i32 %x, i1 false)
+ ret i32 %a
+}
+
+; CHECK-LABEL: clz32_zero_undef:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.clz $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @clz32_zero_undef(i32 %x) {
+ %a = call i32 @llvm.ctlz.i32(i32 %x, i1 true)
+ ret i32 %a
+}
+
+; CHECK-LABEL: ctz32:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.ctz $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @ctz32(i32 %x) {
+ %a = call i32 @llvm.cttz.i32(i32 %x, i1 false)
+ ret i32 %a
+}
+
+; CHECK-LABEL: ctz32_zero_undef:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.ctz $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @ctz32_zero_undef(i32 %x) {
+ %a = call i32 @llvm.cttz.i32(i32 %x, i1 true)
+ ret i32 %a
+}
+
+; CHECK-LABEL: popcnt32:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.popcnt $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @popcnt32(i32 %x) {
+ %a = call i32 @llvm.ctpop.i32(i32 %x)
+ ret i32 %a
+}
diff --git a/test/CodeGen/WebAssembly/i64.ll b/test/CodeGen/WebAssembly/i64.ll
new file mode 100644
index 000000000000..769f74266754
--- /dev/null
+++ b/test/CodeGen/WebAssembly/i64.ll
@@ -0,0 +1,190 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that basic 64-bit integer operations assemble as expected.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+declare i64 @llvm.ctlz.i64(i64, i1)
+declare i64 @llvm.cttz.i64(i64, i1)
+declare i64 @llvm.ctpop.i64(i64)
+
+; CHECK-LABEL: add64:
+; CHECK-NEXT: .param i64, i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.add $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @add64(i64 %x, i64 %y) {
+ %a = add i64 %x, %y
+ ret i64 %a
+}
+
+; CHECK-LABEL: sub64:
+; CHECK-NEXT: .param i64, i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.sub $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @sub64(i64 %x, i64 %y) {
+ %a = sub i64 %x, %y
+ ret i64 %a
+}
+
+; CHECK-LABEL: mul64:
+; CHECK-NEXT: .param i64, i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.mul $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @mul64(i64 %x, i64 %y) {
+ %a = mul i64 %x, %y
+ ret i64 %a
+}
+
+; CHECK-LABEL: sdiv64:
+; CHECK-NEXT: .param i64, i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.div_s $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @sdiv64(i64 %x, i64 %y) {
+ %a = sdiv i64 %x, %y
+ ret i64 %a
+}
+
+; CHECK-LABEL: udiv64:
+; CHECK-NEXT: .param i64, i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.div_u $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @udiv64(i64 %x, i64 %y) {
+ %a = udiv i64 %x, %y
+ ret i64 %a
+}
+
+; CHECK-LABEL: srem64:
+; CHECK-NEXT: .param i64, i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.rem_s $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @srem64(i64 %x, i64 %y) {
+ %a = srem i64 %x, %y
+ ret i64 %a
+}
+
+; CHECK-LABEL: urem64:
+; CHECK-NEXT: .param i64, i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.rem_u $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @urem64(i64 %x, i64 %y) {
+ %a = urem i64 %x, %y
+ ret i64 %a
+}
+
+; CHECK-LABEL: and64:
+; CHECK-NEXT: .param i64, i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.and $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @and64(i64 %x, i64 %y) {
+ %a = and i64 %x, %y
+ ret i64 %a
+}
+
+; CHECK-LABEL: or64:
+; CHECK-NEXT: .param i64, i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.or $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @or64(i64 %x, i64 %y) {
+ %a = or i64 %x, %y
+ ret i64 %a
+}
+
+; CHECK-LABEL: xor64:
+; CHECK-NEXT: .param i64, i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.xor $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @xor64(i64 %x, i64 %y) {
+ %a = xor i64 %x, %y
+ ret i64 %a
+}
+
+; CHECK-LABEL: shl64:
+; CHECK-NEXT: .param i64, i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.shl $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @shl64(i64 %x, i64 %y) {
+ %a = shl i64 %x, %y
+ ret i64 %a
+}
+
+; CHECK-LABEL: shr64:
+; CHECK-NEXT: .param i64, i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.shr_u $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @shr64(i64 %x, i64 %y) {
+ %a = lshr i64 %x, %y
+ ret i64 %a
+}
+
+; CHECK-LABEL: sar64:
+; CHECK-NEXT: .param i64, i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.shr_s $push0=, $0, $1{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @sar64(i64 %x, i64 %y) {
+ %a = ashr i64 %x, %y
+ ret i64 %a
+}
+
+; CHECK-LABEL: clz64:
+; CHECK-NEXT: .param i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.clz $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @clz64(i64 %x) {
+ %a = call i64 @llvm.ctlz.i64(i64 %x, i1 false)
+ ret i64 %a
+}
+
+; CHECK-LABEL: clz64_zero_undef:
+; CHECK-NEXT: .param i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.clz $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @clz64_zero_undef(i64 %x) {
+ %a = call i64 @llvm.ctlz.i64(i64 %x, i1 true)
+ ret i64 %a
+}
+
+; CHECK-LABEL: ctz64:
+; CHECK-NEXT: .param i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.ctz $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @ctz64(i64 %x) {
+ %a = call i64 @llvm.cttz.i64(i64 %x, i1 false)
+ ret i64 %a
+}
+
+; CHECK-LABEL: ctz64_zero_undef:
+; CHECK-NEXT: .param i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.ctz $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @ctz64_zero_undef(i64 %x) {
+ %a = call i64 @llvm.cttz.i64(i64 %x, i1 true)
+ ret i64 %a
+}
+
+; CHECK-LABEL: popcnt64:
+; CHECK-NEXT: .param i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.popcnt $push0=, $0{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @popcnt64(i64 %x) {
+ %a = call i64 @llvm.ctpop.i64(i64 %x)
+ ret i64 %a
+}
diff --git a/test/CodeGen/WebAssembly/ident.ll b/test/CodeGen/WebAssembly/ident.ll
new file mode 100644
index 000000000000..1e0dc2aa6725
--- /dev/null
+++ b/test/CodeGen/WebAssembly/ident.ll
@@ -0,0 +1,12 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test llvm.ident.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK: .ident "hello world"
+
+!llvm.ident = !{!0}
+
+!0 = !{!"hello world"}
diff --git a/test/CodeGen/WebAssembly/immediates.ll b/test/CodeGen/WebAssembly/immediates.ll
new file mode 100644
index 000000000000..abab11f2254e
--- /dev/null
+++ b/test/CodeGen/WebAssembly/immediates.ll
@@ -0,0 +1,198 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that basic immediates assemble as expected.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: zero_i32:
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.const $push[[NUM:[0-9]+]]=, 0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @zero_i32() {
+ ret i32 0
+}
+
+; CHECK-LABEL: one_i32:
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.const $push[[NUM:[0-9]+]]=, 1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @one_i32() {
+ ret i32 1
+}
+
+; CHECK-LABEL: max_i32:
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.const $push[[NUM:[0-9]+]]=, 2147483647{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @max_i32() {
+ ret i32 2147483647
+}
+
+; CHECK-LABEL: min_i32:
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.const $push[[NUM:[0-9]+]]=, -2147483648{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i32 @min_i32() {
+ ret i32 -2147483648
+}
+
+; CHECK-LABEL: zero_i64:
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.const $push[[NUM:[0-9]+]]=, 0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i64 @zero_i64() {
+ ret i64 0
+}
+
+; CHECK-LABEL: one_i64:
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.const $push[[NUM:[0-9]+]]=, 1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i64 @one_i64() {
+ ret i64 1
+}
+
+; CHECK-LABEL: max_i64:
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.const $push[[NUM:[0-9]+]]=, 9223372036854775807{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i64 @max_i64() {
+ ret i64 9223372036854775807
+}
+
+; CHECK-LABEL: min_i64:
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: i64.const $push[[NUM:[0-9]+]]=, -9223372036854775808{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define i64 @min_i64() {
+ ret i64 -9223372036854775808
+}
+
+; CHECK-LABEL: negzero_f32:
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: f32.const $push[[NUM:[0-9]+]]=, -0x0p0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define float @negzero_f32() {
+ ret float -0.0
+}
+
+; CHECK-LABEL: zero_f32:
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: f32.const $push[[NUM:[0-9]+]]=, 0x0p0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define float @zero_f32() {
+ ret float 0.0
+}
+
+; CHECK-LABEL: one_f32:
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: f32.const $push[[NUM:[0-9]+]]=, 0x1p0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define float @one_f32() {
+ ret float 1.0
+}
+
+; CHECK-LABEL: two_f32:
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: f32.const $push[[NUM:[0-9]+]]=, 0x1p1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define float @two_f32() {
+ ret float 2.0
+}
+
+; CHECK-LABEL: nan_f32:
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: f32.const $push[[NUM:[0-9]+]]=, nan{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define float @nan_f32() {
+ ret float 0x7FF8000000000000
+}
+
+; CHECK-LABEL: negnan_f32:
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: f32.const $push[[NUM:[0-9]+]]=, -nan{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define float @negnan_f32() {
+ ret float 0xFFF8000000000000
+}
+
+; CHECK-LABEL: inf_f32:
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: f32.const $push[[NUM:[0-9]+]]=, infinity{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define float @inf_f32() {
+ ret float 0x7FF0000000000000
+}
+
+; CHECK-LABEL: neginf_f32:
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: f32.const $push[[NUM:[0-9]+]]=, -infinity{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define float @neginf_f32() {
+ ret float 0xFFF0000000000000
+}
+
+; CHECK-LABEL: negzero_f64:
+; CHECK-NEXT: .result f64{{$}}
+; CHECK-NEXT: f64.const $push[[NUM:[0-9]+]]=, -0x0p0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define double @negzero_f64() {
+ ret double -0.0
+}
+
+; CHECK-LABEL: zero_f64:
+; CHECK-NEXT: .result f64{{$}}
+; CHECK-NEXT: f64.const $push[[NUM:[0-9]+]]=, 0x0p0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define double @zero_f64() {
+ ret double 0.0
+}
+
+; CHECK-LABEL: one_f64:
+; CHECK-NEXT: .result f64{{$}}
+; CHECK-NEXT: f64.const $push[[NUM:[0-9]+]]=, 0x1p0{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define double @one_f64() {
+ ret double 1.0
+}
+
+; CHECK-LABEL: two_f64:
+; CHECK-NEXT: .result f64{{$}}
+; CHECK-NEXT: f64.const $push[[NUM:[0-9]+]]=, 0x1p1{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define double @two_f64() {
+ ret double 2.0
+}
+
+; CHECK-LABEL: nan_f64:
+; CHECK-NEXT: .result f64{{$}}
+; CHECK-NEXT: f64.const $push[[NUM:[0-9]+]]=, nan{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define double @nan_f64() {
+ ret double 0x7FF8000000000000
+}
+
+; CHECK-LABEL: negnan_f64:
+; CHECK-NEXT: .result f64{{$}}
+; CHECK-NEXT: f64.const $push[[NUM:[0-9]+]]=, -nan{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define double @negnan_f64() {
+ ret double 0xFFF8000000000000
+}
+
+; CHECK-LABEL: inf_f64:
+; CHECK-NEXT: .result f64{{$}}
+; CHECK-NEXT: f64.const $push[[NUM:[0-9]+]]=, infinity{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define double @inf_f64() {
+ ret double 0x7FF0000000000000
+}
+
+; CHECK-LABEL: neginf_f64:
+; CHECK-NEXT: .result f64{{$}}
+; CHECK-NEXT: f64.const $push[[NUM:[0-9]+]]=, -infinity{{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define double @neginf_f64() {
+ ret double 0xFFF0000000000000
+}
diff --git a/test/CodeGen/WebAssembly/inline-asm.ll b/test/CodeGen/WebAssembly/inline-asm.ll
new file mode 100644
index 000000000000..fc066c4b812f
--- /dev/null
+++ b/test/CodeGen/WebAssembly/inline-asm.ll
@@ -0,0 +1,94 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test basic inline assembly.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: foo:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: #APP{{$}}
+; CHECK-NEXT: # $0 = aaa($0){{$}}
+; CHECK-NEXT: #NO_APP{{$}}
+; CHECK-NEXT: return $0{{$}}
+define i32 @foo(i32 %r) {
+entry:
+ %0 = tail call i32 asm sideeffect "# $0 = aaa($1)", "=r,r"(i32 %r) #0, !srcloc !0
+ ret i32 %0
+}
+
+; CHECK-LABEL: bar:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: #APP{{$}}
+; CHECK-NEXT: # 0($1) = bbb(0($0)){{$}}
+; CHECK-NEXT: #NO_APP{{$}}
+; CHECK-NEXT: return{{$}}
+define void @bar(i32* %r, i32* %s) {
+entry:
+ tail call void asm sideeffect "# $0 = bbb($1)", "=*m,*m"(i32* %s, i32* %r) #0, !srcloc !1
+ ret void
+}
+
+; CHECK-LABEL: imm:
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: .local i32{{$}}
+; CHECK-NEXT: #APP{{$}}
+; CHECK-NEXT: # $0 = ccc(42){{$}}
+; CHECK-NEXT: #NO_APP{{$}}
+; CHECK-NEXT: return $0{{$}}
+define i32 @imm() {
+entry:
+ %0 = tail call i32 asm sideeffect "# $0 = ccc($1)", "=r,i"(i32 42) #0, !srcloc !2
+ ret i32 %0
+}
+
+; CHECK-LABEL: foo_i64:
+; CHECK-NEXT: .param i64{{$}}
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: #APP{{$}}
+; CHECK-NEXT: # $0 = aaa($0){{$}}
+; CHECK-NEXT: #NO_APP{{$}}
+; CHECK-NEXT: return $0{{$}}
+define i64 @foo_i64(i64 %r) {
+entry:
+ %0 = tail call i64 asm sideeffect "# $0 = aaa($1)", "=r,r"(i64 %r) #0, !srcloc !0
+ ret i64 %0
+}
+
+; CHECK-LABEL: X_i16:
+; CHECK: foo $1{{$}}
+; CHECK: i32.store16 $discard=, 0($0), $1{{$}}
+define void @X_i16(i16 * %t) {
+ call void asm sideeffect "foo $0", "=*X,~{dirflag},~{fpsr},~{flags},~{memory}"(i16* %t)
+ ret void
+}
+
+; CHECK-LABEL: X_ptr:
+; CHECK: foo $1{{$}}
+; CHECK: i32.store $discard=, 0($0), $1{{$}}
+define void @X_ptr(i16 ** %t) {
+ call void asm sideeffect "foo $0", "=*X,~{dirflag},~{fpsr},~{flags},~{memory}"(i16** %t)
+ ret void
+}
+
+; CHECK-LABEL: funcname:
+; CHECK: foo funcname{{$}}
+define void @funcname() {
+ tail call void asm sideeffect "foo $0", "i"(void ()* nonnull @funcname) #0, !srcloc !0
+ ret void
+}
+
+; CHECK-LABEL: varname:
+; CHECK: foo gv+37{{$}}
+@gv = global [0 x i8] zeroinitializer
+define void @varname() {
+ tail call void asm sideeffect "foo $0", "i"(i8* getelementptr inbounds ([0 x i8], [0 x i8]* @gv, i64 0, i64 37)) #0, !srcloc !0
+ ret void
+}
+
+attributes #0 = { nounwind }
+
+!0 = !{i32 47}
+!1 = !{i32 145}
+!2 = !{i32 231}
diff --git a/test/CodeGen/WebAssembly/legalize.ll b/test/CodeGen/WebAssembly/legalize.ll
new file mode 100644
index 000000000000..e780b2ee36ca
--- /dev/null
+++ b/test/CodeGen/WebAssembly/legalize.ll
@@ -0,0 +1,62 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test various types and operators that need to be legalized.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: shl_i3:
+; CHECK: i32.const $push0=, 7{{$}}
+; CHECK: i32.and $push1=, $1, $pop0{{$}}
+; CHECK: i32.shl $push2=, $0, $pop1{{$}}
+define i3 @shl_i3(i3 %a, i3 %b, i3* %p) {
+ %t = shl i3 %a, %b
+ ret i3 %t
+}
+
+; CHECK-LABEL: shl_i53:
+; CHECK: i64.const $push0=, 9007199254740991{{$}}
+; CHECK: i64.and $push1=, $1, $pop0{{$}}
+; CHECK: i64.shl $push2=, $0, $pop1{{$}}
+define i53 @shl_i53(i53 %a, i53 %b, i53* %p) {
+ %t = shl i53 %a, %b
+ ret i53 %t
+}
+
+; CHECK-LABEL: sext_in_reg_i32_i64:
+; CHECK: i64.shl
+; CHECK: i64.shr_s
+define i64 @sext_in_reg_i32_i64(i64 %a) {
+ %b = shl i64 %a, 32
+ %c = ashr i64 %b, 32
+ ret i64 %c
+}
+
+; CHECK-LABEL: fpext_f32_f64:
+; CHECK: f32.load $push0=, 0($0){{$}}
+; CHECK: f64.promote/f32 $push1=, $pop0{{$}}
+; CHECK: return $pop1{{$}}
+define double @fpext_f32_f64(float *%p) {
+ %v = load float, float* %p
+ %e = fpext float %v to double
+ ret double %e
+}
+
+; CHECK-LABEL: fpconv_f64_f32:
+; CHECK: f64.load $push0=, 0($0){{$}}
+; CHECK: f32.demote/f64 $push1=, $pop0{{$}}
+; CHECK: return $pop1{{$}}
+define float @fpconv_f64_f32(double *%p) {
+ %v = load double, double* %p
+ %e = fptrunc double %v to float
+ ret float %e
+}
+
+; Check that big shifts work. This generates a big pile of code from the
+; legalizer; the main thing here is that we don't abort.
+
+; CHECK-LABEL: bigshift:
+define i1024 @bigshift(i1024 %a, i1024 %b) {
+ %c = shl i1024 %a, %b
+ ret i1024 %c
+}
diff --git a/test/CodeGen/WebAssembly/load-ext.ll b/test/CodeGen/WebAssembly/load-ext.ll
new file mode 100644
index 000000000000..0ffcd38a8666
--- /dev/null
+++ b/test/CodeGen/WebAssembly/load-ext.ll
@@ -0,0 +1,96 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that extending loads are assembled properly.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: sext_i8_i32:
+; CHECK: i32.load8_s $push0=, 0($0){{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @sext_i8_i32(i8 *%p) {
+ %v = load i8, i8* %p
+ %e = sext i8 %v to i32
+ ret i32 %e
+}
+
+; CHECK-LABEL: zext_i8_i32:
+; CHECK: i32.load8_u $push0=, 0($0){{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @zext_i8_i32(i8 *%p) {
+ %v = load i8, i8* %p
+ %e = zext i8 %v to i32
+ ret i32 %e
+}
+
+; CHECK-LABEL: sext_i16_i32:
+; CHECK: i32.load16_s $push0=, 0($0){{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @sext_i16_i32(i16 *%p) {
+ %v = load i16, i16* %p
+ %e = sext i16 %v to i32
+ ret i32 %e
+}
+
+; CHECK-LABEL: zext_i16_i32:
+; CHECK: i32.load16_u $push0=, 0($0){{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @zext_i16_i32(i16 *%p) {
+ %v = load i16, i16* %p
+ %e = zext i16 %v to i32
+ ret i32 %e
+}
+
+; CHECK-LABEL: sext_i8_i64:
+; CHECK: i64.load8_s $push0=, 0($0){{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @sext_i8_i64(i8 *%p) {
+ %v = load i8, i8* %p
+ %e = sext i8 %v to i64
+ ret i64 %e
+}
+
+; CHECK-LABEL: zext_i8_i64:
+; CHECK: i64.load8_u $push0=, 0($0){{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @zext_i8_i64(i8 *%p) {
+ %v = load i8, i8* %p
+ %e = zext i8 %v to i64
+ ret i64 %e
+}
+
+; CHECK-LABEL: sext_i16_i64:
+; CHECK: i64.load16_s $push0=, 0($0){{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @sext_i16_i64(i16 *%p) {
+ %v = load i16, i16* %p
+ %e = sext i16 %v to i64
+ ret i64 %e
+}
+
+; CHECK-LABEL: zext_i16_i64:
+; CHECK: i64.load16_u $push0=, 0($0){{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @zext_i16_i64(i16 *%p) {
+ %v = load i16, i16* %p
+ %e = zext i16 %v to i64
+ ret i64 %e
+}
+
+; CHECK-LABEL: sext_i32_i64:
+; CHECK: i64.load32_s $push0=, 0($0){{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @sext_i32_i64(i32 *%p) {
+ %v = load i32, i32* %p
+ %e = sext i32 %v to i64
+ ret i64 %e
+}
+
+; CHECK-LABEL: zext_i32_i64:
+; CHECK: i64.load32_u $push0=, 0($0){{$}}
+; CHECK: return $pop0{{$}}
+define i64 @zext_i32_i64(i32 *%p) {
+ %v = load i32, i32* %p
+ %e = zext i32 %v to i64
+ ret i64 %e
+}
diff --git a/test/CodeGen/WebAssembly/load-store-i1.ll b/test/CodeGen/WebAssembly/load-store-i1.ll
new file mode 100644
index 000000000000..37b514729479
--- /dev/null
+++ b/test/CodeGen/WebAssembly/load-store-i1.ll
@@ -0,0 +1,68 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that i1 extending loads and truncating stores are assembled properly.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: load_u_i1_i32:
+; CHECK: i32.load8_u $push[[NUM0:[0-9]+]]=, 0($0){{$}}
+; CHECK-NEXT: return $pop[[NUM0]]{{$}}
+define i32 @load_u_i1_i32(i1* %p) {
+ %v = load i1, i1* %p
+ %e = zext i1 %v to i32
+ ret i32 %e
+}
+
+; 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-NEXT: return $pop[[NUM3]]{{$}}
+define i32 @load_s_i1_i32(i1* %p) {
+ %v = load i1, i1* %p
+ %e = sext i1 %v to i32
+ ret i32 %e
+}
+
+; CHECK-LABEL: load_u_i1_i64:
+; CHECK: i64.load8_u $push[[NUM0:[0-9]+]]=, 0($0){{$}}
+; CHECK-NEXT: return $pop[[NUM0]]{{$}}
+define i64 @load_u_i1_i64(i1* %p) {
+ %v = load i1, i1* %p
+ %e = zext i1 %v to i64
+ ret i64 %e
+}
+
+; 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-NEXT: return $pop[[NUM3]]{{$}}
+define i64 @load_s_i1_i64(i1* %p) {
+ %v = load i1, i1* %p
+ %e = sext i1 %v to i64
+ ret i64 %e
+}
+
+; 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]]{{$}}
+define void @store_i32_i1(i1* %p, i32 %v) {
+ %t = trunc i32 %v to i1
+ store i1 %t, i1* %p
+ ret void
+}
+
+; 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]]{{$}}
+define void @store_i64_i1(i1* %p, i64 %v) {
+ %t = trunc i64 %v to i1
+ store i1 %t, i1* %p
+ ret void
+}
diff --git a/test/CodeGen/WebAssembly/load.ll b/test/CodeGen/WebAssembly/load.ll
new file mode 100644
index 000000000000..aa8ae689e0d1
--- /dev/null
+++ b/test/CodeGen/WebAssembly/load.ll
@@ -0,0 +1,46 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that basic loads are assembled properly.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; 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
+}
+
+; 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
+}
+
+; CHECK-LABEL: ldf32:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result f32{{$}}
+; CHECK-NEXT: f32.load $push[[NUM:[0-9]+]]=, 0($0){{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define float @ldf32(float *%p) {
+ %v = load float, float* %p
+ ret float %v
+}
+
+; CHECK-LABEL: ldf64:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result f64{{$}}
+; CHECK-NEXT: f64.load $push[[NUM:[0-9]+]]=, 0($0){{$}}
+; CHECK-NEXT: return $pop[[NUM]]{{$}}
+define double @ldf64(double *%p) {
+ %v = load double, double* %p
+ ret double %v
+}
diff --git a/test/CodeGen/WebAssembly/loop-idiom.ll b/test/CodeGen/WebAssembly/loop-idiom.ll
new file mode 100644
index 000000000000..2906df20a229
--- /dev/null
+++ b/test/CodeGen/WebAssembly/loop-idiom.ll
@@ -0,0 +1,53 @@
+; RUN: opt -loop-idiom -S < %s -march=wasm32 | FileCheck %s
+
+target datalayout = "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/memory-addr32.ll b/test/CodeGen/WebAssembly/memory-addr32.ll
new file mode 100644
index 000000000000..e2dd556bddc0
--- /dev/null
+++ b/test/CodeGen/WebAssembly/memory-addr32.ll
@@ -0,0 +1,27 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that basic memory operations assemble as expected with 32-bit addresses.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+declare i32 @llvm.wasm.memory.size.i32() nounwind readonly
+declare void @llvm.wasm.grow.memory.i32(i32) nounwind
+
+; CHECK-LABEL: memory_size:
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: memory_size $push0={{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @memory_size() {
+ %a = call i32 @llvm.wasm.memory.size.i32()
+ ret i32 %a
+}
+
+; CHECK-LABEL: grow_memory:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK: grow_memory $0{{$}}
+; CHECK-NEXT: return{{$}}
+define void @grow_memory(i32 %n) {
+ call void @llvm.wasm.grow.memory.i32(i32 %n)
+ ret void
+}
diff --git a/test/CodeGen/WebAssembly/memory-addr64.ll b/test/CodeGen/WebAssembly/memory-addr64.ll
new file mode 100644
index 000000000000..5de1f2b11cfd
--- /dev/null
+++ b/test/CodeGen/WebAssembly/memory-addr64.ll
@@ -0,0 +1,27 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that basic memory operations assemble as expected with 64-bit addresses.
+
+target datalayout = "e-p:64:64-i64:64-n32:64-S128"
+target triple = "wasm64-unknown-unknown"
+
+declare i64 @llvm.wasm.memory.size.i64() nounwind readonly
+declare void @llvm.wasm.grow.memory.i64(i64) nounwind
+
+; CHECK-LABEL: memory_size:
+; CHECK-NEXT: .result i64{{$}}
+; CHECK-NEXT: memory_size $push0={{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @memory_size() {
+ %a = call i64 @llvm.wasm.memory.size.i64()
+ ret i64 %a
+}
+
+; CHECK-LABEL: grow_memory:
+; CHECK-NEXT: .param i64{{$}}
+; CHECK: grow_memory $0{{$}}
+; CHECK-NEXT: return{{$}}
+define void @grow_memory(i64 %n) {
+ call void @llvm.wasm.grow.memory.i64(i64 %n)
+ ret void
+}
diff --git a/test/CodeGen/WebAssembly/offset-folding.ll b/test/CodeGen/WebAssembly/offset-folding.ll
new file mode 100644
index 000000000000..2b4e8a90b0f0
--- /dev/null
+++ b/test/CodeGen/WebAssembly/offset-folding.ll
@@ -0,0 +1,48 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that constant offsets can be folded into global addresses.
+
+target datalayout = "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
+@y = global [50 x i32] zeroinitializer
+
+; Test basic constant offsets of both defined and external symbols.
+
+; CHECK-LABEL: test0:
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.const $push0=, x+188{{$}}
+; CHECK=NEXT: return $pop0{{$}}
+define i32* @test0() {
+ ret i32* getelementptr ([0 x i32], [0 x i32]* @x, i32 0, i32 47)
+}
+
+; CHECK-LABEL: test1:
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.const $push0=, y+188{{$}}
+; CHECK=NEXT: return $pop0{{$}}
+define i32* @test1() {
+ ret i32* getelementptr ([50 x i32], [50 x i32]* @y, i32 0, i32 47)
+}
+
+; Test zero offsets.
+
+; CHECK-LABEL: test2:
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.const $push0=, x{{$}}
+; CHECK=NEXT: return $pop0{{$}}
+define i32* @test2() {
+ ret i32* getelementptr ([0 x i32], [0 x i32]* @x, i32 0, i32 0)
+}
+
+; CHECK-LABEL: test3:
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.const $push0=, y{{$}}
+; CHECK=NEXT: return $pop0{{$}}
+define i32* @test3() {
+ ret i32* getelementptr ([50 x i32], [50 x i32]* @y, i32 0, i32 0)
+}
diff --git a/test/CodeGen/WebAssembly/offset.ll b/test/CodeGen/WebAssembly/offset.ll
new file mode 100644
index 000000000000..75a0bc9ab6c6
--- /dev/null
+++ b/test/CodeGen/WebAssembly/offset.ll
@@ -0,0 +1,185 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test constant load and store address offsets.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; With an nuw add, we can fold an offset.
+
+; CHECK-LABEL: load_i32_with_folded_offset:
+; CHECK: i32.load $push0=, 24($0){{$}}
+define i32 @load_i32_with_folded_offset(i32* %p) {
+ %q = ptrtoint i32* %p to i32
+ %r = add nuw i32 %q, 24
+ %s = inttoptr i32 %r to i32*
+ %t = load i32, i32* %s
+ ret i32 %t
+}
+
+; Without nuw, and even with nsw, we can't fold an offset.
+
+; CHECK-LABEL: load_i32_with_unfolded_offset:
+; CHECK: i32.const $push0=, 24{{$}}
+; CHECK: i32.add $push1=, $0, $pop0{{$}}
+; CHECK: i32.load $push2=, 0($pop1){{$}}
+define i32 @load_i32_with_unfolded_offset(i32* %p) {
+ %q = ptrtoint i32* %p to i32
+ %r = add nsw i32 %q, 24
+ %s = inttoptr i32 %r to i32*
+ %t = load i32, i32* %s
+ ret i32 %t
+}
+
+; Same as above but with i64.
+
+; CHECK-LABEL: load_i64_with_folded_offset:
+; CHECK: i64.load $push0=, 24($0){{$}}
+define i64 @load_i64_with_folded_offset(i64* %p) {
+ %q = ptrtoint i64* %p to i32
+ %r = add nuw i32 %q, 24
+ %s = inttoptr i32 %r to i64*
+ %t = load i64, i64* %s
+ ret i64 %t
+}
+
+; Same as above but with i64.
+
+; CHECK-LABEL: load_i64_with_unfolded_offset:
+; CHECK: i32.const $push0=, 24{{$}}
+; CHECK: i32.add $push1=, $0, $pop0{{$}}
+; CHECK: i64.load $push2=, 0($pop1){{$}}
+define i64 @load_i64_with_unfolded_offset(i64* %p) {
+ %q = ptrtoint i64* %p to i32
+ %r = add nsw i32 %q, 24
+ %s = inttoptr i32 %r to i64*
+ %t = load i64, i64* %s
+ ret i64 %t
+}
+
+; Same as above but with store.
+
+; CHECK-LABEL: store_i32_with_folded_offset:
+; CHECK: i32.store $discard=, 24($0), $pop0{{$}}
+define void @store_i32_with_folded_offset(i32* %p) {
+ %q = ptrtoint i32* %p to i32
+ %r = add nuw i32 %q, 24
+ %s = inttoptr i32 %r to i32*
+ store i32 0, i32* %s
+ ret void
+}
+
+; Same as above but with store.
+
+; 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{{$}}
+define void @store_i32_with_unfolded_offset(i32* %p) {
+ %q = ptrtoint i32* %p to i32
+ %r = add nsw i32 %q, 24
+ %s = inttoptr i32 %r to i32*
+ store i32 0, i32* %s
+ ret void
+}
+
+; Same as above but with store with i64.
+
+; CHECK-LABEL: store_i64_with_folded_offset:
+; CHECK: i64.store $discard=, 24($0), $pop0{{$}}
+define void @store_i64_with_folded_offset(i64* %p) {
+ %q = ptrtoint i64* %p to i32
+ %r = add nuw i32 %q, 24
+ %s = inttoptr i32 %r to i64*
+ store i64 0, i64* %s
+ ret void
+}
+
+; Same as above but with store with i64.
+
+; 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{{$}}
+define void @store_i64_with_unfolded_offset(i64* %p) {
+ %q = ptrtoint i64* %p to i32
+ %r = add nsw i32 %q, 24
+ %s = inttoptr i32 %r to i64*
+ store i64 0, i64* %s
+ ret void
+}
+
+; When loading from a fixed address, materialize a zero.
+
+; CHECK-LABEL: load_i32_from_numeric_address
+; CHECK: i32.const $push0=, 0{{$}}
+; CHECK: i32.load $push1=, 42($pop0){{$}}
+define i32 @load_i32_from_numeric_address() {
+ %s = inttoptr i32 42 to i32*
+ %t = load i32, i32* %s
+ ret i32 %t
+}
+
+; CHECK-LABEL: load_i32_from_global_address
+; CHECK: i32.const $push0=, 0{{$}}
+; CHECK: i32.load $push1=, gv($pop0){{$}}
+@gv = global i32 0
+define i32 @load_i32_from_global_address() {
+ %t = load i32, i32* @gv
+ ret i32 %t
+}
+
+; CHECK-LABEL: store_i32_to_numeric_address:
+; CHECK: i32.const $0=, 0{{$}}
+; CHECK: i32.store $discard=, 42($0), $0{{$}}
+define void @store_i32_to_numeric_address() {
+ %s = inttoptr i32 42 to i32*
+ store i32 0, i32* %s
+ ret void
+}
+
+; CHECK-LABEL: store_i32_to_global_address:
+; CHECK: i32.const $0=, 0{{$}}
+; CHECK: i32.store $discard=, gv($0), $0{{$}}
+define void @store_i32_to_global_address() {
+ store i32 0, i32* @gv
+ ret void
+}
+
+; Fold an offset into a sign-extending load.
+
+; CHECK-LABEL: load_i8_s_with_folded_offset:
+; CHECK: i32.load8_s $push0=, 24($0){{$}}
+define i32 @load_i8_s_with_folded_offset(i8* %p) {
+ %q = ptrtoint i8* %p to i32
+ %r = add nuw i32 %q, 24
+ %s = inttoptr i32 %r to i8*
+ %t = load i8, i8* %s
+ %u = sext i8 %t to i32
+ ret i32 %u
+}
+
+; Fold an offset into a zero-extending load.
+
+; CHECK-LABEL: load_i8_u_with_folded_offset:
+; CHECK: i32.load8_u $push0=, 24($0){{$}}
+define i32 @load_i8_u_with_folded_offset(i8* %p) {
+ %q = ptrtoint i8* %p to i32
+ %r = add nuw i32 %q, 24
+ %s = inttoptr i32 %r to i8*
+ %t = load i8, i8* %s
+ %u = zext i8 %t to i32
+ ret i32 %u
+}
+
+; Fold an offset into a truncating store.
+
+; CHECK-LABEL: store_i8_with_folded_offset:
+; CHECK: i32.store8 $discard=, 24($0), $pop0{{$}}
+define void @store_i8_with_folded_offset(i8* %p) {
+ %q = ptrtoint i8* %p to i32
+ %r = add nuw i32 %q, 24
+ %s = inttoptr i32 %r to i8*
+ store i8 0, i8* %s
+ ret void
+}
diff --git a/test/CodeGen/WebAssembly/phi.ll b/test/CodeGen/WebAssembly/phi.ll
new file mode 100644
index 000000000000..bae8a7c9e3b8
--- /dev/null
+++ b/test/CodeGen/WebAssembly/phi.ll
@@ -0,0 +1,47 @@
+; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s
+
+; Test that phis are lowered.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; Basic phi triangle.
+
+; CHECK-LABEL: test0:
+; CHECK: div_s $[[NUM0:[0-9]+]]=, $0, $pop[[NUM1:[0-9]+]]{{$}}
+; CHECK: return $[[NUM0]]{{$}}
+define i32 @test0(i32 %p) {
+entry:
+ %t = icmp slt i32 %p, 0
+ br i1 %t, label %true, label %done
+true:
+ %a = sdiv i32 %p, 3
+ br label %done
+done:
+ %s = phi i32 [ %a, %true ], [ %p, %entry ]
+ ret i32 %s
+}
+
+; Swap phis.
+
+; CHECK-LABEL: test1:
+; CHECK: BB1_1:
+; CHECK: copy_local $[[NUM0:[0-9]+]]=, $[[NUM1:[0-9]+]]{{$}}
+; CHECK: copy_local $[[NUM1]]=, $[[NUM2:[0-9]+]]{{$}}
+; CHECK: copy_local $[[NUM2]]=, $[[NUM0]]{{$}}
+define i32 @test1(i32 %n) {
+entry:
+ br label %loop
+
+loop:
+ %a = phi i32 [ 0, %entry ], [ %b, %loop ]
+ %b = phi i32 [ 1, %entry ], [ %a, %loop ]
+ %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+
+ %i.next = add i32 %i, 1
+ %t = icmp slt i32 %i.next, %n
+ br i1 %t, label %loop, label %exit
+
+exit:
+ ret i32 %a
+}
diff --git a/test/CodeGen/WebAssembly/reg-stackify.ll b/test/CodeGen/WebAssembly/reg-stackify.ll
new file mode 100644
index 000000000000..1c1b1e193f7a
--- /dev/null
+++ b/test/CodeGen/WebAssembly/reg-stackify.ll
@@ -0,0 +1,126 @@
+; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s
+
+; Test the register stackifier pass.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; No because of pointer aliasing.
+
+; CHECK-LABEL: no0:
+; CHECK: return $1{{$}}
+define i32 @no0(i32* %p, i32* %q) {
+ %t = load i32, i32* %q
+ store i32 0, i32* %p
+ ret i32 %t
+}
+
+; No because of side effects.
+
+; CHECK-LABEL: no1:
+; CHECK: return $1{{$}}
+define i32 @no1(i32* %p, i32* dereferenceable(4) %q) {
+ %t = load volatile i32, i32* %q, !invariant.load !0
+ store volatile i32 0, i32* %p
+ ret i32 %t
+}
+
+; Yes because of invariant load and no side effects.
+
+; CHECK-LABEL: yes0:
+; CHECK: return $pop0{{$}}
+define i32 @yes0(i32* %p, i32* dereferenceable(4) %q) {
+ %t = load i32, i32* %q, !invariant.load !0
+ store i32 0, i32* %p
+ ret i32 %t
+}
+
+; Yes because of no intervening side effects.
+
+; CHECK-LABEL: yes1:
+; CHECK: return $pop0{{$}}
+define i32 @yes1(i32* %q) {
+ %t = load volatile i32, i32* %q
+ 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-NEXT: .result i32{{$}}
+; CHECK-NEXT: .local i32, i32{{$}}
+; CHECK-NEXT: i32.const $5=, 2{{$}}
+; CHECK-NEXT: i32.const $4=, 1{{$}}
+; CHECK-NEXT: block BB4_2{{$}}
+; 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, BB4_2{{$}}
+; CHECK-NEXT: i32.const $push8=, 0{{$}}
+; CHECK-NEXT: return $pop8{{$}}
+; CHECK-NEXT: BB4_2:
+; CHECK-NEXT: return $4{{$}}
+define i32 @stack_uses(i32 %x, i32 %y, i32 %z, i32 %w) {
+entry:
+ %c = icmp sle i32 %x, 0
+ %d = icmp sle i32 %y, 1
+ %e = icmp sle i32 %z, 0
+ %f = icmp sle i32 %w, 1
+ %g = xor i1 %c, %d
+ %h = xor i1 %e, %f
+ %i = xor i1 %g, %h
+ br i1 %i, label %true, label %false
+true:
+ ret i32 0
+false:
+ ret i32 1
+}
+
+; Test an interesting case where the load has multiple uses and cannot
+; be trivially stackified.
+
+; CHECK-LABEL: multiple_uses:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK-NEXT: .local i32{{$}}
+; CHECK-NEXT: i32.load $3=, 0($2){{$}}
+; CHECK-NEXT: block BB5_3{{$}}
+; CHECK-NEXT: i32.ge_u $push0=, $3, $1{{$}}
+; CHECK-NEXT: br_if $pop0, BB5_3{{$}}
+; CHECK-NEXT: i32.lt_u $push1=, $3, $0{{$}}
+; CHECK-NEXT: br_if $pop1, BB5_3{{$}}
+; CHECK-NEXT: i32.store $discard=, 0($2), $3{{$}}
+; CHECK-NEXT: BB5_3:
+; CHECK-NEXT: return{{$}}
+define void @multiple_uses(i32* %arg0, i32* %arg1, i32* %arg2) nounwind {
+bb:
+ br label %loop
+
+loop:
+ %tmp7 = load i32, i32* %arg2
+ %tmp8 = inttoptr i32 %tmp7 to i32*
+ %tmp9 = icmp uge i32* %tmp8, %arg1
+ %tmp10 = icmp ult i32* %tmp8, %arg0
+ %tmp11 = or i1 %tmp9, %tmp10
+ br i1 %tmp11, label %back, label %then
+
+then:
+ store i32 %tmp7, i32* %arg2
+ br label %back
+
+back:
+ br i1 undef, label %return, label %loop
+
+return:
+ ret void
+}
+
+!0 = !{}
diff --git a/test/CodeGen/WebAssembly/return-int32.ll b/test/CodeGen/WebAssembly/return-int32.ll
new file mode 100644
index 000000000000..663cef4e459d
--- /dev/null
+++ b/test/CodeGen/WebAssembly/return-int32.ll
@@ -0,0 +1,10 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: return_i32:
+; CHECK: return $0{{$}}
+define i32 @return_i32(i32 %p) {
+ ret i32 %p
+}
diff --git a/test/CodeGen/WebAssembly/return-void.ll b/test/CodeGen/WebAssembly/return-void.ll
new file mode 100644
index 000000000000..4933bfcb87e6
--- /dev/null
+++ b/test/CodeGen/WebAssembly/return-void.ll
@@ -0,0 +1,10 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: return_void:
+; CHECK: return{{$}}
+define void @return_void() {
+ ret void
+}
diff --git a/test/CodeGen/WebAssembly/returned.ll b/test/CodeGen/WebAssembly/returned.ll
new file mode 100644
index 000000000000..e208e198c73d
--- /dev/null
+++ b/test/CodeGen/WebAssembly/returned.ll
@@ -0,0 +1,49 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that the "returned" attribute is optimized effectively.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: _Z3foov:
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.const $push0=, 1{{$}}
+; CHECK-NEXT: {{^}} i32.call $push1=, _Znwm, $pop0{{$}}
+; CHECK-NEXT: {{^}} i32.call $push2=, _ZN5AppleC1Ev, $pop1{{$}}
+; CHECK-NEXT: return $pop2{{$}}
+%class.Apple = type { i8 }
+declare noalias i8* @_Znwm(i32)
+declare %class.Apple* @_ZN5AppleC1Ev(%class.Apple* returned)
+define %class.Apple* @_Z3foov() {
+entry:
+ %call = tail call noalias i8* @_Znwm(i32 1)
+ %0 = bitcast i8* %call to %class.Apple*
+ %call1 = tail call %class.Apple* @_ZN5AppleC1Ev(%class.Apple* %0)
+ ret %class.Apple* %0
+}
+
+; CHECK-LABEL: _Z3barPvS_l:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: {{^}} i32.call $push0=, memcpy, $0, $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+declare i8* @memcpy(i8* returned, i8*, i32)
+define i8* @_Z3barPvS_l(i8* %p, i8* %s, i32 %n) {
+entry:
+ %call = tail call i8* @memcpy(i8* %p, i8* %s, i32 %n)
+ ret i8* %p
+}
+
+; Test that the optimization isn't performed on constant arguments.
+
+; CHECK-LABEL: test_constant_arg:
+; CHECK-NEXT: i32.const $push0=, global{{$}}
+; CHECK-NEXT: {{^}} i32.call $discard=, returns_arg, $pop0{{$}}
+; CHECK-NEXT: return{{$}}
+@global = external global i32
+@addr = global i32* @global
+define void @test_constant_arg() {
+ %call = call i32* @returns_arg(i32* @global)
+ ret void
+}
+declare i32* @returns_arg(i32* returned)
diff --git a/test/CodeGen/WebAssembly/select.ll b/test/CodeGen/WebAssembly/select.ll
new file mode 100644
index 000000000000..1b1d7aed7154
--- /dev/null
+++ b/test/CodeGen/WebAssembly/select.ll
@@ -0,0 +1,135 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+; RUN: llc < %s -asm-verbose=false -fast-isel | FileCheck %s
+
+; Test that wasm select instruction is selected from LLVM select instruction.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+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: return $pop0{{$}}
+define i32 @select_i32_bool(i1 zeroext %a, i32 %b, i32 %c) {
+ %cond = select i1 %a, i32 %b, i32 %c
+ ret i32 %cond
+}
+
+; 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: return $pop0{{$}}
+define i32 @select_i32_eq(i32 %a, i32 %b, i32 %c) {
+ %cmp = icmp eq i32 %a, 0
+ %cond = select i1 %cmp, i32 %b, i32 %c
+ ret i32 %cond
+}
+
+; 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: return $pop0{{$}}
+define i32 @select_i32_ne(i32 %a, i32 %b, i32 %c) {
+ %cmp = icmp ne i32 %a, 0
+ %cond = select i1 %cmp, i32 %b, i32 %c
+ ret i32 %cond
+}
+
+; 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: return $pop0{{$}}
+define i64 @select_i64_bool(i1 zeroext %a, i64 %b, i64 %c) {
+ %cond = select i1 %a, i64 %b, i64 %c
+ ret i64 %cond
+}
+
+; 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: return $pop0{{$}}
+define i64 @select_i64_eq(i32 %a, i64 %b, i64 %c) {
+ %cmp = icmp eq i32 %a, 0
+ %cond = select i1 %cmp, i64 %b, i64 %c
+ ret i64 %cond
+}
+
+; 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: return $pop0{{$}}
+define i64 @select_i64_ne(i32 %a, i64 %b, i64 %c) {
+ %cmp = icmp ne i32 %a, 0
+ %cond = select i1 %cmp, i64 %b, i64 %c
+ ret i64 %cond
+}
+
+; 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: return $pop0{{$}}
+define float @select_f32_bool(i1 zeroext %a, float %b, float %c) {
+ %cond = select i1 %a, float %b, float %c
+ ret float %cond
+}
+
+; 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: return $pop0{{$}}
+define float @select_f32_eq(i32 %a, float %b, float %c) {
+ %cmp = icmp eq i32 %a, 0
+ %cond = select i1 %cmp, float %b, float %c
+ ret float %cond
+}
+
+; 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: return $pop0{{$}}
+define float @select_f32_ne(i32 %a, float %b, float %c) {
+ %cmp = icmp ne i32 %a, 0
+ %cond = select i1 %cmp, float %b, float %c
+ ret float %cond
+}
+
+; 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: return $pop0{{$}}
+define double @select_f64_bool(i1 zeroext %a, double %b, double %c) {
+ %cond = select i1 %a, double %b, double %c
+ ret double %cond
+}
+
+; 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: return $pop0{{$}}
+define double @select_f64_eq(i32 %a, double %b, double %c) {
+ %cmp = icmp eq i32 %a, 0
+ %cond = select i1 %cmp, double %b, double %c
+ ret double %cond
+}
+
+; 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: return $pop0{{$}}
+define double @select_f64_ne(i32 %a, double %b, double %c) {
+ %cmp = icmp ne i32 %a, 0
+ %cond = select i1 %cmp, double %b, double %c
+ ret double %cond
+}
diff --git a/test/CodeGen/WebAssembly/signext-zeroext.ll b/test/CodeGen/WebAssembly/signext-zeroext.ll
new file mode 100644
index 000000000000..40d49af0ccc7
--- /dev/null
+++ b/test/CodeGen/WebAssembly/signext-zeroext.ll
@@ -0,0 +1,60 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test zeroext and signext ABI keywords
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+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: return $pop[[NUM3]]{{$}}
+define signext i8 @z2s_func(i8 zeroext %t) {
+ ret i8 %t
+}
+
+; CHECK-LABEL: s2z_func:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.const $push[[NUM0:[0-9]+]]=, 255{{$}}
+; CHECK-NEXT: i32.and $push[[NUM1:[0-9]+]]=, $0, $pop[[NUM0]]{{$}}
+; CHECK-NEXT: return $pop[[NUM1]]{{$}}
+define zeroext i8 @s2z_func(i8 signext %t) {
+ ret i8 %t
+}
+
+; CHECK-LABEL: z2s_call:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: i32.const $push[[NUM0:[0-9]+]]=, 255{{$}}
+; CHECK-NEXT: i32.and $push[[NUM1:[0-9]+]]=, $0, $pop[[NUM0]]{{$}}
+; CHECK-NEXT: call $push[[NUM2:[0-9]+]]=, z2s_func, $pop[[NUM1]]{{$}}
+; CHECK-NEXT: return $pop[[NUM2]]{{$}}
+define i32 @z2s_call(i32 %t) {
+ %s = trunc i32 %t to i8
+ %u = call signext i8 @z2s_func(i8 zeroext %s)
+ %v = sext i8 %u to i32
+ ret i32 %v
+}
+
+; 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: call $push[[NUM3:[0-9]]]=, s2z_func, $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: return $pop[[NUM5]]{{$}}
+define i32 @s2z_call(i32 %t) {
+ %s = trunc i32 %t to i8
+ %u = call zeroext i8 @s2z_func(i8 signext %s)
+ %v = sext i8 %u to i32
+ ret i32 %v
+}
diff --git a/test/CodeGen/WebAssembly/store-results.ll b/test/CodeGen/WebAssembly/store-results.ll
new file mode 100644
index 000000000000..73479e544db9
--- /dev/null
+++ b/test/CodeGen/WebAssembly/store-results.ll
@@ -0,0 +1,61 @@
+; RUN: llc < %s -asm-verbose=false | 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.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: single_block:
+; 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]]{{$}}
+define i32 @single_block(i32* %p) {
+entry:
+ store i32 0, i32* %p
+ ret i32 0
+}
+
+; Test interesting corner cases for wasm-store-results, in which the operand of
+; a store ends up getting used by a phi, which needs special handling in the
+; dominance test, since phis use their operands on their incoming edges.
+
+%class.Vec3 = type { float, float, float }
+
+@pos = global %class.Vec3 zeroinitializer, align 4
+
+; CHECK-LABEL: foo:
+; CHECK: i32.store $discard=, pos($0), $0{{$}}
+define void @foo() {
+for.body.i:
+ br label %for.body5.i
+
+for.body5.i:
+ %i.0168.i = phi i32 [ 0, %for.body.i ], [ %inc.i, %for.body5.i ]
+ %conv6.i = sitofp i32 %i.0168.i to float
+ store volatile float 0.0, float* getelementptr inbounds (%class.Vec3, %class.Vec3* @pos, i32 0, i32 0)
+ %inc.i = add nuw nsw i32 %i.0168.i, 1
+ %exitcond.i = icmp eq i32 %inc.i, 256
+ br i1 %exitcond.i, label %for.cond.cleanup4.i, label %for.body5.i
+
+for.cond.cleanup4.i:
+ ret void
+}
+
+; CHECK-LABEL: bar:
+; CHECK: i32.store $discard=, pos($0), $0{{$}}
+define void @bar() {
+for.body.i:
+ br label %for.body5.i
+
+for.body5.i:
+ %i.0168.i = phi float [ 0.0, %for.body.i ], [ %inc.i, %for.body5.i ]
+ store volatile float 0.0, float* getelementptr inbounds (%class.Vec3, %class.Vec3* @pos, i32 0, i32 0)
+ %inc.i = fadd float %i.0168.i, 1.0
+ %exitcond.i = fcmp oeq float %inc.i, 256.0
+ br i1 %exitcond.i, label %for.cond.cleanup4.i, label %for.body5.i
+
+for.cond.cleanup4.i:
+ ret void
+}
diff --git a/test/CodeGen/WebAssembly/store-trunc.ll b/test/CodeGen/WebAssembly/store-trunc.ll
new file mode 100644
index 000000000000..c12b716dfd59
--- /dev/null
+++ b/test/CodeGen/WebAssembly/store-trunc.ll
@@ -0,0 +1,46 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that truncating stores are assembled properly.
+
+target datalayout = "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{{$}}
+define void @trunc_i8_i32(i8 *%p, i32 %v) {
+ %t = trunc i32 %v to i8
+ store i8 %t, i8* %p
+ ret void
+}
+
+; CHECK-LABEL: trunc_i16_i32:
+; CHECK: i32.store16 $discard=, 0($0), $1{{$}}
+define void @trunc_i16_i32(i16 *%p, i32 %v) {
+ %t = trunc i32 %v to i16
+ store i16 %t, i16* %p
+ ret void
+}
+
+; CHECK-LABEL: trunc_i8_i64:
+; CHECK: i64.store8 $discard=, 0($0), $1{{$}}
+define void @trunc_i8_i64(i8 *%p, i64 %v) {
+ %t = trunc i64 %v to i8
+ store i8 %t, i8* %p
+ ret void
+}
+
+; CHECK-LABEL: trunc_i16_i64:
+; CHECK: i64.store16 $discard=, 0($0), $1{{$}}
+define void @trunc_i16_i64(i16 *%p, i64 %v) {
+ %t = trunc i64 %v to i16
+ store i16 %t, i16* %p
+ ret void
+}
+
+; CHECK-LABEL: trunc_i32_i64:
+; CHECK: i64.store32 $discard=, 0($0), $1{{$}}
+define void @trunc_i32_i64(i32 *%p, i64 %v) {
+ %t = trunc i64 %v to i32
+ store i32 %t, i32* %p
+ ret void
+}
diff --git a/test/CodeGen/WebAssembly/store.ll b/test/CodeGen/WebAssembly/store.ll
new file mode 100644
index 000000000000..442caedef3a7
--- /dev/null
+++ b/test/CodeGen/WebAssembly/store.ll
@@ -0,0 +1,42 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that basic stores are assembled properly.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: sti32:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: i32.store $discard=, 0($0), $1{{$}}
+; CHECK-NEXT: return{{$}}
+define void @sti32(i32 *%p, i32 %v) {
+ store i32 %v, i32* %p
+ ret void
+}
+
+; CHECK-LABEL: sti64:
+; CHECK-NEXT: .param i32, i64{{$}}
+; CHECK-NEXT: i64.store $discard=, 0($0), $1{{$}}
+; CHECK-NEXT: return{{$}}
+define void @sti64(i64 *%p, i64 %v) {
+ store i64 %v, i64* %p
+ ret void
+}
+
+; CHECK-LABEL: stf32:
+; CHECK-NEXT: .param i32, f32{{$}}
+; CHECK-NEXT: f32.store $discard=, 0($0), $1{{$}}
+; CHECK-NEXT: return{{$}}
+define void @stf32(float *%p, float %v) {
+ store float %v, float* %p
+ ret void
+}
+
+; CHECK-LABEL: stf64:
+; CHECK-NEXT: .param i32, f64{{$}}
+; CHECK-NEXT: f64.store $discard=, 0($0), $1{{$}}
+; CHECK-NEXT: return{{$}}
+define void @stf64(double *%p, double %v) {
+ store double %v, double* %p
+ ret void
+}
diff --git a/test/CodeGen/WebAssembly/switch.ll b/test/CodeGen/WebAssembly/switch.ll
new file mode 100644
index 000000000000..7f6f6efff7d6
--- /dev/null
+++ b/test/CodeGen/WebAssembly/switch.ll
@@ -0,0 +1,174 @@
+; RUN: llc < %s -asm-verbose=false -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.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+declare void @foo0()
+declare void @foo1()
+declare void @foo2()
+declare void @foo3()
+declare void @foo4()
+declare void @foo5()
+
+; CHECK-LABEL: bar32:
+; CHECK: block BB0_8{{$}}
+; CHECK: block BB0_7{{$}}
+; CHECK: block BB0_6{{$}}
+; CHECK: block BB0_5{{$}}
+; CHECK: block BB0_4{{$}}
+; CHECK: block BB0_3{{$}}
+; CHECK: block BB0_2{{$}}
+; CHECK: tableswitch {{[^,]*}}, BB0_2, BB0_2, BB0_2, BB0_2, BB0_2, BB0_2, BB0_2, BB0_2, BB0_3, BB0_3, BB0_3, BB0_3, BB0_3, BB0_3, BB0_3, BB0_3, BB0_4, BB0_4, BB0_4, BB0_4, BB0_4, BB0_4, BB0_5, BB0_6, BB0_7{{$}}
+; CHECK: BB0_2:
+; CHECK: call foo0
+; CHECK: BB0_3:
+; CHECK: call foo1
+; CHECK: BB0_4:
+; CHECK: call foo2
+; CHECK: BB0_5:
+; CHECK: call foo3
+; CHECK: BB0_6:
+; CHECK: call foo4
+; CHECK: BB0_7:
+; CHECK: call foo5
+; CHECK: BB0_8:
+; CHECK: return{{$}}
+define void @bar32(i32 %n) {
+entry:
+ switch i32 %n, label %sw.epilog [
+ i32 0, label %sw.bb
+ i32 1, label %sw.bb
+ i32 2, label %sw.bb
+ i32 3, label %sw.bb
+ i32 4, label %sw.bb
+ i32 5, label %sw.bb
+ i32 6, label %sw.bb
+ i32 7, label %sw.bb.1
+ i32 8, label %sw.bb.1
+ i32 9, label %sw.bb.1
+ i32 10, label %sw.bb.1
+ i32 11, label %sw.bb.1
+ i32 12, label %sw.bb.1
+ i32 13, label %sw.bb.1
+ i32 14, label %sw.bb.1
+ i32 15, label %sw.bb.2
+ i32 16, label %sw.bb.2
+ i32 17, label %sw.bb.2
+ i32 18, label %sw.bb.2
+ i32 19, label %sw.bb.2
+ i32 20, label %sw.bb.2
+ i32 21, label %sw.bb.3
+ i32 22, label %sw.bb.4
+ i32 23, label %sw.bb.5
+ ]
+
+sw.bb: ; preds = %entry, %entry, %entry, %entry, %entry, %entry, %entry
+ tail call void @foo0()
+ br label %sw.epilog
+
+sw.bb.1: ; preds = %entry, %entry, %entry, %entry, %entry, %entry, %entry, %entry
+ tail call void @foo1()
+ br label %sw.epilog
+
+sw.bb.2: ; preds = %entry, %entry, %entry, %entry, %entry, %entry
+ tail call void @foo2()
+ br label %sw.epilog
+
+sw.bb.3: ; preds = %entry
+ tail call void @foo3()
+ br label %sw.epilog
+
+sw.bb.4: ; preds = %entry
+ tail call void @foo4()
+ br label %sw.epilog
+
+sw.bb.5: ; preds = %entry
+ tail call void @foo5()
+ br label %sw.epilog
+
+sw.epilog: ; preds = %entry, %sw.bb.5, %sw.bb.4, %sw.bb.3, %sw.bb.2, %sw.bb.1, %sw.bb
+ ret void
+}
+
+; CHECK-LABEL: bar64:
+; CHECK: block BB1_8{{$}}
+; CHECK: block BB1_7{{$}}
+; CHECK: block BB1_6{{$}}
+; CHECK: block BB1_5{{$}}
+; CHECK: block BB1_4{{$}}
+; CHECK: block BB1_3{{$}}
+; CHECK: block BB1_2{{$}}
+; CHECK: tableswitch {{[^,]*}}, BB1_2, BB1_2, BB1_2, BB1_2, BB1_2, BB1_2, BB1_2, BB1_2, BB1_3, BB1_3, BB1_3, BB1_3, BB1_3, BB1_3, BB1_3, BB1_3, BB1_4, BB1_4, BB1_4, BB1_4, BB1_4, BB1_4, BB1_5, BB1_6, BB1_7{{$}}
+; CHECK: BB1_2:
+; CHECK: call foo0
+; CHECK: BB1_3:
+; CHECK: call foo1
+; CHECK: BB1_4:
+; CHECK: call foo2
+; CHECK: BB1_5:
+; CHECK: call foo3
+; CHECK: BB1_6:
+; CHECK: call foo4
+; CHECK: BB1_7:
+; CHECK: call foo5
+; CHECK: BB1_8:
+; CHECK: return{{$}}
+define void @bar64(i64 %n) {
+entry:
+ switch i64 %n, label %sw.epilog [
+ i64 0, label %sw.bb
+ i64 1, label %sw.bb
+ i64 2, label %sw.bb
+ i64 3, label %sw.bb
+ i64 4, label %sw.bb
+ i64 5, label %sw.bb
+ i64 6, label %sw.bb
+ i64 7, label %sw.bb.1
+ i64 8, label %sw.bb.1
+ i64 9, label %sw.bb.1
+ i64 10, label %sw.bb.1
+ i64 11, label %sw.bb.1
+ i64 12, label %sw.bb.1
+ i64 13, label %sw.bb.1
+ i64 14, label %sw.bb.1
+ i64 15, label %sw.bb.2
+ i64 16, label %sw.bb.2
+ i64 17, label %sw.bb.2
+ i64 18, label %sw.bb.2
+ i64 19, label %sw.bb.2
+ i64 20, label %sw.bb.2
+ i64 21, label %sw.bb.3
+ i64 22, label %sw.bb.4
+ i64 23, label %sw.bb.5
+ ]
+
+sw.bb: ; preds = %entry, %entry, %entry, %entry, %entry, %entry, %entry
+ tail call void @foo0()
+ br label %sw.epilog
+
+sw.bb.1: ; preds = %entry, %entry, %entry, %entry, %entry, %entry, %entry, %entry
+ tail call void @foo1()
+ br label %sw.epilog
+
+sw.bb.2: ; preds = %entry, %entry, %entry, %entry, %entry, %entry
+ tail call void @foo2()
+ br label %sw.epilog
+
+sw.bb.3: ; preds = %entry
+ tail call void @foo3()
+ br label %sw.epilog
+
+sw.bb.4: ; preds = %entry
+ tail call void @foo4()
+ br label %sw.epilog
+
+sw.bb.5: ; preds = %entry
+ tail call void @foo5()
+ br label %sw.epilog
+
+sw.epilog: ; preds = %entry, %sw.bb.5, %sw.bb.4, %sw.bb.3, %sw.bb.2, %sw.bb.1, %sw.bb
+ ret void
+}
diff --git a/test/CodeGen/WebAssembly/unreachable.ll b/test/CodeGen/WebAssembly/unreachable.ll
new file mode 100644
index 000000000000..414767e5c35d
--- /dev/null
+++ b/test/CodeGen/WebAssembly/unreachable.ll
@@ -0,0 +1,34 @@
+; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s
+; RUN: llc < %s -asm-verbose=false -fast-isel -verify-machineinstrs | FileCheck %s
+
+; Test that LLVM unreachable instruction and trap intrinsic are lowered to
+; wasm unreachable
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+declare void @llvm.trap()
+declare void @llvm.debugtrap()
+declare void @abort()
+
+; CHECK-LABEL: f1:
+; CHECK: call abort
+; CHECK: unreachable
+define i32 @f1() {
+ call void @abort()
+ unreachable
+}
+
+; CHECK-LABEL: f2:
+; CHECK: unreachable
+define void @f2() {
+ call void @llvm.trap()
+ ret void
+}
+
+; CHECK-LABEL: f3:
+; CHECK: unreachable
+define void @f3() {
+ call void @llvm.debugtrap()
+ ret void
+}
diff --git a/test/CodeGen/WebAssembly/unused-argument.ll b/test/CodeGen/WebAssembly/unused-argument.ll
new file mode 100644
index 000000000000..e7851b216cb4
--- /dev/null
+++ b/test/CodeGen/WebAssembly/unused-argument.ll
@@ -0,0 +1,31 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Make sure that argument offsets are correct even if some arguments are unused.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; CHECK-LABEL: unused_first:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: return $1{{$}}
+define i32 @unused_first(i32 %x, i32 %y) {
+ ret i32 %y
+}
+
+; CHECK-LABEL: unused_second:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .result i32{{$}}
+; CHECK-NEXT: return $0{{$}}
+define i32 @unused_second(i32 %x, i32 %y) {
+ ret i32 %x
+}
+
+; CHECK-LABEL: call_something:
+; CHECK-NEXT: {{^}} i32.call $discard=, return_something{{$}}
+; CHECK-NEXT: return{{$}}
+declare i32 @return_something()
+define void @call_something() {
+ call i32 @return_something()
+ ret void
+}
diff --git a/test/CodeGen/WebAssembly/userstack.ll b/test/CodeGen/WebAssembly/userstack.ll
new file mode 100644
index 000000000000..6e01e36cf9fa
--- /dev/null
+++ b/test/CodeGen/WebAssembly/userstack.ll
@@ -0,0 +1,81 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+; RUN: llc < %s -asm-verbose=false -fast-isel | FileCheck %s
+
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+; 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]]
+ %retval = alloca i32
+ ; CHECK: i32.const $push[[L3:.+]]=, 0
+ ; CHECK: i32.store {{.*}}=, 12([[SP]]), $pop[[L3]]
+ 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]]
+ ret void
+}
+
+; CHECK-LABEL: alloca3264:
+; CHECK: .local i32, i32, i32, 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]]
+ %r1 = alloca i32
+ %r2 = alloca double
+ ; CHECK: i32.const $push[[L3:.+]]=, 0
+ ; CHECK: i32.store {{.*}}=, 12([[SP]]), $pop[[L3]]
+ store i32 0, i32* %r1
+ ; CHECK: i64.const $push[[L4:.+]]=, 0
+ ; CHECK: i64.store {{.*}}=, 0([[SP]]), $pop[[L4]]
+ 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]]
+ ret void
+}
+
+; CHECK-LABEL: allocarray:
+; CHECK: .local i32, i32, i32, i32, i32, 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
+ 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
+ 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]]
+ ret void
+}
+
+define void @dynamic_alloca(i32 %alloc) {
+ ; TODO: Support frame pointers
+ ;%r = alloca i32, i32 %alloc
+ ;store i32 0, i32* %r
+ ret void
+}
+; TODO: test aligned alloc
diff --git a/test/CodeGen/WebAssembly/varargs.ll b/test/CodeGen/WebAssembly/varargs.ll
new file mode 100644
index 000000000000..c564d9420742
--- /dev/null
+++ b/test/CodeGen/WebAssembly/varargs.ll
@@ -0,0 +1,123 @@
+; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s
+
+; Test varargs constructs.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+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
+;}
+
+; Test va_end.
+
+; CHECK-LABEL: end:
+; CHECK-NEXT: .param i32{{$}}
+; CHECK-NEXT: return{{$}}
+define void @end(i8** %ap) {
+entry:
+ %0 = bitcast i8** %ap to i8*
+ call void @llvm.va_end(i8* %0)
+ ret void
+}
+
+; Test va_copy.
+
+; 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: return{{$}}
+define void @copy(i8** %ap, i8** %bp) {
+entry:
+ %0 = bitcast i8** %ap to i8*
+ %1 = bitcast i8** %bp to i8*
+ call void @llvm.va_copy(i8* %0, i8* %1)
+ ret void
+}
+
+; Test va_arg with an i8 argument.
+
+; CHECK-LABEL: arg_i8:
+; 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{{$}}
+define i8 @arg_i8(i8** %ap) {
+entry:
+ %t = va_arg i8** %ap, i8
+ ret i8 %t
+}
+
+; Test va_arg with an i32 argument.
+
+; CHECK-LABEL: arg_i32:
+; 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{{$}}
+define i32 @arg_i32(i8** %ap) {
+entry:
+ %t = va_arg i8** %ap, i32
+ ret i32 %t
+}
+
+; Test va_arg with an i128 argument.
+
+; CHECK-LABEL: arg_i128:
+; CHECK-NEXT: .param i32, i32{{$}}
+; CHECK-NEXT: .local
+; CHECK: i32.and
+; CHECK: i64.load
+; CHECK: i64.load
+; CHECK: return{{$}}
+define i128 @arg_i128(i8** %ap) {
+entry:
+ %t = va_arg i8** %ap, i128
+ ret i128 %t
+}
+
+; Test a varargs call with no actual arguments.
+
+declare void @callee(...)
+
+; CHECK-LABEL: caller_none:
+; CHECK-NEXT: call callee{{$}}
+; CHECK-NEXT: return{{$}}
+define void @caller_none() {
+ call void (...) @callee()
+ ret void
+}
+
+; CHECK-LABEL: caller_some
+define void @caller_some() {
+ ; TODO: Fix interaction between register coalescer and reg stackifier,
+ ; or disable coalescer.
+ ;call void (...) @callee(i32 0, double 2.0)
+ ret void
+}
+
+declare void @llvm.va_start(i8*)
+declare void @llvm.va_end(i8*)
+declare void @llvm.va_copy(i8*, i8*)
diff --git a/test/CodeGen/WebAssembly/vtable.ll b/test/CodeGen/WebAssembly/vtable.ll
new file mode 100644
index 000000000000..38298bc474b5
--- /dev/null
+++ b/test/CodeGen/WebAssembly/vtable.ll
@@ -0,0 +1,171 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s --check-prefix=TYPEINFONAME
+; RUN: llc < %s -asm-verbose=false | FileCheck %s --check-prefix=VTABLE
+; RUN: llc < %s -asm-verbose=false | FileCheck %s --check-prefix=TYPEINFO
+
+; Test that simple vtables assemble as expected.
+;
+; The class hierarchy is:
+; struct A;
+; struct B : public A;
+; struct C : public A;
+; struct D : public B;
+; Each with a virtual dtor and method foo.
+
+target datalayout = "e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+%struct.A = type { i32 (...)** }
+%struct.B = type { %struct.A }
+%struct.C = type { %struct.A }
+%struct.D = type { %struct.B }
+
+@_ZTVN10__cxxabiv117__class_type_infoE = external global i8*
+@_ZTVN10__cxxabiv120__si_class_type_infoE = external global i8*
+
+; TYPEINFONAME-LABEL: _ZTS1A:
+; TYPEINFONAME-NEXT: .asciz "1A"
+@_ZTS1A = constant [3 x i8] c"1A\00"
+; TYPEINFONAME-LABEL: _ZTS1B:
+; TYPEINFONAME-NEXT: .asciz "1B"
+@_ZTS1B = constant [3 x i8] c"1B\00"
+; TYPEINFONAME-LABEL: _ZTS1C:
+; TYPEINFONAME-NEXT: .asciz "1C"
+@_ZTS1C = constant [3 x i8] c"1C\00"
+; TYPEINFONAME-LABEL: _ZTS1D:
+; TYPEINFONAME-NEXT: .asciz "1D"
+@_ZTS1D = constant [3 x i8] c"1D\00"
+
+; VTABLE: .type _ZTV1A,@object
+; VTABLE-NEXT: .section .data.rel.ro,"aw",@progbits
+; VTABLE-NEXT: .globl _ZTV1A
+; VTABLE-LABEL: _ZTV1A:
+; VTABLE-NEXT: .int32 0
+; VTABLE-NEXT: .int32 _ZTI1A
+; VTABLE-NEXT: .int32 _ZN1AD2Ev
+; VTABLE-NEXT: .int32 _ZN1AD0Ev
+; VTABLE-NEXT: .int32 _ZN1A3fooEv
+; VTABLE-NEXT: .size _ZTV1A, 20
+@_ZTV1A = constant [5 x i8*] [i8* null, i8* bitcast ({ i8*, i8* }* @_ZTI1A to i8*), i8* bitcast (%struct.A* (%struct.A*)* @_ZN1AD2Ev to i8*), i8* bitcast (void (%struct.A*)* @_ZN1AD0Ev to i8*), i8* bitcast (void (%struct.A*)* @_ZN1A3fooEv to i8*)], align 4
+; VTABLE: .type _ZTV1B,@object
+; VTABLE-NEXT: .globl _ZTV1B
+; VTABLE-LABEL: _ZTV1B:
+; VTABLE-NEXT: .int32 0
+; VTABLE-NEXT: .int32 _ZTI1B
+; VTABLE-NEXT: .int32 _ZN1AD2Ev
+; VTABLE-NEXT: .int32 _ZN1BD0Ev
+; VTABLE-NEXT: .int32 _ZN1B3fooEv
+; VTABLE-NEXT: .size _ZTV1B, 20
+@_ZTV1B = constant [5 x i8*] [i8* null, i8* bitcast ({ i8*, i8*, i8* }* @_ZTI1B to i8*), i8* bitcast (%struct.A* (%struct.A*)* @_ZN1AD2Ev to i8*), i8* bitcast (void (%struct.B*)* @_ZN1BD0Ev to i8*), i8* bitcast (void (%struct.B*)* @_ZN1B3fooEv to i8*)], align 4
+; VTABLE: .type _ZTV1C,@object
+; VTABLE-NEXT: .globl _ZTV1C
+; VTABLE-LABEL: _ZTV1C:
+; VTABLE-NEXT: .int32 0
+; VTABLE-NEXT: .int32 _ZTI1C
+; VTABLE-NEXT: .int32 _ZN1AD2Ev
+; VTABLE-NEXT: .int32 _ZN1CD0Ev
+; VTABLE-NEXT: .int32 _ZN1C3fooEv
+; VTABLE-NEXT: .size _ZTV1C, 20
+@_ZTV1C = constant [5 x i8*] [i8* null, i8* bitcast ({ i8*, i8*, i8* }* @_ZTI1C to i8*), i8* bitcast (%struct.A* (%struct.A*)* @_ZN1AD2Ev to i8*), i8* bitcast (void (%struct.C*)* @_ZN1CD0Ev to i8*), i8* bitcast (void (%struct.C*)* @_ZN1C3fooEv to i8*)], align 4
+; VTABLE: .type _ZTV1D,@object
+; VTABLE-NEXT: .globl _ZTV1D
+; VTABLE-LABEL: _ZTV1D:
+; VTABLE-NEXT: .int32 0
+; VTABLE-NEXT: .int32 _ZTI1D
+; VTABLE-NEXT: .int32 _ZN1AD2Ev
+; VTABLE-NEXT: .int32 _ZN1DD0Ev
+; VTABLE-NEXT: .int32 _ZN1D3fooEv
+; VTABLE-NEXT: .size _ZTV1D, 20
+@_ZTV1D = constant [5 x i8*] [i8* null, i8* bitcast ({ i8*, i8*, i8* }* @_ZTI1D to i8*), i8* bitcast (%struct.A* (%struct.A*)* @_ZN1AD2Ev to i8*), i8* bitcast (void (%struct.D*)* @_ZN1DD0Ev to i8*), i8* bitcast (void (%struct.D*)* @_ZN1D3fooEv to i8*)], align 4
+
+; TYPEINFO: .type _ZTI1A,@object
+; TYPEINFO: .globl _ZTI1A
+; TYPEINFO-LABEL: _ZTI1A:
+; TYPEINFO-NEXT: .int32 _ZTVN10__cxxabiv117__class_type_infoE+8
+; TYPEINFO-NEXT: .int32 _ZTS1A
+; TYPEINFO-NEXT: .size _ZTI1A, 8
+@_ZTI1A = constant { i8*, i8* } { i8* bitcast (i8** getelementptr inbounds (i8*, i8** @_ZTVN10__cxxabiv117__class_type_infoE, i32 2) to i8*), i8* getelementptr inbounds ([3 x i8], [3 x i8]* @_ZTS1A, i32 0, i32 0) }
+; TYPEINFO: .type _ZTI1B,@object
+; TYPEINFO: .globl _ZTI1B
+; TYPEINFO-LABEL: _ZTI1B:
+; TYPEINFO-NEXT: .int32 _ZTVN10__cxxabiv120__si_class_type_infoE+8
+; TYPEINFO-NEXT: .int32 _ZTS1B
+; TYPEINFO-NEXT: .int32 _ZTI1A
+; TYPEINFO-NEXT: .size _ZTI1B, 12
+@_ZTI1B = constant { i8*, i8*, i8* } { i8* bitcast (i8** getelementptr inbounds (i8*, i8** @_ZTVN10__cxxabiv120__si_class_type_infoE, i32 2) to i8*), i8* getelementptr inbounds ([3 x i8], [3 x i8]* @_ZTS1B, i32 0, i32 0), i8* bitcast ({ i8*, i8* }* @_ZTI1A to i8*) }
+; TYPEINFO: .type _ZTI1C,@object
+; TYPEINFO: .globl _ZTI1C
+; TYPEINFO-LABEL: _ZTI1C:
+; TYPEINFO-NEXT: .int32 _ZTVN10__cxxabiv120__si_class_type_infoE+8
+; TYPEINFO-NEXT: .int32 _ZTS1C
+; TYPEINFO-NEXT: .int32 _ZTI1A
+; TYPEINFO-NEXT: .size _ZTI1C, 12
+@_ZTI1C = constant { i8*, i8*, i8* } { i8* bitcast (i8** getelementptr inbounds (i8*, i8** @_ZTVN10__cxxabiv120__si_class_type_infoE, i32 2) to i8*), i8* getelementptr inbounds ([3 x i8], [3 x i8]* @_ZTS1C, i32 0, i32 0), i8* bitcast ({ i8*, i8* }* @_ZTI1A to i8*) }
+; TYPEINFO: .type _ZTI1D,@object
+; TYPEINFO: .globl _ZTI1D
+; TYPEINFO-LABEL: _ZTI1D:
+; TYPEINFO-NEXT: .int32 _ZTVN10__cxxabiv120__si_class_type_infoE+8
+; TYPEINFO-NEXT: .int32 _ZTS1D
+; TYPEINFO-NEXT: .int32 _ZTI1B
+; TYPEINFO-NEXT: .size _ZTI1D, 12
+@_ZTI1D = constant { i8*, i8*, i8* } { i8* bitcast (i8** getelementptr inbounds (i8*, i8** @_ZTVN10__cxxabiv120__si_class_type_infoE, i32 2) to i8*), i8* getelementptr inbounds ([3 x i8], [3 x i8]* @_ZTS1D, i32 0, i32 0), i8* bitcast ({ i8*, i8*, i8* }* @_ZTI1B to i8*) }
+
+@g = global i32 0, align 4
+
+define void @_ZN1A3fooEv(%struct.A* %this) {
+entry:
+ store i32 2, i32* @g, align 4
+ ret void
+}
+
+define void @_ZN1B3fooEv(%struct.B* %this) {
+entry:
+ store i32 4, i32* @g, align 4
+ ret void
+}
+
+define void @_ZN1C3fooEv(%struct.C* %this) {
+entry:
+ store i32 6, i32* @g, align 4
+ ret void
+}
+
+define void @_ZN1D3fooEv(%struct.D* %this) {
+entry:
+ store i32 8, i32* @g, align 4
+ ret void
+}
+
+define linkonce_odr void @_ZN1AD0Ev(%struct.A* %this) {
+entry:
+ %0 = bitcast %struct.A* %this to i8*
+ tail call void @_ZdlPv(i8* %0)
+ ret void
+}
+
+define linkonce_odr void @_ZN1BD0Ev(%struct.B* %this) {
+entry:
+ %0 = bitcast %struct.B* %this to i8*
+ tail call void @_ZdlPv(i8* %0)
+ ret void
+}
+
+define linkonce_odr void @_ZN1CD0Ev(%struct.C* %this) {
+entry:
+ %0 = bitcast %struct.C* %this to i8*
+ tail call void @_ZdlPv(i8* %0)
+ ret void
+}
+
+define linkonce_odr %struct.A* @_ZN1AD2Ev(%struct.A* returned %this) {
+entry:
+ ret %struct.A* %this
+}
+
+define linkonce_odr void @_ZN1DD0Ev(%struct.D* %this) {
+entry:
+ %0 = bitcast %struct.D* %this to i8*
+ tail call void @_ZdlPv(i8* %0)
+ ret void
+}
+
+declare void @_ZdlPv(i8*)