Skip to content

Commit a1d97a9

Browse files
committedJun 26, 2019
[WebAssembly] Implement tail calls and unify tablegen call classes
Summary: Implements direct and indirect tail calls enabled by the 'tail-call' feature in both DAG ISel and FastISel. Updates existing call tests and adds new tests including a binary encoding test. Reviewers: aheejin Subscribers: dschuff, sbc100, jgravelle-google, hiraditya, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D62877 llvm-svn: 364445
1 parent c950465 commit a1d97a9

14 files changed

+371
-170
lines changed
 

Diff for: ‎llvm/docs/CodeGenerator.rst

+6-1
Original file line numberDiff line numberDiff line change
@@ -2075,7 +2075,8 @@ Tail call optimization
20752075
----------------------
20762076

20772077
Tail call optimization, callee reusing the stack of the caller, is currently
2078-
supported on x86/x86-64 and PowerPC. It is performed if:
2078+
supported on x86/x86-64, PowerPC, and WebAssembly. It is performed on x86/x86-64
2079+
and PowerPC if:
20792080

20802081
* Caller and callee have the calling convention ``fastcc``, ``cc 10`` (GHC
20812082
calling convention) or ``cc 11`` (HiPE calling convention).
@@ -2103,6 +2104,10 @@ PowerPC constraints:
21032104
* On ppc32/64 GOT/PIC only module-local calls (visibility = hidden or protected)
21042105
are supported.
21052106

2107+
On WebAssembly, tail calls are lowered to ``return_call`` and
2108+
``return_call_indirect`` instructions whenever the 'tail-call' target attribute
2109+
is enabled.
2110+
21062111
Example:
21072112

21082113
Call as ``llc -tailcallopt test.ll``.

Diff for: ‎llvm/lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp

