diff --git a/llvm/test/CodeGen/WebAssembly/tailcall.ll b/llvm/test/CodeGen/WebAssembly/tailcall.ll index 07cdea1ec9b0f..34dd0a9a424b6 100644 --- a/llvm/test/CodeGen/WebAssembly/tailcall.ll +++ b/llvm/test/CodeGen/WebAssembly/tailcall.ll @@ -1,5 +1,6 @@ -; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mcpu=mvp -mattr=+tail-call | FileCheck --check-prefixes=CHECK,SLOW %s -; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -fast-isel -mcpu=mvp -mattr=+tail-call | FileCheck --check-prefixes=CHECK,FAST %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mcpu=mvp -mattr=+tail-call | FileCheck --check-prefixes=CHECK,SLOW %s +; RUN: llc < %s -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -fast-isel -mcpu=mvp -mattr=+tail-call | FileCheck --check-prefixes=CHECK,FAST %s ; RUN: llc < %s --filetype=obj -mattr=+tail-call | obj2yaml | FileCheck --check-prefix=YAML %s ; Test that the tail calls lower correctly @@ -10,101 +11,177 @@ target triple = "wasm32-unknown-unknown" declare i1 @foo(i1) declare i1 @bar(i1) -; CHECK-LABEL: recursive_notail_nullary: -; CHECK: {{^}} call recursive_notail_nullary{{$}} -; CHECK-NEXT: return define void @recursive_notail_nullary() { +; CHECK-LABEL: recursive_notail_nullary: +; CHECK: .functype recursive_notail_nullary () -> () +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: call recursive_notail_nullary +; CHECK-NEXT: return notail call void @recursive_notail_nullary() ret void } -; CHECK-LABEL: recursive_musttail_nullary: -; CHECK: return_call recursive_musttail_nullary{{$}} define void @recursive_musttail_nullary() { +; CHECK-LABEL: recursive_musttail_nullary: +; CHECK: .functype recursive_musttail_nullary () -> () +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: return_call recursive_musttail_nullary musttail call void @recursive_musttail_nullary() ret void } - -; CHECK-LABEL: recursive_tail_nullary: -; SLOW: return_call recursive_tail_nullary{{$}} -; FAST: {{^}} call recursive_tail_nullary{{$}} -; FAST-NEXT: return{{$}} define void @recursive_tail_nullary() { +; SLOW-LABEL: recursive_tail_nullary: +; SLOW: .functype recursive_tail_nullary () -> () +; SLOW-NEXT: # %bb.0: +; SLOW-NEXT: return_call recursive_tail_nullary +; +; FAST-LABEL: recursive_tail_nullary: +; FAST: .functype recursive_tail_nullary () -> () +; FAST-NEXT: # %bb.0: +; FAST-NEXT: call recursive_tail_nullary +; FAST-NEXT: return tail call void @recursive_tail_nullary() ret void } -; CHECK-LABEL: recursive_notail: -; CHECK: call $push[[L:[0-9]+]]=, recursive_notail, $0, $1{{$}} -; CHECK-NEXT: return $pop[[L]]{{$}} define i32 @recursive_notail(i32 %x, i32 %y) { +; CHECK-LABEL: recursive_notail: +; CHECK: .functype recursive_notail (i32, i32) -> (i32) +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: call $push0=, recursive_notail, $0, $1 +; CHECK-NEXT: return $pop0 %v = notail call i32 @recursive_notail(i32 %x, i32 %y) ret i32 %v } -; CHECK-LABEL: recursive_musttail: -; CHECK: return_call recursive_musttail, $0, $1{{$}} define i32 @recursive_musttail(i32 %x, i32 %y) { +; CHECK-LABEL: recursive_musttail: +; CHECK: .functype recursive_musttail (i32, i32) -> (i32) +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: return_call recursive_musttail, $0, $1 %v = musttail call i32 @recursive_musttail(i32 %x, i32 %y) ret i32 %v } -; CHECK-LABEL: recursive_tail: -; SLOW: return_call recursive_tail, $0, $1{{$}} -; FAST: call $push[[L:[0-9]+]]=, recursive_tail, $0, $1{{$}} -; FAST-NEXT: return $pop[[L]]{{$}} define i32 @recursive_tail(i32 %x, i32 %y) { +; SLOW-LABEL: recursive_tail: +; SLOW: .functype recursive_tail (i32, i32) -> (i32) +; SLOW-NEXT: # %bb.0: +; SLOW-NEXT: return_call recursive_tail, $0, $1 +; +; FAST-LABEL: recursive_tail: +; FAST: .functype recursive_tail (i32, i32) -> (i32) +; FAST-NEXT: # %bb.0: +; FAST-NEXT: call $push0=, recursive_tail, $0, $1 +; FAST-NEXT: return $pop0 %v = tail call i32 @recursive_tail(i32 %x, i32 %y) ret i32 %v } -; CHECK-LABEL: indirect_notail: -; CHECK: call_indirect $push[[L:[0-9]+]]=, $0, $1, $2, $0{{$}} -; CHECK-NEXT: return $pop[[L]]{{$}} define i32 @indirect_notail(%fn %f, i32 %x, i32 %y) { +; CHECK-LABEL: indirect_notail: +; CHECK: .functype indirect_notail (i32, i32, i32) -> (i32) +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: call_indirect $push0=, $0, $1, $2, $0 # Invalid depth argument! +; CHECK-NEXT: return $pop0 %p = extractvalue %fn %f, 0 %v = notail call i32 %p(%fn %f, i32 %x, i32 %y) ret i32 %v } -; CHECK-LABEL: indirect_musttail: -; CHECK: return_call_indirect , $0, $1, $2, $0{{$}} define i32 @indirect_musttail(%fn %f, i32 %x, i32 %y) { +; CHECK-LABEL: indirect_musttail: +; CHECK: .functype indirect_musttail (i32, i32, i32) -> (i32) +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: return_call_indirect , $0, $1, $2, $0 %p = extractvalue %fn %f, 0 %v = musttail call i32 %p(%fn %f, i32 %x, i32 %y) ret i32 %v } -; CHECK-LABEL: indirect_tail: -; CHECK: return_call_indirect , $0, $1, $2, $0{{$}} define i32 @indirect_tail(%fn %f, i32 %x, i32 %y) { +; CHECK-LABEL: indirect_tail: +; CHECK: .functype indirect_tail (i32, i32, i32) -> (i32) +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: return_call_indirect , $0, $1, $2, $0 %p = extractvalue %fn %f, 0 %v = tail call i32 %p(%fn %f, i32 %x, i32 %y) ret i32 %v } -; CHECK-LABEL: choice_notail: -; CHECK: call_indirect $push[[L:[0-9]+]]=, $0, $pop{{[0-9]+}}{{$}} -; CHECK-NEXT: return $pop[[L]]{{$}} define i1 @choice_notail(i1 %x) { +; SLOW-LABEL: choice_notail: +; SLOW: .functype choice_notail (i32) -> (i32) +; SLOW-NEXT: # %bb.0: +; SLOW-NEXT: i32.const $push3=, foo +; SLOW-NEXT: i32.const $push2=, bar +; SLOW-NEXT: i32.const $push0=, 1 +; SLOW-NEXT: i32.and $push1=, $0, $pop0 +; SLOW-NEXT: i32.select $push4=, $pop3, $pop2, $pop1 +; SLOW-NEXT: call_indirect $push5=, $0, $pop4 # Invalid depth argument! +; SLOW-NEXT: return $pop5 +; +; FAST-LABEL: choice_notail: +; FAST: .functype choice_notail (i32) -> (i32) +; FAST-NEXT: # %bb.0: +; FAST-NEXT: i32.const $push3=, foo +; FAST-NEXT: i32.const $push4=, bar +; FAST-NEXT: i32.const $push1=, 1 +; FAST-NEXT: i32.and $push2=, $0, $pop1 +; FAST-NEXT: i32.select $push5=, $pop3, $pop4, $pop2 +; FAST-NEXT: call_indirect $push0=, $0, $pop5 # Invalid depth argument! +; FAST-NEXT: return $pop0 %p = select i1 %x, ptr @foo, ptr @bar %v = notail call i1 %p(i1 %x) ret i1 %v } -; CHECK-LABEL: choice_musttail: -; CHECK: return_call_indirect , $0, $pop{{[0-9]+}}{{$}} define i1 @choice_musttail(i1 %x) { +; SLOW-LABEL: choice_musttail: +; SLOW: .functype choice_musttail (i32) -> (i32) +; SLOW-NEXT: # %bb.0: +; SLOW-NEXT: i32.const $push3=, foo +; SLOW-NEXT: i32.const $push2=, bar +; SLOW-NEXT: i32.const $push0=, 1 +; SLOW-NEXT: i32.and $push1=, $0, $pop0 +; SLOW-NEXT: i32.select $push4=, $pop3, $pop2, $pop1 +; SLOW-NEXT: return_call_indirect , $0, $pop4 +; +; FAST-LABEL: choice_musttail: +; FAST: .functype choice_musttail (i32) -> (i32) +; FAST-NEXT: # %bb.0: +; FAST-NEXT: i32.const $push4=, foo +; FAST-NEXT: i32.const $push3=, bar +; FAST-NEXT: i32.const $push1=, 1 +; FAST-NEXT: i32.and $push2=, $0, $pop1 +; FAST-NEXT: i32.select $push0=, $pop4, $pop3, $pop2 +; FAST-NEXT: return_call_indirect , $0, $pop0 %p = select i1 %x, ptr @foo, ptr @bar %v = musttail call i1 %p(i1 %x) ret i1 %v } -; CHECK-LABEL: choice_tail: -; SLOW: return_call_indirect , $0, $pop{{[0-9]+}}{{$}} -; FAST: call_indirect $push[[L:[0-9]+]]=, $0, $pop{{[0-9]+}}{{$}} -; FAST: return $pop[[L]]{{$}} define i1 @choice_tail(i1 %x) { +; SLOW-LABEL: choice_tail: +; SLOW: .functype choice_tail (i32) -> (i32) +; SLOW-NEXT: # %bb.0: +; SLOW-NEXT: i32.const $push3=, foo +; SLOW-NEXT: i32.const $push2=, bar +; SLOW-NEXT: i32.const $push0=, 1 +; SLOW-NEXT: i32.and $push1=, $0, $pop0 +; SLOW-NEXT: i32.select $push4=, $pop3, $pop2, $pop1 +; SLOW-NEXT: return_call_indirect , $0, $pop4 +; +; FAST-LABEL: choice_tail: +; FAST: .functype choice_tail (i32) -> (i32) +; FAST-NEXT: # %bb.0: +; FAST-NEXT: i32.const $push3=, foo +; FAST-NEXT: i32.const $push4=, bar +; FAST-NEXT: i32.const $push1=, 1 +; FAST-NEXT: i32.and $push2=, $0, $pop1 +; FAST-NEXT: i32.select $push5=, $pop3, $pop4, $pop2 +; FAST-NEXT: call_indirect $push0=, $0, $pop5 # Invalid depth argument! +; FAST-NEXT: return $pop0 %p = select i1 %x, ptr @foo, ptr @bar %v = tail call i1 %p(i1 %x) ret i1 %v @@ -114,95 +191,200 @@ define i1 @choice_tail(i1 %x) { ; prototype than its caller, so the following tests can only be done with ; 'tail'. -; CHECK-LABEL: mismatched_prototypes: -; SLOW: return_call baz, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} -; FAST: call $push[[L:[0-9]+]]=, baz, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} -; FAST: return $pop[[L]]{{$}} declare i32 @baz(i32, i32, i32) define i32 @mismatched_prototypes() { +; SLOW-LABEL: mismatched_prototypes: +; SLOW: .functype mismatched_prototypes () -> (i32) +; SLOW-NEXT: # %bb.0: +; SLOW-NEXT: i32.const $push2=, 0 +; SLOW-NEXT: i32.const $push1=, 42 +; SLOW-NEXT: i32.const $push0=, 6 +; SLOW-NEXT: return_call baz, $pop2, $pop1, $pop0 +; +; FAST-LABEL: mismatched_prototypes: +; FAST: .functype mismatched_prototypes () -> (i32) +; FAST-NEXT: # %bb.0: +; FAST-NEXT: i32.const $push1=, 0 +; FAST-NEXT: i32.const $push2=, 42 +; FAST-NEXT: i32.const $push3=, 6 +; FAST-NEXT: call $push0=, baz, $pop1, $pop2, $pop3 +; FAST-NEXT: return $pop0 %v = tail call i32 @baz(i32 0, i32 42, i32 6) ret i32 %v } -; CHECK-LABEL: mismatched_return_void: -; CHECK: call $drop=, baz, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} -; CHECK: return{{$}} define void @mismatched_return_void() { +; SLOW-LABEL: mismatched_return_void: +; SLOW: .functype mismatched_return_void () -> () +; SLOW-NEXT: # %bb.0: +; SLOW-NEXT: i32.const $push2=, 0 +; SLOW-NEXT: i32.const $push1=, 42 +; SLOW-NEXT: i32.const $push0=, 6 +; SLOW-NEXT: call $drop=, baz, $pop2, $pop1, $pop0 +; SLOW-NEXT: return +; +; FAST-LABEL: mismatched_return_void: +; FAST: .functype mismatched_return_void () -> () +; FAST-NEXT: # %bb.0: +; FAST-NEXT: i32.const $push0=, 0 +; FAST-NEXT: i32.const $push1=, 42 +; FAST-NEXT: i32.const $push2=, 6 +; FAST-NEXT: call $drop=, baz, $pop0, $pop1, $pop2 +; FAST-NEXT: return %v = tail call i32 @baz(i32 0, i32 42, i32 6) ret void } -; CHECK-LABEL: mismatched_return_f32: -; CHECK: call $push[[L:[0-9]+]]=, baz, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} -; CHECK: f32.reinterpret_i32 $push[[L1:[0-9]+]]=, $pop[[L]]{{$}} -; CHECK: return $pop[[L1]]{{$}} define float @mismatched_return_f32() { +; SLOW-LABEL: mismatched_return_f32: +; SLOW: .functype mismatched_return_f32 () -> (f32) +; SLOW-NEXT: # %bb.0: +; SLOW-NEXT: i32.const $push2=, 0 +; SLOW-NEXT: i32.const $push1=, 42 +; SLOW-NEXT: i32.const $push0=, 6 +; SLOW-NEXT: call $push3=, baz, $pop2, $pop1, $pop0 +; SLOW-NEXT: f32.reinterpret_i32 $push4=, $pop3 +; SLOW-NEXT: return $pop4 +; +; FAST-LABEL: mismatched_return_f32: +; FAST: .functype mismatched_return_f32 () -> (f32) +; FAST-NEXT: # %bb.0: +; FAST-NEXT: i32.const $push2=, 0 +; FAST-NEXT: i32.const $push3=, 42 +; FAST-NEXT: i32.const $push4=, 6 +; FAST-NEXT: call $push1=, baz, $pop2, $pop3, $pop4 +; FAST-NEXT: f32.reinterpret_i32 $push0=, $pop1 +; FAST-NEXT: return $pop0 %v = tail call i32 @baz(i32 0, i32 42, i32 6) %u = bitcast i32 %v to float ret float %u } -; CHECK-LABEL: mismatched_indirect_void: -; CHECK: call_indirect $drop=, $0, $1, $2, $0{{$}} -; CHECK: return{{$}} define void @mismatched_indirect_void(%fn %f, i32 %x, i32 %y) { +; CHECK-LABEL: mismatched_indirect_void: +; CHECK: .functype mismatched_indirect_void (i32, i32, i32) -> () +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: call_indirect $drop=, $0, $1, $2, $0 # Invalid depth argument! +; CHECK-NEXT: return %p = extractvalue %fn %f, 0 %v = tail call i32 %p(%fn %f, i32 %x, i32 %y) ret void } -; CHECK-LABEL: mismatched_indirect_f32: -; CHECK: call_indirect $push[[L:[0-9]+]]=, $0, $1, $2, $0{{$}} -; CHECK: f32.reinterpret_i32 $push[[L1:[0-9]+]]=, $pop[[L]]{{$}} -; CHECK: return $pop[[L1]]{{$}} define float @mismatched_indirect_f32(%fn %f, i32 %x, i32 %y) { +; CHECK-LABEL: mismatched_indirect_f32: +; CHECK: .functype mismatched_indirect_f32 (i32, i32, i32) -> (f32) +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: call_indirect $push0=, $0, $1, $2, $0 # Invalid depth argument! +; CHECK-NEXT: f32.reinterpret_i32 $push1=, $pop0 +; CHECK-NEXT: return $pop1 %p = extractvalue %fn %f, 0 %v = tail call i32 %p(%fn %f, i32 %x, i32 %y) %u = bitcast i32 %v to float ret float %u } -; CHECK-LABEL: mismatched_byval: -; CHECK: i32.store -; CHECK: return_call quux, $pop{{[0-9]+}}{{$}} declare i32 @quux(ptr byval(i32)) define i32 @mismatched_byval(ptr %x) { +; CHECK-LABEL: mismatched_byval: +; CHECK: .functype mismatched_byval (i32) -> (i32) +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: global.get $push1=, __stack_pointer +; CHECK-NEXT: i32.const $push2=, 16 +; CHECK-NEXT: i32.sub $push8=, $pop1, $pop2 +; CHECK-NEXT: local.tee $push7=, $1=, $pop8 +; CHECK-NEXT: global.set __stack_pointer, $pop7 +; CHECK-NEXT: i32.load $push0=, 0($0) +; CHECK-NEXT: i32.store 12($1), $pop0 +; CHECK-NEXT: i32.const $push3=, 16 +; CHECK-NEXT: i32.add $push4=, $1, $pop3 +; CHECK-NEXT: global.set __stack_pointer, $pop4 +; CHECK-NEXT: i32.const $push5=, 12 +; CHECK-NEXT: i32.add $push6=, $1, $pop5 +; CHECK-NEXT: return_call quux, $pop6 %v = tail call i32 @quux(ptr byval(i32) %x) ret i32 %v } -; CHECK-LABEL: varargs: -; CHECK: i32.store -; CHECK: call $0=, var, $1{{$}} -; CHECK: return $0{{$}} declare i32 @var(...) define i32 @varargs(i32 %x) { +; CHECK-LABEL: varargs: +; CHECK: .functype varargs (i32) -> (i32) +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: global.get $push0=, __stack_pointer +; CHECK-NEXT: i32.const $push1=, 16 +; CHECK-NEXT: i32.sub $push5=, $pop0, $pop1 +; CHECK-NEXT: local.tee $push4=, $1=, $pop5 +; CHECK-NEXT: global.set __stack_pointer, $pop4 +; CHECK-NEXT: i32.store 0($1), $0 +; CHECK-NEXT: call $0=, var, $1 +; CHECK-NEXT: i32.const $push2=, 16 +; CHECK-NEXT: i32.add $push3=, $1, $pop2 +; CHECK-NEXT: global.set __stack_pointer, $pop3 +; CHECK-NEXT: return $0 %v = tail call i32 (...) @var(i32 %x) ret i32 %v } ; Type transformations inhibit tail calls, even when they are nops -; CHECK-LABEL: mismatched_return_zext: -; CHECK: call define i32 @mismatched_return_zext() { +; SLOW-LABEL: mismatched_return_zext: +; SLOW: .functype mismatched_return_zext () -> (i32) +; SLOW-NEXT: # %bb.0: +; SLOW-NEXT: i32.const $push0=, 1 +; SLOW-NEXT: call $push1=, foo, $pop0 +; SLOW-NEXT: i32.const $push3=, 1 +; SLOW-NEXT: i32.and $push2=, $pop1, $pop3 +; SLOW-NEXT: return $pop2 +; +; FAST-LABEL: mismatched_return_zext: +; FAST: .functype mismatched_return_zext () -> (i32) +; FAST-NEXT: # %bb.0: +; FAST-NEXT: i32.const $push2=, 1 +; FAST-NEXT: call $push1=, foo, $pop2 +; FAST-NEXT: i32.const $push3=, 1 +; FAST-NEXT: i32.and $push0=, $pop1, $pop3 +; FAST-NEXT: return $pop0 %v = tail call i1 @foo(i1 1) %u = zext i1 %v to i32 ret i32 %u } -; CHECK-LABEL: mismatched_return_sext: -; CHECK: call define i32 @mismatched_return_sext() { +; SLOW-LABEL: mismatched_return_sext: +; SLOW: .functype mismatched_return_sext () -> (i32) +; SLOW-NEXT: # %bb.0: +; SLOW-NEXT: i32.const $push3=, 0 +; SLOW-NEXT: i32.const $push0=, 1 +; SLOW-NEXT: call $push1=, foo, $pop0 +; SLOW-NEXT: i32.const $push5=, 1 +; SLOW-NEXT: i32.and $push2=, $pop1, $pop5 +; SLOW-NEXT: i32.sub $push4=, $pop3, $pop2 +; SLOW-NEXT: return $pop4 +; +; FAST-LABEL: mismatched_return_sext: +; FAST: .functype mismatched_return_sext () -> (i32) +; FAST-NEXT: # %bb.0: +; FAST-NEXT: i32.const $push4=, 1 +; FAST-NEXT: call $push3=, foo, $pop4 +; FAST-NEXT: i32.const $push0=, 31 +; FAST-NEXT: i32.shl $push1=, $pop3, $pop0 +; FAST-NEXT: i32.const $push5=, 31 +; FAST-NEXT: i32.shr_s $push2=, $pop1, $pop5 +; FAST-NEXT: return $pop2 %v = tail call i1 @foo(i1 1) %u = sext i1 %v to i32 ret i32 %u } -; CHECK-LABEL: mismatched_return_trunc: -; CHECK: call declare i32 @int() define i1 @mismatched_return_trunc() { +; CHECK-LABEL: mismatched_return_trunc: +; CHECK: .functype mismatched_return_trunc () -> (i32) +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: call $push0=, int +; CHECK-NEXT: return $pop0 %v = tail call i32 @int() %u = trunc i32 %v to i1 ret i1 %u @@ -210,30 +392,115 @@ define i1 @mismatched_return_trunc() { ; Stack-allocated arguments inhibit tail calls -; CHECK-LABEL: stack_arg: -; CHECK: call define i32 @stack_arg(ptr %x) { +; SLOW-LABEL: stack_arg: +; SLOW: .functype stack_arg (i32) -> (i32) +; SLOW-NEXT: # %bb.0: +; SLOW-NEXT: global.get $push0=, __stack_pointer +; SLOW-NEXT: i32.const $push1=, 16 +; SLOW-NEXT: i32.sub $push7=, $pop0, $pop1 +; SLOW-NEXT: local.tee $push6=, $2=, $pop7 +; SLOW-NEXT: global.set __stack_pointer, $pop6 +; SLOW-NEXT: i32.const $push4=, 12 +; SLOW-NEXT: i32.add $push5=, $2, $pop4 +; SLOW-NEXT: call $1=, stack_arg, $pop5 +; SLOW-NEXT: i32.const $push2=, 16 +; SLOW-NEXT: i32.add $push3=, $2, $pop2 +; SLOW-NEXT: global.set __stack_pointer, $pop3 +; SLOW-NEXT: return $1 +; +; FAST-LABEL: stack_arg: +; FAST: .functype stack_arg (i32) -> (i32) +; FAST-NEXT: # %bb.0: +; FAST-NEXT: global.get $push1=, __stack_pointer +; FAST-NEXT: i32.const $push2=, 16 +; FAST-NEXT: i32.sub $push8=, $pop1, $pop2 +; FAST-NEXT: local.tee $push7=, $2=, $pop8 +; FAST-NEXT: global.set __stack_pointer, $pop7 +; FAST-NEXT: i32.const $push5=, 12 +; FAST-NEXT: i32.add $push6=, $2, $pop5 +; FAST-NEXT: local.copy $push0=, $pop6 +; FAST-NEXT: call $1=, stack_arg, $pop0 +; FAST-NEXT: i32.const $push3=, 16 +; FAST-NEXT: i32.add $push4=, $2, $pop3 +; FAST-NEXT: global.set __stack_pointer, $pop4 +; FAST-NEXT: return $1 %a = alloca i32 %v = tail call i32 @stack_arg(ptr %a) ret i32 %v } -; CHECK-LABEL: stack_arg_gep: -; CHECK: call define i32 @stack_arg_gep(ptr %x) { +; SLOW-LABEL: stack_arg_gep: +; SLOW: .functype stack_arg_gep (i32) -> (i32) +; SLOW-NEXT: # %bb.0: +; SLOW-NEXT: global.get $push2=, __stack_pointer +; SLOW-NEXT: i32.const $push3=, 16 +; SLOW-NEXT: i32.sub $push9=, $pop2, $pop3 +; SLOW-NEXT: local.tee $push8=, $2=, $pop9 +; SLOW-NEXT: global.set __stack_pointer, $pop8 +; SLOW-NEXT: i32.const $push6=, 8 +; SLOW-NEXT: i32.add $push7=, $2, $pop6 +; SLOW-NEXT: i32.const $push0=, 4 +; SLOW-NEXT: i32.or $push1=, $pop7, $pop0 +; SLOW-NEXT: call $1=, stack_arg_gep, $pop1 +; SLOW-NEXT: i32.const $push4=, 16 +; SLOW-NEXT: i32.add $push5=, $2, $pop4 +; SLOW-NEXT: global.set __stack_pointer, $pop5 +; SLOW-NEXT: return $1 +; +; FAST-LABEL: stack_arg_gep: +; FAST: .functype stack_arg_gep (i32) -> (i32) +; FAST-NEXT: # %bb.0: +; FAST-NEXT: global.get $push3=, __stack_pointer +; FAST-NEXT: i32.const $push4=, 16 +; FAST-NEXT: i32.sub $push10=, $pop3, $pop4 +; FAST-NEXT: local.tee $push9=, $2=, $pop10 +; FAST-NEXT: global.set __stack_pointer, $pop9 +; FAST-NEXT: i32.const $push7=, 8 +; FAST-NEXT: i32.add $push8=, $2, $pop7 +; FAST-NEXT: local.copy $push0=, $pop8 +; FAST-NEXT: i32.const $push1=, 4 +; FAST-NEXT: i32.add $push2=, $pop0, $pop1 +; FAST-NEXT: call $1=, stack_arg_gep, $pop2 +; FAST-NEXT: i32.const $push5=, 16 +; FAST-NEXT: i32.add $push6=, $2, $pop5 +; FAST-NEXT: global.set __stack_pointer, $pop6 +; FAST-NEXT: return $1 %a = alloca { i32, i32 } %p = getelementptr { i32, i32 }, ptr %a, i32 0, i32 1 %v = tail call i32 @stack_arg_gep(ptr %p) ret i32 %v } -; CHECK-LABEL: stack_arg_cast: -; CHECK: global.get $push{{[0-9]+}}=, __stack_pointer -; CHECK: global.set __stack_pointer, $pop{{[0-9]+}} -; FAST: call ${{[0-9]+}}=, stack_arg_cast, $pop{{[0-9]+}} -; CHECK: global.set __stack_pointer, $pop{{[0-9]+}} -; SLOW: return_call stack_arg_cast, ${{[0-9]+}} define i32 @stack_arg_cast(i32 %x) { +; SLOW-LABEL: stack_arg_cast: +; SLOW: .functype stack_arg_cast (i32) -> (i32) +; SLOW-NEXT: # %bb.0: +; SLOW-NEXT: global.get $push0=, __stack_pointer +; SLOW-NEXT: i32.const $push1=, 256 +; SLOW-NEXT: i32.sub $push5=, $pop0, $pop1 +; SLOW-NEXT: local.tee $push4=, $1=, $pop5 +; SLOW-NEXT: global.set __stack_pointer, $pop4 +; SLOW-NEXT: i32.const $push2=, 256 +; SLOW-NEXT: i32.add $push3=, $1, $pop2 +; SLOW-NEXT: global.set __stack_pointer, $pop3 +; SLOW-NEXT: return_call stack_arg_cast, $1 +; +; FAST-LABEL: stack_arg_cast: +; FAST: .functype stack_arg_cast (i32) -> (i32) +; FAST-NEXT: # %bb.0: +; FAST-NEXT: global.get $push1=, __stack_pointer +; FAST-NEXT: i32.const $push2=, 256 +; FAST-NEXT: i32.sub $push6=, $pop1, $pop2 +; FAST-NEXT: local.tee $push5=, $2=, $pop6 +; FAST-NEXT: global.set __stack_pointer, $pop5 +; FAST-NEXT: local.copy $push0=, $2 +; FAST-NEXT: call $1=, stack_arg_cast, $pop0 +; FAST-NEXT: i32.const $push3=, 256 +; FAST-NEXT: i32.add $push4=, $2, $pop3 +; FAST-NEXT: global.set __stack_pointer, $pop4 +; FAST-NEXT: return $1 %a = alloca [64 x i32] %i = ptrtoint ptr %a to i32 %v = tail call i32 @stack_arg_cast(i32 %i) @@ -252,6 +519,28 @@ define i32 @stack_arg_cast(i32 %x) { ; YAML-NEXT: ReturnTypes: ; YAML-NEXT: - I32 define i32 @unique_caller(ptr %p) { +; SLOW-LABEL: unique_caller: +; SLOW: .functype unique_caller (i32) -> (i32) +; SLOW-NEXT: # %bb.0: +; SLOW-NEXT: i32.const $push4=, 0 +; SLOW-NEXT: f32.const $push3=, 0x0p0 +; SLOW-NEXT: i64.const $push2=, 0 +; SLOW-NEXT: f64.const $push1=, 0x0p0 +; SLOW-NEXT: i32.load $push0=, 0($0) +; SLOW-NEXT: return_call_indirect , $pop4, $pop3, $pop2, $pop1, $pop0 +; +; FAST-LABEL: unique_caller: +; FAST: .functype unique_caller (i32) -> (i32) +; FAST-NEXT: # %bb.0: +; FAST-NEXT: i32.const $push1=, 0 +; FAST-NEXT: i32.const $push7=, 0 +; FAST-NEXT: f32.convert_i32_s $push2=, $pop7 +; FAST-NEXT: i64.const $push3=, 0 +; FAST-NEXT: i32.const $push6=, 0 +; FAST-NEXT: f64.convert_i32_s $push4=, $pop6 +; FAST-NEXT: i32.load $push5=, 0($0) +; FAST-NEXT: call_indirect $push0=, $pop1, $pop2, $pop3, $pop4, $pop5 # Invalid depth argument! +; FAST-NEXT: return $pop0 %f = load ptr, ptr %p %v = tail call i32 %f(i32 0, float 0., i64 0, double 0.) ret i32 %v