Skip to content

Commit 00f9e5a

Browse files
committed
[WebAssembly] Make returns variadic
Summary: This is necessary and sufficient to get simple cases of multiple return working with multivalue enabled. More complex cases will require block and loop signatures to be generalized to potentially be type indices as well. Reviewers: aheejin, dschuff Subscribers: sbc100, jgravelle-google, hiraditya, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D68684 llvm-svn: 374235
1 parent ffb26d9 commit 00f9e5a

20 files changed

+108
-202
lines changed

llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, raw_ostream &OS,
5252

5353
// Print any additional variadic operands.
5454
const MCInstrDesc &Desc = MII.get(MI->getOpcode());
55-
if (Desc.isVariadic())
55+
if (Desc.isVariadic()) {
56+
if (Desc.getNumOperands() == 0 && MI->getNumOperands() > 0)
57+
OS << "\t";
5658
for (auto I = Desc.getNumOperands(), E = MI->getNumOperands(); I < E; ++I) {
5759
// FIXME: For CALL_INDIRECT_VOID, don't print a leading comma, because
5860
// we have an extra flags operand which is not currently printed, for
@@ -63,6 +65,7 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, raw_ostream &OS,
6365
OS << ", ";
6466
printOperand(MI, I, OS);
6567
}
68+
}
6669

6770
// Print any added annotation.
6871
printAnnotation(OS, Annot);

llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp

