diff options
Diffstat (limited to 'test/CodeGen/WebAssembly')
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*) |