+12-8
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,14 @@ static unsigned getNonPseudoCallIndirectOpcode(const MachineInstr &MI) {
6565
using namespace WebAssembly;
6666
case PCALL_INDIRECT_VOID:
6767
return CALL_INDIRECT_VOID;
68-
case PCALL_INDIRECT_I32:
69-
return CALL_INDIRECT_I32;
70-
case PCALL_INDIRECT_I64:
71-
return CALL_INDIRECT_I64;
72-
case PCALL_INDIRECT_F32:
73-
return CALL_INDIRECT_F32;
74-
case PCALL_INDIRECT_F64:
75-
return CALL_INDIRECT_F64;
68+
case PCALL_INDIRECT_i32:
69+
return CALL_INDIRECT_i32;
70+
case PCALL_INDIRECT_i64:
71+
return CALL_INDIRECT_i64;
72+
case PCALL_INDIRECT_f32:
73+
return CALL_INDIRECT_f32;
74+
case PCALL_INDIRECT_f64:
75+
return CALL_INDIRECT_f64;
7676
case PCALL_INDIRECT_v16i8:
7777
return CALL_INDIRECT_v16i8;
7878
case PCALL_INDIRECT_v8i16:
@@ -85,6 +85,10 @@ static unsigned getNonPseudoCallIndirectOpcode(const MachineInstr &MI) {
8585
return CALL_INDIRECT_v4f32;
8686
case PCALL_INDIRECT_v2f64:
8787
return CALL_INDIRECT_v2f64;
88+
case PCALL_INDIRECT_ExceptRef:
89+
return CALL_INDIRECT_ExceptRef;
90+
case PRET_CALL_INDIRECT:
91+
return RET_CALL_INDIRECT;
8892
default:
8993
return INSTRUCTION_LIST_END;
9094
}

Diff for: ‎llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp

+7-6
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,7 @@ bool WebAssemblyFastISel::fastLowerArguments() {
741741
bool WebAssemblyFastISel::selectCall(const Instruction *I) {
742742
const auto *Call = cast<CallInst>(I);
743743

744+
// TODO: Support tail calls in FastISel
744745
if (Call->isMustTailCall() || Call->isInlineAsm() ||
745746
Call->getFunctionType()->isVarArg())
746747
return false;
@@ -769,19 +770,19 @@ bool WebAssemblyFastISel::selectCall(const Instruction *I) {
769770
case MVT::i8:
770771
case MVT::i16:
771772
case MVT::i32:
772-
Opc = IsDirect ? WebAssembly::CALL_I32 : WebAssembly::PCALL_INDIRECT_I32;
773+
Opc = IsDirect ? WebAssembly::CALL_i32 : WebAssembly::PCALL_INDIRECT_i32;
773774
ResultReg = createResultReg(&WebAssembly::I32RegClass);
774775
break;
775776
case MVT::i64:
776-
Opc = IsDirect ? WebAssembly::CALL_I64 : WebAssembly::PCALL_INDIRECT_I64;
777+
Opc = IsDirect ? WebAssembly::CALL_i64 : WebAssembly::PCALL_INDIRECT_i64;
777778
ResultReg = createResultReg(&WebAssembly::I64RegClass);
778779
break;
779780
case MVT::f32:
780-
Opc = IsDirect ? WebAssembly::CALL_F32 : WebAssembly::PCALL_INDIRECT_F32;
781+
Opc = IsDirect ? WebAssembly::CALL_f32 : WebAssembly::PCALL_INDIRECT_f32;
781782
ResultReg = createResultReg(&WebAssembly::F32RegClass);
782783
break;
783784
case MVT::f64:
784-
Opc = IsDirect ? WebAssembly::CALL_F64 : WebAssembly::PCALL_INDIRECT_F64;
785+
Opc = IsDirect ? WebAssembly::CALL_f64 : WebAssembly::PCALL_INDIRECT_f64;
785786
ResultReg = createResultReg(&WebAssembly::F64RegClass);
786787
break;
787788
case MVT::v16i8:
@@ -815,8 +816,8 @@ bool WebAssemblyFastISel::selectCall(const Instruction *I) {
815816
ResultReg = createResultReg(&WebAssembly::V128RegClass);
816817
break;
817818
case MVT::ExceptRef:
818-
Opc = IsDirect ? WebAssembly::CALL_EXCEPT_REF
819-
: WebAssembly::PCALL_INDIRECT_EXCEPT_REF;
819+
Opc = IsDirect ? WebAssembly::CALL_ExceptRef
820+
: WebAssembly::PCALL_INDIRECT_ExceptRef;
820821
ResultReg = createResultReg(&WebAssembly::EXCEPT_REFRegClass);
821822
break;
822823
default:

Diff for: ‎llvm/lib/Target/WebAssembly/WebAssemblyISD.def

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
HANDLE_NODETYPE(CALL1)
1717
HANDLE_NODETYPE(CALL0)
18+
HANDLE_NODETYPE(RET_CALL)
1819
HANDLE_NODETYPE(RETURN)
1920
HANDLE_NODETYPE(ARGUMENT)
2021
// A wrapper node for TargetExternalSymbol, TargetGlobalAddress, and MCSymbol

Diff for: ‎llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp

+15-7
Original file line numberDiff line numberDiff line change
@@ -644,13 +644,14 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI,
644644
if (CLI.IsPatchPoint)
645645
fail(DL, DAG, "WebAssembly doesn't support patch point yet");
646646

647-
// WebAssembly doesn't currently support explicit tail calls. If they are
648-
// required, fail. Otherwise, just disable them.
649-
if ((CallConv == CallingConv::Fast && CLI.IsTailCall &&
650-
MF.getTarget().Options.GuaranteedTailCallOpt) ||
651-
(CLI.CS && CLI.CS.isMustTailCall()))
652-
fail(DL, DAG, "WebAssembly doesn't support tail call yet");
653-
CLI.IsTailCall = false;
647+
// Fail if tail calls are required but not enabled
648+
if (!Subtarget->hasTailCall()) {
649+
if ((CallConv == CallingConv::Fast && CLI.IsTailCall &&
650+
MF.getTarget().Options.GuaranteedTailCallOpt) ||
651+
(CLI.CS && CLI.CS.isMustTailCall()))
652+
fail(DL, DAG, "WebAssembly 'tail-call' feature not enabled");
653+
CLI.IsTailCall = false;
654+
}
654655

655656
SmallVectorImpl<ISD::InputArg> &Ins = CLI.Ins;
656657
if (Ins.size() > 1)
@@ -783,6 +784,13 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI,
783784
// registers.
784785
InTys.push_back(In.VT);
785786
}
787+
788+
if (CLI.IsTailCall) {
789+
// ret_calls do not return values to the current frame
790+
SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue);
791+
return DAG.getNode(WebAssemblyISD::RET_CALL, DL, NodeTys, Ops);
792+
}
793+
786794
InTys.push_back(MVT::Other);
787795
SDVTList InTyList = DAG.getVTList(InTys);
788796
SDValue Res =

Diff for: ‎llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td

+96-86
Original file line numberDiff line numberDiff line change
@@ -23,106 +23,110 @@ defm ADJCALLSTACKUP : NRI<(outs), (ins i32imm:$amt, i32imm:$amt2),
2323
[(WebAssemblycallseq_end timm:$amt, timm:$amt2)]>;
2424
} // Uses = [SP32, SP64], Defs = [SP32, SP64], isCodeGenOnly = 1
2525

26-
multiclass CALL<WebAssemblyRegClass vt, string prefix> {
27-
defm CALL_#vt : I<(outs vt:$dst), (ins function32_op:$callee, variable_ops),
28-
(outs), (ins function32_op:$callee),
29-
[(set vt:$dst, (WebAssemblycall1 (i32 imm:$callee)))],
30-
!strconcat(prefix, "call\t$dst, $callee"),
31-
!strconcat(prefix, "call\t$callee"),
32-
0x10>;
26+
multiclass CALL<ValueType vt, WebAssemblyRegClass rt, string prefix,
27+
list<Predicate> preds = []> {
28+
defm CALL_#vt :
29+
I<(outs rt:$dst), (ins function32_op:$callee, variable_ops),
30+
(outs), (ins function32_op:$callee),
31+
[(set (vt rt:$dst), (WebAssemblycall1 (i32 imm:$callee)))],
32+
!strconcat(prefix, "call\t$dst, $callee"),
33+
!strconcat(prefix, "call\t$callee"),
34+
0x10>,
35+
Requires<preds>;
3336

3437
let isCodeGenOnly = 1 in
35-
defm PCALL_INDIRECT_#vt : I<(outs vt:$dst), (ins I32:$callee, variable_ops),
36-
(outs), (ins I32:$callee),
37-
[(set vt:$dst, (WebAssemblycall1 I32:$callee))],
38-
"PSEUDO CALL INDIRECT\t$callee",
39-
"PSEUDO CALL INDIRECT\t$callee">;
38+
defm PCALL_INDIRECT_#vt :
39+
I<(outs rt:$dst), (ins I32:$callee, variable_ops),
40+
(outs), (ins I32:$callee),
41+
[(set (vt rt:$dst), (WebAssemblycall1 I32:$callee))],
42+
"PSEUDO CALL INDIRECT\t$callee",
43+
"PSEUDO CALL INDIRECT\t$callee">,
44+
Requires<preds>;
4045

41-
defm CALL_INDIRECT_#vt : I<(outs vt:$dst),
42-
(ins TypeIndex:$type, i32imm:$flags, variable_ops),
43-
(outs), (ins TypeIndex:$type, i32imm:$flags),
44-
[],
45-
!strconcat(prefix, "call_indirect\t$dst"),
46-
!strconcat(prefix, "call_indirect\t$type"),
47-
0x11>;
46+
defm CALL_INDIRECT_#vt :
47+
I<(outs rt:$dst),
48+
(ins TypeIndex:$type, i32imm:$flags, variable_ops),
49+
(outs), (ins TypeIndex:$type, i32imm:$flags),
50+
[],
51+
!strconcat(prefix, "call_indirect\t$dst"),
52+
!strconcat(prefix, "call_indirect\t$type"),
53+
0x11>,
54+
Requires<preds>;
4855
}
4956

50-
multiclass SIMD_CALL<ValueType vt, string prefix> {
57+
let Uses = [SP32, SP64], isCall = 1 in {
58+
defm "" : CALL<i32, I32, "i32.">;
59+
defm "" : CALL<i64, I64, "i64.">;
60+
defm "" : CALL<f32, F32, "f32.">;
61+
defm "" : CALL<f64, F64, "f64.">;
62+
defm "" : CALL<ExceptRef, EXCEPT_REF, "except_ref.", [HasExceptionHandling]>;
63+
defm "" : CALL<v16i8, V128, "v128.", [HasSIMD128]>;
64+
defm "" : CALL<v8i16, V128, "v128.", [HasSIMD128]>;
65+
defm "" : CALL<v4i32, V128, "v128.", [HasSIMD128]>;
66+
defm "" : CALL<v2i64, V128, "v128.", [HasSIMD128]>;
67+
defm "" : CALL<v4f32, V128, "v128.", [HasSIMD128]>;
68+
defm "" : CALL<v2f64, V128, "v128.", [HasSIMD128]>;
5169

52-
defm CALL_#vt : I<(outs V128:$dst), (ins function32_op:$callee, variable_ops),
53-
(outs), (ins function32_op:$callee),
54-
[(set (vt V128:$dst),
55-
(WebAssemblycall1 (i32 imm:$callee)))],
56-
!strconcat(prefix, "call\t$dst, $callee"),
57-
!strconcat(prefix, "call\t$callee"),
58-
0x10>,
59-
Requires<[HasSIMD128]>;
70+
let IsCanonical = 1 in {
71+
defm CALL_VOID :
72+
I<(outs), (ins function32_op:$callee, variable_ops),
73+
(outs), (ins function32_op:$callee),
74+
[(WebAssemblycall0 (i32 imm:$callee))],
75+
"call \t$callee", "call\t$callee", 0x10>;
6076

61-
let isCodeGenOnly = 1 in
62-
defm PCALL_INDIRECT_#vt : I<(outs V128:$dst),
63-
(ins I32:$callee, variable_ops),
64-
(outs), (ins I32:$callee),
65-
[(set (vt V128:$dst),
66-
(WebAssemblycall1 I32:$callee))],
67-
"PSEUDO CALL INDIRECT\t$callee",
68-
"PSEUDO CALL INDIRECT\t$callee">,
69-
Requires<[HasSIMD128]>;
77+
let isReturn = 1 in
78+
defm RET_CALL :
79+
I<(outs), (ins function32_op:$callee, variable_ops),
80+
(outs), (ins function32_op:$callee),
81+
[(WebAssemblyretcall (i32 imm:$callee))],
82+
"return_call \t$callee", "return_call\t$callee", 0x12>,
83+
Requires<[HasTailCall]>;
7084

71-
defm CALL_INDIRECT_#vt : I<(outs V128:$dst),
72-
(ins TypeIndex:$type, i32imm:$flags, variable_ops),
73-
(outs), (ins TypeIndex:$type, i32imm:$flags),
74-
[],
75-
!strconcat(prefix, "call_indirect\t$dst"),
76-
!strconcat(prefix, "call_indirect\t$type"),
77-
0x11>,
78-
Requires<[HasSIMD128]>;
79-
}
85+
let isCodeGenOnly = 1 in
86+
defm PCALL_INDIRECT_VOID :
87+
I<(outs), (ins I32:$callee, variable_ops),
88+
(outs), (ins I32:$callee),
89+
[(WebAssemblycall0 I32:$callee)],
90+
"PSEUDO CALL INDIRECT\t$callee",
91+
"PSEUDO CALL INDIRECT\t$callee">;
8092

81-
let Uses = [SP32, SP64], isCall = 1 in {
82-
defm "" : CALL<I32, "i32.">;
83-
defm "" : CALL<I64, "i64.">;
84-
defm "" : CALL<F32, "f32.">;
85-
defm "" : CALL<F64, "f64.">;
86-
defm "" : CALL<EXCEPT_REF, "except_ref.">;
87-
defm "" : SIMD_CALL<v16i8, "v128.">;
88-
defm "" : SIMD_CALL<v8i16, "v128.">;
89-
defm "" : SIMD_CALL<v4i32, "v128.">;
90-
defm "" : SIMD_CALL<v2i64, "v128.">;
91-
defm "" : SIMD_CALL<v4f32, "v128.">;
92-
defm "" : SIMD_CALL<v2f64, "v128.">;
93+
defm CALL_INDIRECT_VOID :
94+
I<(outs), (ins TypeIndex:$type, i32imm:$flags, variable_ops),
95+
(outs), (ins TypeIndex:$type, i32imm:$flags),
96+
[],
97+
"call_indirect\t", "call_indirect\t$type",
98+
0x11>;
9399

94-
let IsCanonical = 1 in {
95-
defm CALL_VOID : I<(outs), (ins function32_op:$callee, variable_ops),
96-
(outs), (ins function32_op:$callee),
97-
[(WebAssemblycall0 (i32 imm:$callee))],
98-
"call \t$callee", "call\t$callee", 0x10>;
100+
let isReturn = 1 in
101+
defm RET_CALL_INDIRECT :
102+
I<(outs), (ins TypeIndex:$type, i32imm:$flags, variable_ops),
103+
(outs), (ins TypeIndex:$type, i32imm:$flags),
104+
[],
105+
"return_call_indirect\t", "return_call_indirect\t$type",
106+
0x13>,
107+
Requires<[HasTailCall]>;
99108

100-
let isCodeGenOnly = 1 in
101-
defm PCALL_INDIRECT_VOID : I<(outs), (ins I32:$callee, variable_ops),
102-
(outs), (ins I32:$callee),
103-
[(WebAssemblycall0 I32:$callee)],
104-
"PSEUDO CALL INDIRECT\t$callee",
105-
"PSEUDO CALL INDIRECT\t$callee">;
109+
let isCodeGenOnly = 1, isReturn = 1 in
110+
defm PRET_CALL_INDIRECT:
111+
I<(outs), (ins I32:$callee, variable_ops),
112+
(outs), (ins I32:$callee),
113+
[(WebAssemblyretcall I32:$callee)],
114+
"PSEUDO RET_CALL INDIRECT\t$callee",
115+
"PSEUDO RET_CALL INDIRECT\t$callee">,
116+
Requires<[HasTailCall]>;
106117

107-
defm CALL_INDIRECT_VOID : I<(outs),
108-
(ins TypeIndex:$type, i32imm:$flags,
109-
variable_ops),
110-
(outs), (ins TypeIndex:$type, i32imm:$flags),
111-
[],
112-
"call_indirect\t", "call_indirect\t$type",
113-
0x11>;
114118
} // IsCanonical = 1
115119
} // Uses = [SP32,SP64], isCall = 1
116120

117121
// Patterns for matching a direct call to a global address.
118122
def : Pat<(i32 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))),
119-
(CALL_I32 tglobaladdr:$callee)>;
123+
(CALL_i32 tglobaladdr:$callee)>;
120124
def : Pat<(i64 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))),
121-
(CALL_I64 tglobaladdr:$callee)>;
125+
(CALL_i64 tglobaladdr:$callee)>;
122126
def : Pat<(f32 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))),
123-
(CALL_F32 tglobaladdr:$callee)>;
127+
(CALL_f32 tglobaladdr:$callee)>;
124128
def : Pat<(f64 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))),
125-
(CALL_F64 tglobaladdr:$callee)>;
129+
(CALL_f64 tglobaladdr:$callee)>;
126130
def : Pat<(v16i8 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))),
127131
(CALL_v16i8 tglobaladdr:$callee)>, Requires<[HasSIMD128]>;
128132
def : Pat<(v8i16 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))),
@@ -137,19 +141,22 @@ def : Pat<(v2f64 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))),
137141
(CALL_v2f64 tglobaladdr:$callee)>, Requires<[HasSIMD128]>;
138142
def : Pat<(ExceptRef
139143
(WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))),
140-
(CALL_EXCEPT_REF tglobaladdr:$callee)>;
144+
(CALL_ExceptRef tglobaladdr:$callee)>,
145+
Requires<[HasExceptionHandling]>;
141146
def : Pat<(WebAssemblycall0 (WebAssemblywrapper tglobaladdr:$callee)),
142147
(CALL_VOID tglobaladdr:$callee)>;
148+
def : Pat<(WebAssemblyretcall (WebAssemblywrapper tglobaladdr:$callee)),
149+
(RET_CALL tglobaladdr:$callee)>, Requires<[HasTailCall]>;
143150

