4 changes: 2 additions & 2 deletions llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ bool WebAssemblyPeephole::runOnMachineFunction(MachineFunction &MF) {
switch (MI.getOpcode()) {
default:
break;
case WebAssembly::CALL_I32:
case WebAssembly::CALL_I64: {
case WebAssembly::CALL_i32:
case WebAssembly::CALL_i64: {
MachineOperand &Op1 = MI.getOperand(1);
if (Op1.isSymbol()) {
StringRef Name(Op1.getSymbolName());
Expand Down
88 changes: 48 additions & 40 deletions llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,14 @@ bool WebAssembly::isCallDirect(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case WebAssembly::CALL_VOID:
case WebAssembly::CALL_VOID_S:
case WebAssembly::CALL_I32:
case WebAssembly::CALL_I32_S:
case WebAssembly::CALL_I64:
case WebAssembly::CALL_I64_S:
case WebAssembly::CALL_F32:
case WebAssembly::CALL_F32_S:
case WebAssembly::CALL_F64:
case WebAssembly::CALL_F64_S:
case WebAssembly::CALL_i32:
case WebAssembly::CALL_i32_S:
case WebAssembly::CALL_i64:
case WebAssembly::CALL_i64_S:
case WebAssembly::CALL_f32:
case WebAssembly::CALL_f32_S:
case WebAssembly::CALL_f64:
case WebAssembly::CALL_f64_S:
case WebAssembly::CALL_v16i8:
case WebAssembly::CALL_v16i8_S:
case WebAssembly::CALL_v8i16:
Expand All @@ -127,8 +127,10 @@ bool WebAssembly::isCallDirect(const MachineInstr &MI) {
case WebAssembly::CALL_v4f32_S:
case WebAssembly::CALL_v2f64:
case WebAssembly::CALL_v2f64_S:
case WebAssembly::CALL_EXCEPT_REF:
case WebAssembly::CALL_EXCEPT_REF_S:
case WebAssembly::CALL_ExceptRef:
case WebAssembly::CALL_ExceptRef_S:
case WebAssembly::RET_CALL:
case WebAssembly::RET_CALL_S:
return true;
default:
return false;
Expand All @@ -139,14 +141,14 @@ bool WebAssembly::isCallIndirect(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case WebAssembly::CALL_INDIRECT_VOID:
case WebAssembly::CALL_INDIRECT_VOID_S:
case WebAssembly::CALL_INDIRECT_I32:
case WebAssembly::CALL_INDIRECT_I32_S:
case WebAssembly::CALL_INDIRECT_I64:
case WebAssembly::CALL_INDIRECT_I64_S:
case WebAssembly::CALL_INDIRECT_F32:
case WebAssembly::CALL_INDIRECT_F32_S:
case WebAssembly::CALL_INDIRECT_F64:
case WebAssembly::CALL_INDIRECT_F64_S:
case WebAssembly::CALL_INDIRECT_i32:
case WebAssembly::CALL_INDIRECT_i32_S:
case WebAssembly::CALL_INDIRECT_i64:
case WebAssembly::CALL_INDIRECT_i64_S:
case WebAssembly::CALL_INDIRECT_f32:
case WebAssembly::CALL_INDIRECT_f32_S:
case WebAssembly::CALL_INDIRECT_f64:
case WebAssembly::CALL_INDIRECT_f64_S:
case WebAssembly::CALL_INDIRECT_v16i8:
case WebAssembly::CALL_INDIRECT_v16i8_S:
case WebAssembly::CALL_INDIRECT_v8i16:
Expand All @@ -159,8 +161,10 @@ bool WebAssembly::isCallIndirect(const MachineInstr &MI) {
case WebAssembly::CALL_INDIRECT_v4f32_S:
case WebAssembly::CALL_INDIRECT_v2f64:
case WebAssembly::CALL_INDIRECT_v2f64_S:
case WebAssembly::CALL_INDIRECT_EXCEPT_REF:
case WebAssembly::CALL_INDIRECT_EXCEPT_REF_S:
case WebAssembly::CALL_INDIRECT_ExceptRef:
case WebAssembly::CALL_INDIRECT_ExceptRef_S:
case WebAssembly::RET_CALL_INDIRECT:
case WebAssembly::RET_CALL_INDIRECT_S:
return true;
default:
return false;
Expand All @@ -173,15 +177,19 @@ unsigned WebAssembly::getCalleeOpNo(const MachineInstr &MI) {
case WebAssembly::CALL_VOID_S:
case WebAssembly::CALL_INDIRECT_VOID:
case WebAssembly::CALL_INDIRECT_VOID_S:
case WebAssembly::RET_CALL:
case WebAssembly::RET_CALL_S:
case WebAssembly::RET_CALL_INDIRECT:
case WebAssembly::RET_CALL_INDIRECT_S:
return 0;
case WebAssembly::CALL_I32:
case WebAssembly::CALL_I32_S:
case WebAssembly::CALL_I64:
case WebAssembly::CALL_I64_S:
case WebAssembly::CALL_F32:
case WebAssembly::CALL_F32_S:
case WebAssembly::CALL_F64:
case WebAssembly::CALL_F64_S:
case WebAssembly::CALL_i32:
case WebAssembly::CALL_i32_S:
case WebAssembly::CALL_i64:
case WebAssembly::CALL_i64_S:
case WebAssembly::CALL_f32:
case WebAssembly::CALL_f32_S:
case WebAssembly::CALL_f64:
case WebAssembly::CALL_f64_S:
case WebAssembly::CALL_v16i8:
case WebAssembly::CALL_v16i8_S:
case WebAssembly::CALL_v8i16:
Expand All @@ -194,16 +202,16 @@ unsigned WebAssembly::getCalleeOpNo(const MachineInstr &MI) {
case WebAssembly::CALL_v4f32_S:
case WebAssembly::CALL_v2f64:
case WebAssembly::CALL_v2f64_S:
case WebAssembly::CALL_EXCEPT_REF:
case WebAssembly::CALL_EXCEPT_REF_S:
case WebAssembly::CALL_INDIRECT_I32:
case WebAssembly::CALL_INDIRECT_I32_S:
case WebAssembly::CALL_INDIRECT_I64:
case WebAssembly::CALL_INDIRECT_I64_S:
case WebAssembly::CALL_INDIRECT_F32:
case WebAssembly::CALL_INDIRECT_F32_S:
case WebAssembly::CALL_INDIRECT_F64:
case WebAssembly::CALL_INDIRECT_F64_S:
case WebAssembly::CALL_ExceptRef:
case WebAssembly::CALL_ExceptRef_S:
case WebAssembly::CALL_INDIRECT_i32:
case WebAssembly::CALL_INDIRECT_i32_S:
case WebAssembly::CALL_INDIRECT_i64:
case WebAssembly::CALL_INDIRECT_i64_S:
case WebAssembly::CALL_INDIRECT_f32:
case WebAssembly::CALL_INDIRECT_f32_S:
case WebAssembly::CALL_INDIRECT_f64:
case WebAssembly::CALL_INDIRECT_f64_S:
case WebAssembly::CALL_INDIRECT_v16i8:
case WebAssembly::CALL_INDIRECT_v16i8_S:
case WebAssembly::CALL_INDIRECT_v8i16:
Expand All @@ -216,8 +224,8 @@ unsigned WebAssembly::getCalleeOpNo(const MachineInstr &MI) {
case WebAssembly::CALL_INDIRECT_v4f32_S:
case WebAssembly::CALL_INDIRECT_v2f64:
case WebAssembly::CALL_INDIRECT_v2f64_S:
case WebAssembly::CALL_INDIRECT_EXCEPT_REF:
case WebAssembly::CALL_INDIRECT_EXCEPT_REF_S:
case WebAssembly::CALL_INDIRECT_ExceptRef:
case WebAssembly::CALL_INDIRECT_ExceptRef_S:
return 1;
default:
llvm_unreachable("Not a call instruction");
Expand Down
27 changes: 19 additions & 8 deletions llvm/test/CodeGen/WebAssembly/call.ll
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -mattr=+sign-ext,+simd128 | FileCheck %s
; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -fast-isel -fast-isel-abort=1 -mattr=+sign-ext,+simd128 | FileCheck %s
; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -mattr=+sign-ext,+simd128 | FileCheck --check-prefixes=CHECK,NO-TAIL,SLOW-NO-TAIL %s
; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -fast-isel -fast-isel-abort=1 -mattr=+sign-ext,+simd128 | FileCheck --check-prefixes=CHECK,NO-TAIL,FAST,FAST-NO-TAIL %s
; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -mattr=+sign-ext,+simd128,+tail-call | FileCheck --check-prefixes=CHECK,TAIL,SLOW-TAIL %s
; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -fast-isel -fast-isel-abort=1 -mattr=+sign-ext,+simd128,+tail-call | FileCheck --check-prefixes=CHECK,TAIL,FAST-TAIL %s

; Test that basic call operations assemble as expected.

Expand Down Expand Up @@ -176,26 +178,35 @@ define void @call_indirect_arg_2(i32 (i32, i32)* %callee, i32 %arg, i32 %arg2) {

; CHECK-LABEL: tail_call_void_nullary:
; CHECK-NEXT: .functype tail_call_void_nullary () -> (){{$}}
; CHECK-NEXT: {{^}} call void_nullary{{$}}
; CHECK-NEXT: return{{$}}
; NO-TAIL-NEXT: {{^}} call void_nullary{{$}}
; NO-TAIL-NEXT: return{{$}}
; SLOW-TAIL-NEXT: {{^}} return_call void_nullary{{$}}
; FAST-TAIL-NEXT: {{^}} call void_nullary{{$}}
; FAST-TAIL-NEXT: return{{$}}
define void @tail_call_void_nullary() {
tail call void @void_nullary()
ret void
}

; CHECK-LABEL: fastcc_tail_call_void_nullary:
; CHECK-NEXT: .functype fastcc_tail_call_void_nullary () -> (){{$}}
; CHECK-NEXT: {{^}} call void_nullary{{$}}
; CHECK-NEXT: return{{$}}
; NO-TAIL-NEXT: {{^}} call void_nullary{{$}}
; NO-TAIL-NEXT: return{{$}}
; SLOW-TAIL-NEXT: {{^}} return_call void_nullary{{$}}
; FAST-TAIL-NEXT: {{^}} call void_nullary{{$}}
; FAST-TAIL-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: .functype coldcc_tail_call_void_nullary () -> (){{$}}
; CHECK-NEXT: {{^}} call void_nullary{{$}}
; CHECK-NEXT: return{{$}}
; NO-TAIL-NEXT: {{^}} call void_nullary{{$}}
; NO-TAIL-NEXT: return{{$}}
; SLOW-TAIL-NEXT: {{^}} return_call void_nullary{{$}}
; FAST-TAIL-NEXT: {{^}} call void_nullary{{$}}
; FAST-TAIL-NEXT: return{{$}}
define void @coldcc_tail_call_void_nullary() {
tail call coldcc void @void_nullary()
ret void
Expand Down
140 changes: 134 additions & 6 deletions llvm/test/CodeGen/WebAssembly/tailcall.ll
Original file line number Diff line number Diff line change
@@ -1,16 +1,144 @@
; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=+tail-call | FileCheck %s
; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -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 -mattr=+tail-call | FileCheck --check-prefixes=CHECK,FAST %s

; Test that the tail-call attribute is accepted
; TODO(tlively): implement tail call

target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"

%fn = type <{i32 (%fn, i32, i32)*}>
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() {
notail call void @recursive_notail_nullary()
ret void
}

; CHECK-LABEL: recursive_musttail_nullary:
; CHECK: return_call recursive_musttail_nullary{{$}}
define void @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() {
tail call void @recursive_tail_nullary()
ret void
}

; CHECK-LABEL: recursive_notail:
; CHECK: i32.call $push[[L:[0-9]+]]=, recursive_notail, $0, $1{{$}}
; CHECK-NEXT: return $pop[[L]]{{$}}
define i32 @recursive_notail(i32 %x, i32 %y) {
%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) {
%v = musttail call i32 @recursive_musttail(i32 %x, i32 %y)
ret i32 %v
}

; CHECK-LABEL: recursive_tail:
; CHECK: i32.call $push[[L0:[0-9]+]]=, recursive_tail{{$}}
; CHECK-NEXT: return $pop[[L0]]{{$}}
define i32 @recursive_tail() {
%v = tail call i32 @recursive_tail()
; SLOW: return_call recursive_tail, $0, $1{{$}}
; FAST: i32.call $push[[L:[0-9]+]]=, recursive_tail, $0, $1{{$}}
; FAST-NEXT: return $pop[[L]]{{$}}
define i32 @recursive_tail(i32 %x, i32 %y) {
%v = tail call i32 @recursive_tail(i32 %x, i32 %y)
ret i32 %v
}

; CHECK-LABEL: indirect_notail:
; CHECK: i32.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) {
%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) {
%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) {
%p = extractvalue %fn %f, 0
%v = tail call i32 %p(%fn %f, i32 %x, i32 %y)
ret i32 %v
}

; CHECK-LABEL: choice_notail:
; CHECK: i32.call_indirect $push[[L:[0-9]+]]=, $0, $pop{{[0-9]+}}{{$}}
; CHECK-NEXT: return $pop[[L]]{{$}}
define i1 @choice_notail(i1 %x) {
%p = select i1 %x, i1 (i1)* @foo, i1 (i1)* @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) {
%p = select i1 %x, i1 (i1)* @foo, i1 (i1)* @bar
%v = musttail call i1 %p(i1 %x)
ret i1 %v
}

; CHECK-LABEL: choice_tail:
; SLOW: return_call_indirect , $0, $pop{{[0-9]+}}{{$}}
; FAST: i32.call_indirect $push[[L:[0-9]+]]=, $0, $pop{{[0-9]+}}{{$}}
; FAST: return $pop[[L]]{{$}}
define i1 @choice_tail(i1 %x) {
%p = select i1 %x, i1 (i1)* @foo, i1 (i1)* @bar
%v = tail call i1 %p(i1 %x)
ret i1 %v
}

; It is an LLVM validation error for a 'musttail' callee to have a different
; 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: i32.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() {
%v = tail call i32 @baz(i32 0, i32 42, i32 6)
ret i32 %v
}

; CHECK-LABEL: mismatched_byval:
; CHECK: i32.store
; CHECK: return_call quux, $pop{{[0-9]+}}{{$}}
declare i32 @quux(i32* byval)
define i32 @mismatched_byval(i32* %x) {
%v = tail call i32 @quux(i32* byval %x)
ret i32 %v
}

; CHECK-LABEL: varargs:
; CHECK: i32.store
; CHECK: return_call var, $1{{$}}
declare i32 @var(...)
define i32 @varargs(i32 %x) {
%v = tail call i32 (...) @var(i32 %x)
ret i32 %v
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
# CHECK: body:
# CHECK: %1:i32 = I32_WRAP_I64 %0,
# CHECK-NEXT: DBG_VALUE %1,
# CHECK-NEXT: %1:i32 = CALL_I32 @bar,
# CHECK-NEXT: %1:i32 = CALL_i32 @bar,
# CHECK-NEXT: DBG_VALUE %1,
# CHECK-NEXT: %[[NEWREG:.*]]:i32 = CALL_I32 @bar,
# CHECK-NEXT: %[[NEWREG:.*]]:i32 = CALL_i32 @bar,
# CHECK-NEXT: DBG_VALUE %[[NEWREG]],
# CHECK-NEXT: CALL_VOID @foo, %[[NEWREG]],

Expand Down Expand Up @@ -50,9 +50,9 @@ body: |
%0:i64 = ARGUMENT_i64 0, implicit $arguments
%1:i32 = I32_WRAP_I64 %0:i64, implicit-def dead $arguments
DBG_VALUE %1:i32, $noreg, !10, !DIExpression(), debug-location !13; <unknown>:357:12 line no:357
%1:i32 = CALL_I32 @bar, implicit-def dead $arguments, implicit $sp32, implicit $sp64
%1:i32 = CALL_i32 @bar, implicit-def dead $arguments, implicit $sp32, implicit $sp64
DBG_VALUE %1:i32, $noreg, !11, !DIExpression(), debug-location !14; <unknown>:357:12 line no:357
%1:i32 = CALL_I32 @bar, implicit-def dead $arguments, implicit $sp32, implicit $sp64
%1:i32 = CALL_i32 @bar, implicit-def dead $arguments, implicit $sp32, implicit $sp64
DBG_VALUE %1:i32, $noreg, !12, !DIExpression(), debug-location !15; <unknown>:357:12 line no:357
CALL_VOID @foo, %1:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
RETURN_VOID implicit-def dead $arguments
Expand Down
22 changes: 22 additions & 0 deletions llvm/test/MC/WebAssembly/tail-call-encodings.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# RUN: llvm-mc -show-encoding -triple=wasm32-unkown-unknown -mattr=+tail-call < %s | FileCheck %s

bar1:
.functype bar1 () -> ()
end_function

foo1:
.functype foo1 () -> ()

# CHECK: return_call bar1 # encoding: [0x12,
# CHECK-NEXT: fixup A - offset: 1, value: bar1, kind: fixup_uleb128_i32
return_call bar1

end_function

foo2:
.functype foo2 () -> ()

# CHECK: return_call_indirect 0 # encoding: [0x13,0x00,0x00]
return_call_indirect 0

end_function