+3-31
Original file line numberDiff line numberDiff line change
@@ -332,43 +332,15 @@ void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) {
332332
// These represent values which are live into the function entry, so there's
333333
// no instruction to emit.
334334
break;
335-
case WebAssembly::FALLTHROUGH_RETURN_I32:
336-
case WebAssembly::FALLTHROUGH_RETURN_I32_S:
337-
case WebAssembly::FALLTHROUGH_RETURN_I64:
338-
case WebAssembly::FALLTHROUGH_RETURN_I64_S:
339-
case WebAssembly::FALLTHROUGH_RETURN_F32:
340-
case WebAssembly::FALLTHROUGH_RETURN_F32_S:
341-
case WebAssembly::FALLTHROUGH_RETURN_F64:
342-
case WebAssembly::FALLTHROUGH_RETURN_F64_S:
343-
case WebAssembly::FALLTHROUGH_RETURN_v16i8:
344-
case WebAssembly::FALLTHROUGH_RETURN_v16i8_S:
345-
case WebAssembly::FALLTHROUGH_RETURN_v8i16:
346-
case WebAssembly::FALLTHROUGH_RETURN_v8i16_S:
347-
case WebAssembly::FALLTHROUGH_RETURN_v4i32:
348-
case WebAssembly::FALLTHROUGH_RETURN_v4i32_S:
349-
case WebAssembly::FALLTHROUGH_RETURN_v2i64:
350-
case WebAssembly::FALLTHROUGH_RETURN_v2i64_S:
351-
case WebAssembly::FALLTHROUGH_RETURN_v4f32:
352-
case WebAssembly::FALLTHROUGH_RETURN_v4f32_S:
353-
case WebAssembly::FALLTHROUGH_RETURN_v2f64:
354-
case WebAssembly::FALLTHROUGH_RETURN_v2f64_S: {
335+
case WebAssembly::FALLTHROUGH_RETURN: {
355336
// These instructions represent the implicit return at the end of a
356-
// function body. Always pops one value off the stack.
337+
// function body.
357338
if (isVerbose()) {
358-
OutStreamer->AddComment("fallthrough-return-value");
339+
OutStreamer->AddComment("fallthrough-return");
359340
OutStreamer->AddBlankLine();
360341
}
361342
break;
362343
}
363-
case WebAssembly::FALLTHROUGH_RETURN_VOID:
364-
case WebAssembly::FALLTHROUGH_RETURN_VOID_S:
365-
// This instruction represents the implicit return at the end of a
366-
// function body with no return value.
367-
if (isVerbose()) {
368-
OutStreamer->AddComment("fallthrough-return-void");
369-
OutStreamer->AddBlankLine();
370-
}
371-
break;
372344
case WebAssembly::COMPILER_FENCE:
373345
// This is a compiler barrier that prevents instruction reordering during
374346
// backend compilation, and should not be emitted.

llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -1227,11 +1227,11 @@ getDepth(const SmallVectorImpl<const MachineBasicBlock *> &Stack,
12271227
/// checks for such cases and fixes up the signatures.
12281228
void WebAssemblyCFGStackify::fixEndsAtEndOfFunction(MachineFunction &MF) {
12291229
const auto &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
1230-
assert(MFI.getResults().size() <= 1);
12311230

12321231
if (MFI.getResults().empty())
12331232
return;
12341233

1234+
// TODO: Generalize from value types to function types for multivalue
12351235
WebAssembly::ExprType RetType;
12361236
switch (MFI.getResults().front().SimpleTy) {
12371237
case MVT::i32:
@@ -1266,10 +1266,14 @@ void WebAssemblyCFGStackify::fixEndsAtEndOfFunction(MachineFunction &MF) {
12661266
if (MI.isPosition() || MI.isDebugInstr())
12671267
continue;
12681268
if (MI.getOpcode() == WebAssembly::END_BLOCK) {
1269+
if (MFI.getResults().size() > 1)
1270+
report_fatal_error("Multivalue block signatures not implemented yet");
12691271
EndToBegin[&MI]->getOperand(0).setImm(int32_t(RetType));
12701272
continue;
12711273
}
12721274
if (MI.getOpcode() == WebAssembly::END_LOOP) {
1275+
if (MFI.getResults().size() > 1)
1276+
report_fatal_error("Multivalue loop signatures not implemented yet");
12731277
EndToBegin[&MI]->getOperand(0).setImm(int32_t(RetType));
12741278
continue;
12751279
}

llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp

+8-24
Original file line numberDiff line numberDiff line change
@@ -1302,51 +1302,33 @@ bool WebAssemblyFastISel::selectRet(const Instruction *I) {
13021302

13031303
if (Ret->getNumOperands() == 0) {
13041304
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc,
1305-
TII.get(WebAssembly::RETURN_VOID));
1305+
TII.get(WebAssembly::RETURN));
13061306
return true;
13071307
}
13081308

1309+
// TODO: support multiple return in FastISel
1310+
if (Ret->getNumOperands() > 1)
1311+
return false;
1312+
13091313
Value *RV = Ret->getOperand(0);
13101314
if (!Subtarget->hasSIMD128() && RV->getType()->isVectorTy())
13111315
return false;
13121316

1313-
unsigned Opc;
13141317
switch (getSimpleType(RV->getType())) {
13151318
case MVT::i1:
13161319
case MVT::i8:
13171320
case MVT::i16:
13181321
case MVT::i32:
1319-
Opc = WebAssembly::RETURN_I32;
1320-
break;
13211322
case MVT::i64:
1322-
Opc = WebAssembly::RETURN_I64;
1323-
break;
13241323
case MVT::f32:
1325-
Opc = WebAssembly::RETURN_F32;
1326-
break;
13271324
case MVT::f64:
1328-
Opc = WebAssembly::RETURN_F64;
1329-
break;
13301325
case MVT::v16i8:
1331-
Opc = WebAssembly::RETURN_v16i8;
1332-
break;
13331326
case MVT::v8i16:
1334-
Opc = WebAssembly::RETURN_v8i16;
1335-
break;
13361327
case MVT::v4i32:
1337-
Opc = WebAssembly::RETURN_v4i32;
1338-
break;
13391328
case MVT::v2i64:
1340-
Opc = WebAssembly::RETURN_v2i64;
1341-
break;
13421329
case MVT::v4f32:
1343-
Opc = WebAssembly::RETURN_v4f32;
1344-
break;
13451330
case MVT::v2f64:
1346-
Opc = WebAssembly::RETURN_v2f64;
1347-
break;
13481331
case MVT::exnref:
1349-
Opc = WebAssembly::RETURN_EXNREF;
13501332
break;
13511333
default:
13521334
return false;
@@ -1363,7 +1345,9 @@ bool WebAssemblyFastISel::selectRet(const Instruction *I) {
13631345
if (Reg == 0)
13641346
return false;
13651347

1366-
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc)).addReg(Reg);
1348+
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc,
1349+
TII.get(WebAssembly::RETURN))
1350+
.addReg(Reg);
13671351
return true;
13681352
}
13691353

llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -852,16 +852,17 @@ bool WebAssemblyTargetLowering::CanLowerReturn(
852852
CallingConv::ID /*CallConv*/, MachineFunction & /*MF*/, bool /*IsVarArg*/,
853853
const SmallVectorImpl<ISD::OutputArg> &Outs,
854854
LLVMContext & /*Context*/) const {
855-
// WebAssembly can't currently handle returning tuples.
856-
return Outs.size() <= 1;
855+
// WebAssembly can only handle returning tuples with multivalue enabled
856+
return Subtarget->hasMultivalue() || Outs.size() <= 1;
857857
}
858858

859859
SDValue WebAssemblyTargetLowering::LowerReturn(
860860
SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/,
861861
const SmallVectorImpl<ISD::OutputArg> &Outs,
862862
const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL,
863863
SelectionDAG &DAG) const {
864-
assert(Outs.size() <= 1 && "WebAssembly can only return up to one value");
864+
assert(Subtarget->hasMultivalue() ||
865+
Outs.size() <= 1 && "MVP WebAssembly can only return up to one value");
865866
if (!callingConvSupported(CallConv))
866867
fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions");
867868

llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td

+9-39
Original file line numberDiff line numberDiff line change
@@ -84,49 +84,19 @@ let isTerminator = 1, isBarrier = 1 in
8484
defm END_FUNCTION : NRI<(outs), (ins), [], "end_function", 0x0b>;
8585
} // Uses = [VALUE_STACK], Defs = [VALUE_STACK]
8686

87-
multiclass RETURN<WebAssemblyRegClass vt> {
88-
defm RETURN_#vt : I<(outs), (ins vt:$val), (outs), (ins),
89-
[(WebAssemblyreturn vt:$val)],
90-
"return \t$val", "return", 0x0f>;
91-
// Equivalent to RETURN_#vt, for use at the end of a function when wasm
92-
// semantics return by falling off the end of the block.
93-
let isCodeGenOnly = 1 in
94-
defm FALLTHROUGH_RETURN_#vt : I<(outs), (ins vt:$val), (outs), (ins), []>;
95-
}
96-
97-
multiclass SIMD_RETURN<ValueType vt> {
98-
defm RETURN_#vt : I<(outs), (ins V128:$val), (outs), (ins),
99-
[(WebAssemblyreturn (vt V128:$val))],
100-
"return \t$val", "return", 0x0f>,
101-
Requires<[HasSIMD128]>;
102-
// Equivalent to RETURN_#vt, for use at the end of a function when wasm
103-
// semantics return by falling off the end of the block.
104-
let isCodeGenOnly = 1 in
105-
defm FALLTHROUGH_RETURN_#vt : I<(outs), (ins V128:$val), (outs), (ins),
106-
[]>,
107-
Requires<[HasSIMD128]>;
108-
}
10987

11088
let isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in {
11189

11290
let isReturn = 1 in {
113-
defm "": RETURN<I32>;
114-
defm "": RETURN<I64>;
115-
defm "": RETURN<F32>;
116-
defm "": RETURN<F64>;
117-
defm "": RETURN<EXNREF>;
118-
defm "": SIMD_RETURN<v16i8>;
119-
defm "": SIMD_RETURN<v8i16>;
120-
defm "": SIMD_RETURN<v4i32>;
121-
defm "": SIMD_RETURN<v2i64>;
122-
defm "": SIMD_RETURN<v4f32>;
123-
defm "": SIMD_RETURN<v2f64>;
124-
125-
defm RETURN_VOID : NRI<(outs), (ins), [(WebAssemblyreturn)], "return", 0x0f>;
126-
127-
// This is to RETURN_VOID what FALLTHROUGH_RETURN_#vt is to RETURN_#vt.
128-
let isCodeGenOnly = 1 in
129-
defm FALLTHROUGH_RETURN_VOID : NRI<(outs), (ins), []>;
91+
92+
defm RETURN : I<(outs), (ins variable_ops), (outs), (ins),
93+
[(WebAssemblyreturn)],
94+
"return", "return", 0x0f>;
95+
// Equivalent to RETURN, for use at the end of a function when wasm
96+
// semantics return by falling off the end of the block.
97+
let isCodeGenOnly = 1 in
98+
defm FALLTHROUGH_RETURN : I<(outs), (ins variable_ops), (outs), (ins), []>;
99+
130100
} // isReturn = 1
131101

132102
defm UNREACHABLE : NRI<(outs), (ins), [(trap)], "unreachable", 0x00>;

llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td

+2-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ def WebAssemblybr_table : SDNode<"WebAssemblyISD::BR_TABLE",
106106
def WebAssemblyargument : SDNode<"WebAssemblyISD::ARGUMENT",
107107
SDT_WebAssemblyArgument>;
108108
def WebAssemblyreturn : SDNode<"WebAssemblyISD::RETURN",
109-
SDT_WebAssemblyReturn, [SDNPHasChain]>;
109+
SDT_WebAssemblyReturn,
110+
[SDNPHasChain, SDNPVariadic]>;
110111
def WebAssemblywrapper : SDNode<"WebAssemblyISD::Wrapper",
111112
SDT_WebAssemblyWrapper>;
112113
def WebAssemblywrapperPIC : SDNode<"WebAssemblyISD::WrapperPIC",

llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp

+6-4
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@ void llvm::computeSignatureVTs(const FunctionType *Ty, const Function &F,
4949
computeLegalValueVTs(F, TM, Ty->getReturnType(), Results);
5050

5151
MVT PtrVT = MVT::getIntegerVT(TM.createDataLayout().getPointerSizeInBits());
52-
if (Results.size() > 1) {
53-
// WebAssembly currently can't lower returns of multiple values without
54-
// demoting to sret (see WebAssemblyTargetLowering::CanLowerReturn). So
55-
// replace multiple return values with a pointer parameter.
52+
if (Results.size() > 1 &&
53+
!TM.getSubtarget<WebAssemblySubtarget>(F).hasMultivalue()) {
54+
// WebAssembly can't lower returns of multiple values without demoting to
55+
// sret unless multivalue is enabled (see
56+
// WebAssemblyTargetLowering::CanLowerReturn). So replace multiple return
57+
// values with a poitner parameter.
5658
Results.clear();
5759
Params.push_back(PtrVT);
5860
}

llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp

+32-64
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,7 @@ static bool maybeRewriteToFallthrough(MachineInstr &MI, MachineBasicBlock &MBB,
7575
const MachineFunction &MF,
7676
WebAssemblyFunctionInfo &MFI,
7777
MachineRegisterInfo &MRI,
78-
const WebAssemblyInstrInfo &TII,
79-
unsigned FallthroughOpc,
80-
unsigned CopyLocalOpc) {
78+
const WebAssemblyInstrInfo &TII) {
8179
if (DisableWebAssemblyFallthroughReturnOpt)
8280
return false;
8381
if (&MBB != &MF.back())
@@ -90,22 +88,44 @@ static bool maybeRewriteToFallthrough(MachineInstr &MI, MachineBasicBlock &MBB,
9088
if (&MI != &*End)
9189
return false;
9290

93-
if (FallthroughOpc != WebAssembly::FALLTHROUGH_RETURN_VOID) {
94-
// If the operand isn't stackified, insert a COPY to read the operand and
95-
// stackify it.
96-
MachineOperand &MO = MI.getOperand(0);
91+
for (auto &MO : MI.explicit_operands()) {
92+
// If the operand isn't stackified, insert a COPY to read the operands and
93+
// stackify them.
9794
Register Reg = MO.getReg();
9895
if (!MFI.isVRegStackified(Reg)) {
99-
Register NewReg = MRI.createVirtualRegister(MRI.getRegClass(Reg));
96+
unsigned CopyLocalOpc;
97+
const TargetRegisterClass *RegClass = MRI.getRegClass(Reg);
98+
switch (RegClass->getID()) {
99+
case WebAssembly::I32RegClassID:
100+
CopyLocalOpc = WebAssembly::COPY_I32;
101+
break;
102+
case WebAssembly::I64RegClassID:
103+
CopyLocalOpc = WebAssembly::COPY_I64;
104+
break;
105+
case WebAssembly::F32RegClassID:
106+
CopyLocalOpc = WebAssembly::COPY_F32;
107+
break;
108+
case WebAssembly::F64RegClassID:
109+
CopyLocalOpc = WebAssembly::COPY_F64;
110+
break;
111+
case WebAssembly::V128RegClassID:
112+
CopyLocalOpc = WebAssembly::COPY_V128;
113+
break;
114+
case WebAssembly::EXNREFRegClassID:
115+
CopyLocalOpc = WebAssembly::COPY_EXNREF;
116+
break;
117+
default:
118+
llvm_unreachable("Unexpected register class for return operand");
119+
}
120+
Register NewReg = MRI.createVirtualRegister(RegClass);
100121
BuildMI(MBB, MI, MI.getDebugLoc(), TII.get(CopyLocalOpc), NewReg)
101122
.addReg(Reg);
102123
MO.setReg(NewReg);
103124
MFI.stackifyVReg(NewReg);
104125
}
105126
}
106127

107-
// Rewrite the return.
108-
MI.setDesc(TII.get(FallthroughOpc));
128+
MI.setDesc(TII.get(WebAssembly::FALLTHROUGH_RETURN));
109129
return true;
110130
}
111131

@@ -157,60 +177,8 @@ bool WebAssemblyPeephole::runOnMachineFunction(MachineFunction &MF) {
157177
break;
158178
}
159179
// Optimize away an explicit void return at the end of the function.
160-
case WebAssembly::RETURN_I32:
161-
Changed |= maybeRewriteToFallthrough(
162-
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_I32,
163-
WebAssembly::COPY_I32);
164-
break;
165-
case WebAssembly::RETURN_I64:
166-
Changed |= maybeRewriteToFallthrough(
167-
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_I64,
168-
WebAssembly::COPY_I64);
169-
break;
170-
case WebAssembly::RETURN_F32:
171-
Changed |= maybeRewriteToFallthrough(
172-
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_F32,
173-
WebAssembly::COPY_F32);
174-
break;
175-
case WebAssembly::RETURN_F64:
176-
Changed |= maybeRewriteToFallthrough(
177-
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_F64,
178-
WebAssembly::COPY_F64);
179-
break;
180-
case WebAssembly::RETURN_v16i8:
181-
Changed |= maybeRewriteToFallthrough(
182-
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v16i8,
183-
WebAssembly::COPY_V128);
184-
break;
185-
case WebAssembly::RETURN_v8i16:
186-
Changed |= maybeRewriteToFallthrough(
187-
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v8i16,
188-
WebAssembly::COPY_V128);
189-
break;
190-
case WebAssembly::RETURN_v4i32:
191-
Changed |= maybeRewriteToFallthrough(
192-
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v4i32,
193-
WebAssembly::COPY_V128);
194-
break;
195-
case WebAssembly::RETURN_v2i64:
196-
Changed |= maybeRewriteToFallthrough(
197-
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v2i64,
198-
WebAssembly::COPY_V128);
199-
break;
200-
case WebAssembly::RETURN_v4f32:
201-
Changed |= maybeRewriteToFallthrough(
202-
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v4f32,
203-
WebAssembly::COPY_V128);
204-
break;
205-
case WebAssembly::RETURN_v2f64:
206-
Changed |= maybeRewriteToFallthrough(
207-
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v2f64,
208-
WebAssembly::COPY_V128);
209-
break;
210-
case WebAssembly::RETURN_VOID:
211-
Changed |= maybeRewriteToFallthrough(
212-
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_VOID,
213-
WebAssembly::INSTRUCTION_LIST_END);
180+
case WebAssembly::RETURN:
181+
Changed |= maybeRewriteToFallthrough(MI, MBB, MF, MFI, MRI, TII);
214182
break;
215183
}
216184

0 commit comments

Comments
 (0)