144151
// Patterns for matching a direct call to an external symbol.
145152
def : Pat<(i32 (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))),
146-
(CALL_I32 texternalsym:$callee)>;
153+
(CALL_i32 texternalsym:$callee)>;
147154
def : Pat<(i64 (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))),
148-
(CALL_I64 texternalsym:$callee)>;
155+
(CALL_i64 texternalsym:$callee)>;
149156
def : Pat<(f32 (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))),
150-
(CALL_F32 texternalsym:$callee)>;
157+
(CALL_f32 texternalsym:$callee)>;
151158
def : Pat<(f64 (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))),
152-
(CALL_F64 texternalsym:$callee)>;
159+
(CALL_f64 texternalsym:$callee)>;
153160
def : Pat<(v16i8 (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))),
154161
(CALL_v16i8 texternalsym:$callee)>, Requires<[HasSIMD128]>;
155162
def : Pat<(v8i16 (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))),
@@ -164,6 +171,9 @@ def : Pat<(v2f64 (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))),
164171
(CALL_v2f64 texternalsym:$callee)>, Requires<[HasSIMD128]>;
165172
def : Pat<(ExceptRef
166173
(WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))),
167-
(CALL_EXCEPT_REF texternalsym:$callee)>;
174+
(CALL_ExceptRef texternalsym:$callee)>,
175+
Requires<[HasExceptionHandling]>;
168176
def : Pat<(WebAssemblycall0 (WebAssemblywrapper texternalsym:$callee)),
169177
(CALL_VOID texternalsym:$callee)>;
178+
def : Pat<(WebAssemblyretcall (WebAssemblywrapper texternalsym:$callee)),
179+
(RET_CALL texternalsym:$callee)>, Requires<[HasTailCall]>;

Diff for: ‎llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td

+3
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ def WebAssemblycall0 : SDNode<"WebAssemblyISD::CALL0",
9797
def WebAssemblycall1 : SDNode<"WebAssemblyISD::CALL1",
9898
SDT_WebAssemblyCall1,
9999
[SDNPHasChain, SDNPVariadic]>;
100+
def WebAssemblyretcall : SDNode<"WebAssemblyISD::RET_CALL",
101+
SDT_WebAssemblyCall0,
102+
[SDNPHasChain, SDNPVariadic]>;
100103
def WebAssemblybr_table : SDNode<"WebAssemblyISD::BR_TABLE",
101104
SDT_WebAssemblyBrTable,
102105
[SDNPHasChain, SDNPVariadic]>;

Diff for: ‎llvm/lib/Target/WebAssembly/WebAssemblyMemIntrinsicResults.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,8 @@ bool WebAssemblyMemIntrinsicResults::runOnMachineFunction(MachineFunction &MF) {
200200
switch (MI.getOpcode()) {
201201
default:
202202
break;
203-
case WebAssembly::CALL_I32:
204-
case WebAssembly::CALL_I64:
203+
case WebAssembly::CALL_i32:
204+
case WebAssembly::CALL_i64:
205205
Changed |= optimizeCall(MBB, MI, MRI, MDT, LIS, TLI, LibInfo);
206206
break;
207207
}

Diff for: ‎llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ bool WebAssemblyPeephole::runOnMachineFunction(MachineFunction &MF) {
128128
switch (MI.getOpcode()) {
129129
default:
130130
break;
131-
case WebAssembly::CALL_I32:
132-
case WebAssembly::CALL_I64: {
131+
case WebAssembly::CALL_i32:
132+
case WebAssembly::CALL_i64: {
133133
MachineOperand &Op1 = MI.getOperand(1);
134134
if (Op1.isSymbol()) {
135135
StringRef Name(Op1.getSymbolName());

Diff for: ‎llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp

+48-40
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,14 @@ bool WebAssembly::isCallDirect(const MachineInstr &MI) {
107107
switch (MI.getOpcode()) {
108108
case WebAssembly::CALL_VOID:
109109
case WebAssembly::CALL_VOID_S:
110-
case WebAssembly::CALL_I32:
111-
case WebAssembly::CALL_I32_S:
112-
case WebAssembly::CALL_I64:
113-
case WebAssembly::CALL_I64_S:
114-
case WebAssembly::CALL_F32:
115-
case WebAssembly::CALL_F32_S:
116-
case WebAssembly::CALL_F64:
117-
case WebAssembly::CALL_F64_S:
110+
case WebAssembly::CALL_i32:
111+
case WebAssembly::CALL_i32_S:
112+
case WebAssembly::CALL_i64:
113+
case WebAssembly::CALL_i64_S:
114+
case WebAssembly::CALL_f32:
115+
case WebAssembly::CALL_f32_S:
116+
case WebAssembly::CALL_f64:
117+
case WebAssembly::CALL_f64_S:
118118
case WebAssembly::CALL_v16i8:
119119
case WebAssembly::CALL_v16i8_S:
120120
case WebAssembly::CALL_v8i16:
@@ -127,8 +127,10 @@ bool WebAssembly::isCallDirect(const MachineInstr &MI) {
127127
case WebAssembly::CALL_v4f32_S:
128128
case WebAssembly::CALL_v2f64:
129129
case WebAssembly::CALL_v2f64_S:
130-
case WebAssembly::CALL_EXCEPT_REF:
131-
case WebAssembly::CALL_EXCEPT_REF_S:
130+
case WebAssembly::CALL_ExceptRef:
131+
case WebAssembly::CALL_ExceptRef_S:
132+
case WebAssembly::RET_CALL:
133+
case WebAssembly::RET_CALL_S:
132134
return true;
133135
default:
134136
return false;
@@ -139,14 +141,14 @@ bool WebAssembly::isCallIndirect(const MachineInstr &MI) {
139141
switch (MI.getOpcode()) {
140142
case WebAssembly::CALL_INDIRECT_VOID:
141143
case WebAssembly::CALL_INDIRECT_VOID_S:
142-
case WebAssembly::CALL_INDIRECT_I32:
143-
case WebAssembly::CALL_INDIRECT_I32_S:
144-
case WebAssembly::CALL_INDIRECT_I64:
145-
case WebAssembly::CALL_INDIRECT_I64_S:
146-
case WebAssembly::CALL_INDIRECT_F32:
147-
case WebAssembly::CALL_INDIRECT_F32_S:
148-
case WebAssembly::CALL_INDIRECT_F64:
149-
case WebAssembly::CALL_INDIRECT_F64_S:
144+
case WebAssembly::CALL_INDIRECT_i32:
145+
case WebAssembly::CALL_INDIRECT_i32_S:
146+
case WebAssembly::CALL_INDIRECT_i64:
147+
case WebAssembly::CALL_INDIRECT_i64_S:
148+
case WebAssembly::CALL_INDIRECT_f32:
149+
case WebAssembly::CALL_INDIRECT_f32_S:
150+
case WebAssembly::CALL_INDIRECT_f64:
151+
case WebAssembly::CALL_INDIRECT_f64_S:
150152
case WebAssembly::CALL_INDIRECT_v16i8:
151153
case WebAssembly::CALL_INDIRECT_v16i8_S:
152154
case WebAssembly::CALL_INDIRECT_v8i16:
@@ -159,8 +161,10 @@ bool WebAssembly::isCallIndirect(const MachineInstr &MI) {
159161
case WebAssembly::CALL_INDIRECT_v4f32_S:
160162
case WebAssembly::CALL_INDIRECT_v2f64:
161163
case WebAssembly::CALL_INDIRECT_v2f64_S:
162-
case WebAssembly::CALL_INDIRECT_EXCEPT_REF:
163-
case WebAssembly::CALL_INDIRECT_EXCEPT_REF_S:
164+
case WebAssembly::CALL_INDIRECT_ExceptRef:
165+
case WebAssembly::CALL_INDIRECT_ExceptRef_S:
166+
case WebAssembly::RET_CALL_INDIRECT:
167+
case WebAssembly::RET_CALL_INDIRECT_S:
164168
return true;
165169
default:
166170
return false;
@@ -173,15 +177,19 @@ unsigned WebAssembly::getCalleeOpNo(const MachineInstr &MI) {
173177
case WebAssembly::CALL_VOID_S:
174178
case WebAssembly::CALL_INDIRECT_VOID:
175179
case WebAssembly::CALL_INDIRECT_VOID_S:
180+
case WebAssembly::RET_CALL:
181+
case WebAssembly::RET_CALL_S:
182+
case WebAssembly::RET_CALL_INDIRECT:
183+
case WebAssembly::RET_CALL_INDIRECT_S:
176184
return 0;
177-
case WebAssembly::CALL_I32:
178-
case WebAssembly::CALL_I32_S:
179-
case WebAssembly::CALL_I64:
180-
case WebAssembly::CALL_I64_S:
181-
case WebAssembly::CALL_F32:
182-
case WebAssembly::CALL_F32_S:
183-
case WebAssembly::CALL_F64:
184-
case WebAssembly::CALL_F64_S:
185+
case WebAssembly::CALL_i32:
186+
case WebAssembly::CALL_i32_S:
187+
case WebAssembly::CALL_i64:
188+
case WebAssembly::CALL_i64_S:
189+
case WebAssembly::CALL_f32:
190+
case WebAssembly::CALL_f32_S:
191+
case WebAssembly::CALL_f64:
192+
case WebAssembly::CALL_f64_S:
185193
case WebAssembly::CALL_v16i8:
186194
case WebAssembly::CALL_v16i8_S:
187195
case WebAssembly::CALL_v8i16:
@@ -194,16 +202,16 @@ unsigned WebAssembly::getCalleeOpNo(const MachineInstr &MI) {
194202
case WebAssembly::CALL_v4f32_S:
195203
case WebAssembly::CALL_v2f64:
196204
case WebAssembly::CALL_v2f64_S:
197-
case WebAssembly::CALL_EXCEPT_REF:
198-
case WebAssembly::CALL_EXCEPT_REF_S:
199-
case WebAssembly::CALL_INDIRECT_I32:
200-
case WebAssembly::CALL_INDIRECT_I32_S:
201-
case WebAssembly::CALL_INDIRECT_I64:
202-
case WebAssembly::CALL_INDIRECT_I64_S:
203-
case WebAssembly::CALL_INDIRECT_F32:
204-
case WebAssembly::CALL_INDIRECT_F32_S:
205-
case WebAssembly::CALL_INDIRECT_F64:
206-
case WebAssembly::CALL_INDIRECT_F64_S:
205+
case WebAssembly::CALL_ExceptRef:
206+
case WebAssembly::CALL_ExceptRef_S:
207+
case WebAssembly::CALL_INDIRECT_i32:
208+
case WebAssembly::CALL_INDIRECT_i32_S:
209+
case WebAssembly::CALL_INDIRECT_i64:
210+
case WebAssembly::CALL_INDIRECT_i64_S:
211+
case WebAssembly::CALL_INDIRECT_f32:
212+
case WebAssembly::CALL_INDIRECT_f32_S:
213+
case WebAssembly::CALL_INDIRECT_f64:
214+
case WebAssembly::CALL_INDIRECT_f64_S:
207215
case WebAssembly::CALL_INDIRECT_v16i8:
208216
case WebAssembly::CALL_INDIRECT_v16i8_S:
209217
case WebAssembly::CALL_INDIRECT_v8i16:
@@ -216,8 +224,8 @@ unsigned WebAssembly::getCalleeOpNo(const MachineInstr &MI) {
216224
case WebAssembly::CALL_INDIRECT_v4f32_S:
217225
case WebAssembly::CALL_INDIRECT_v2f64:
218226
case WebAssembly::CALL_INDIRECT_v2f64_S:
219-
case WebAssembly::CALL_INDIRECT_EXCEPT_REF:
220-
case WebAssembly::CALL_INDIRECT_EXCEPT_REF_S:
227+
case WebAssembly::CALL_INDIRECT_ExceptRef:
228+
case WebAssembly::CALL_INDIRECT_ExceptRef_S:
221229
return 1;
222230
default:
223231
llvm_unreachable("Not a call instruction");

Diff for: ‎llvm/test/CodeGen/WebAssembly/call.ll

+19-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -mattr=+sign-ext,+simd128 | FileCheck %s
2-
; 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
1+
; 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
2+
; 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
3+
; 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
4+
; 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
35

46
; Test that basic call operations assemble as expected.
57

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

177179
; CHECK-LABEL: tail_call_void_nullary:
178180
; CHECK-NEXT: .functype tail_call_void_nullary () -> (){{$}}
179-
; CHECK-NEXT: {{^}} call void_nullary{{$}}
180-
; CHECK-NEXT: return{{$}}
181+
; NO-TAIL-NEXT: {{^}} call void_nullary{{$}}
182+
; NO-TAIL-NEXT: return{{$}}
183+
; SLOW-TAIL-NEXT: {{^}} return_call void_nullary{{$}}
184+
; FAST-TAIL-NEXT: {{^}} call void_nullary{{$}}
185+
; FAST-TAIL-NEXT: return{{$}}
181186
define void @tail_call_void_nullary() {
182187
tail call void @void_nullary()
183188
ret void
184189
}
185190

186191
; CHECK-LABEL: fastcc_tail_call_void_nullary:
187192
; CHECK-NEXT: .functype fastcc_tail_call_void_nullary () -> (){{$}}
188-
; CHECK-NEXT: {{^}} call void_nullary{{$}}
189-
; CHECK-NEXT: return{{$}}
193+
; NO-TAIL-NEXT: {{^}} call void_nullary{{$}}
194+
; NO-TAIL-NEXT: return{{$}}
195+
; SLOW-TAIL-NEXT: {{^}} return_call void_nullary{{$}}
196+
; FAST-TAIL-NEXT: {{^}} call void_nullary{{$}}
197+
; FAST-TAIL-NEXT: return{{$}}
190198
define void @fastcc_tail_call_void_nullary() {
191199
tail call fastcc void @void_nullary()
192200
ret void
193201
}
194202

195203
; CHECK-LABEL: coldcc_tail_call_void_nullary:
196204
; CHECK-NEXT: .functype coldcc_tail_call_void_nullary () -> (){{$}}
197-
; CHECK-NEXT: {{^}} call void_nullary{{$}}
198-
; CHECK-NEXT: return{{$}}
205+
; NO-TAIL-NEXT: {{^}} call void_nullary{{$}}
206+
; NO-TAIL-NEXT: return{{$}}
207+
; SLOW-TAIL-NEXT: {{^}} return_call void_nullary{{$}}
208+
; FAST-TAIL-NEXT: {{^}} call void_nullary{{$}}
209+
; FAST-TAIL-NEXT: return{{$}}
199210
define void @coldcc_tail_call_void_nullary() {
200211
tail call coldcc void @void_nullary()
201212
ret void

Diff for: ‎llvm/test/CodeGen/WebAssembly/tailcall.ll

+134-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,144 @@
1-
; 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
1+
; 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
2+
; 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
23

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

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

9+
%fn = type <{i32 (%fn, i32, i32)*}>
10+
declare i1 @foo(i1)
11+
declare i1 @bar(i1)
12+
13+
; CHECK-LABEL: recursive_notail_nullary:
14+
; CHECK: {{^}} call recursive_notail_nullary{{$}}
15+
; CHECK-NEXT: return
16+
define void @recursive_notail_nullary() {
17+
notail call void @recursive_notail_nullary()
18+
ret void
19+
}
20+
21+
; CHECK-LABEL: recursive_musttail_nullary:
22+
; CHECK: return_call recursive_musttail_nullary{{$}}
23+
define void @recursive_musttail_nullary() {
24+
musttail call void @recursive_musttail_nullary()
25+
ret void
26+
}
27+
28+
; CHECK-LABEL: recursive_tail_nullary:
29+
; SLOW: return_call recursive_tail_nullary{{$}}
30+
; FAST: {{^}} call recursive_tail_nullary{{$}}
31+
; FAST-NEXT: return{{$}}
32+
define void @recursive_tail_nullary() {
33+
tail call void @recursive_tail_nullary()
34+
ret void
35+
}
36+
37+
; CHECK-LABEL: recursive_notail:
38+
; CHECK: i32.call $push[[L:[0-9]+]]=, recursive_notail, $0, $1{{$}}
39+
; CHECK-NEXT: return $pop[[L]]{{$}}
40+
define i32 @recursive_notail(i32 %x, i32 %y) {
41+
%v = notail call i32 @recursive_notail(i32 %x, i32 %y)
42+
ret i32 %v
43+
}
44+
45+
; CHECK-LABEL: recursive_musttail:
46+
; CHECK: return_call recursive_musttail, $0, $1{{$}}
47+
define i32 @recursive_musttail(i32 %x, i32 %y) {
48+
%v = musttail call i32 @recursive_musttail(i32 %x, i32 %y)
49+
ret i32 %v
50+
}
51+
952
; CHECK-LABEL: recursive_tail:
10-
; CHECK: i32.call $push[[L0:[0-9]+]]=, recursive_tail{{$}}
11-
; CHECK-NEXT: return $pop[[L0]]{{$}}
12-
define i32 @recursive_tail() {
13-
%v = tail call i32 @recursive_tail()
53+
; SLOW: return_call recursive_tail, $0, $1{{$}}
54+
; FAST: i32.call $push[[L:[0-9]+]]=, recursive_tail, $0, $1{{$}}
55+
; FAST-NEXT: return $pop[[L]]{{$}}
56+
define i32 @recursive_tail(i32 %x, i32 %y) {
57+
%v = tail call i32 @recursive_tail(i32 %x, i32 %y)
58+
ret i32 %v
59+
}
60+
61+
; CHECK-LABEL: indirect_notail:
62+
; CHECK: i32.call_indirect $push[[L:[0-9]+]]=, $0, $1, $2, $0{{$}}
63+
; CHECK-NEXT: return $pop[[L]]{{$}}
64+
define i32 @indirect_notail(%fn %f, i32 %x, i32 %y) {
65+
%p = extractvalue %fn %f, 0
66+
%v = notail call i32 %p(%fn %f, i32 %x, i32 %y)
67+
ret i32 %v
68+
}
69+
70+
; CHECK-LABEL: indirect_musttail:
71+
; CHECK: return_call_indirect , $0, $1, $2, $0{{$}}
72+
define i32 @indirect_musttail(%fn %f, i32 %x, i32 %y) {
73+
%p = extractvalue %fn %f, 0
74+
%v = musttail call i32 %p(%fn %f, i32 %x, i32 %y)
75+
ret i32 %v
76+
}
77+
78+
; CHECK-LABEL: indirect_tail:
79+
; CHECK: return_call_indirect , $0, $1, $2, $0{{$}}
80+
define i32 @indirect_tail(%fn %f, i32 %x, i32 %y) {
81+
%p = extractvalue %fn %f, 0
82+
%v = tail call i32 %p(%fn %f, i32 %x, i32 %y)
83+
ret i32 %v
84+
}
85+
86+
; CHECK-LABEL: choice_notail:
87+
; CHECK: i32.call_indirect $push[[L:[0-9]+]]=, $0, $pop{{[0-9]+}}{{$}}
88+
; CHECK-NEXT: return $pop[[L]]{{$}}
89+
define i1 @choice_notail(i1 %x) {
90+
%p = select i1 %x, i1 (i1)* @foo, i1 (i1)* @bar
91+
%v = notail call i1 %p(i1 %x)
92+
ret i1 %v
93+
}
94+
95+
; CHECK-LABEL: choice_musttail:
96+
; CHECK: return_call_indirect , $0, $pop{{[0-9]+}}{{$}}
97+
define i1 @choice_musttail(i1 %x) {
98+
%p = select i1 %x, i1 (i1)* @foo, i1 (i1)* @bar
99+
%v = musttail call i1 %p(i1 %x)
100+
ret i1 %v
101+
}
102+
103+
; CHECK-LABEL: choice_tail:
104+
; SLOW: return_call_indirect , $0, $pop{{[0-9]+}}{{$}}
105+
; FAST: i32.call_indirect $push[[L:[0-9]+]]=, $0, $pop{{[0-9]+}}{{$}}
106+
; FAST: return $pop[[L]]{{$}}
107+
define i1 @choice_tail(i1 %x) {
108+
%p = select i1 %x, i1 (i1)* @foo, i1 (i1)* @bar
109+
%v = tail call i1 %p(i1 %x)
110+
ret i1 %v
111+
}
112+
113+
; It is an LLVM validation error for a 'musttail' callee to have a different
114+
; prototype than its caller, so the following tests can only be done with
115+
; 'tail'.
116+
117+
; CHECK-LABEL: mismatched_prototypes:
118+
; SLOW: return_call baz, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}}
119+
; FAST: i32.call $push[[L:[0-9]+]]=, baz, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}}
120+
; FAST: return $pop[[L]]{{$}}
121+
declare i32 @baz(i32, i32, i32)
122+
define i32 @mismatched_prototypes() {
123+
%v = tail call i32 @baz(i32 0, i32 42, i32 6)
124+
ret i32 %v
125+
}
126+
127+
; CHECK-LABEL: mismatched_byval:
128+
; CHECK: i32.store
129+
; CHECK: return_call quux, $pop{{[0-9]+}}{{$}}
130+
declare i32 @quux(i32* byval)
131+
define i32 @mismatched_byval(i32* %x) {
132+
%v = tail call i32 @quux(i32* byval %x)
133+
ret i32 %v
134+
}
135+
136+
; CHECK-LABEL: varargs:
137+
; CHECK: i32.store
138+
; CHECK: return_call var, $1{{$}}
139+
declare i32 @var(...)
140+
define i32 @varargs(i32 %x) {
141+
%v = tail call i32 (...) @var(i32 %x)
14142
ret i32 %v
15143
}
16144

Diff for: ‎llvm/test/DebugInfo/WebAssembly/dbg-value-move-reg-stackify.mir

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
# CHECK: body:
44
# CHECK: %1:i32 = I32_WRAP_I64 %0,
55
# CHECK-NEXT: DBG_VALUE %1,
6-
# CHECK-NEXT: %1:i32 = CALL_I32 @bar,
6+
# CHECK-NEXT: %1:i32 = CALL_i32 @bar,
77
# CHECK-NEXT: DBG_VALUE %1,
8-
# CHECK-NEXT: %[[NEWREG:.*]]:i32 = CALL_I32 @bar,
8+
# CHECK-NEXT: %[[NEWREG:.*]]:i32 = CALL_i32 @bar,
99
# CHECK-NEXT: DBG_VALUE %[[NEWREG]],
1010
# CHECK-NEXT: CALL_VOID @foo, %[[NEWREG]],
1111

@@ -50,9 +50,9 @@ body: |
5050
%0:i64 = ARGUMENT_i64 0, implicit $arguments
5151
%1:i32 = I32_WRAP_I64 %0:i64, implicit-def dead $arguments
5252
DBG_VALUE %1:i32, $noreg, !10, !DIExpression(), debug-location !13; <unknown>:357:12 line no:357
53-
%1:i32 = CALL_I32 @bar, implicit-def dead $arguments, implicit $sp32, implicit $sp64
53+
%1:i32 = CALL_i32 @bar, implicit-def dead $arguments, implicit $sp32, implicit $sp64
5454
DBG_VALUE %1:i32, $noreg, !11, !DIExpression(), debug-location !14; <unknown>:357:12 line no:357
55-
%1:i32 = CALL_I32 @bar, implicit-def dead $arguments, implicit $sp32, implicit $sp64
55+
%1:i32 = CALL_i32 @bar, implicit-def dead $arguments, implicit $sp32, implicit $sp64
5656
DBG_VALUE %1:i32, $noreg, !12, !DIExpression(), debug-location !15; <unknown>:357:12 line no:357
5757
CALL_VOID @foo, %1:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
5858
RETURN_VOID implicit-def dead $arguments

Diff for: ‎llvm/test/MC/WebAssembly/tail-call-encodings.s

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# RUN: llvm-mc -show-encoding -triple=wasm32-unkown-unknown -mattr=+tail-call < %s | FileCheck %s
2+
3+
bar1:
4+
.functype bar1 () -> ()
5+
end_function
6+
7+
foo1:
8+
.functype foo1 () -> ()
9+
10+
# CHECK: return_call bar1 # encoding: [0x12,
11+
# CHECK-NEXT: fixup A - offset: 1, value: bar1, kind: fixup_uleb128_i32
12+
return_call bar1
13+
14+
end_function
15+
16+
foo2:
17+
.functype foo2 () -> ()
18+
19+
# CHECK: return_call_indirect 0 # encoding: [0x13,0x00,0x00]
20+
return_call_indirect 0
21+
22+
end_function

0 commit comments

Comments
 (0)
Please sign in to comment.