Skip to content

Commit 2cb2707

Browse files
committed
[WebAssembly] Allow multivalue types in block signature operands
Summary: Renames `ExprType` to the more apt `BlockType` and adds a variant for multivalue blocks. Currently non-void blocks are only generated at the end of functions where the block return type needs to agree with the function return type, and that remains true for multivalue blocks. That invariant means that the actual signature does not need to be stored in the block signature `MachineOperand` because it can be inferred by `WebAssemblyMCInstLower` from the return type of the parent function. `WebAssemblyMCInstLower` continues to lower block signature operands to immediates when possible but lowers multivalue signatures to function type symbols. The AsmParser and Disassembler are updated to handle multivalue block types as well. Reviewers: aheejin, dschuff, aardappel Subscribers: sbc100, jgravelle-google, hiraditya, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D68889 llvm-svn: 374933
1 parent 0650355 commit 2cb2707

15 files changed

+181
-117
lines changed

llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -313,16 +313,17 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
313313
return Optional<wasm::ValType>();
314314
}
315315

316-
WebAssembly::ExprType parseBlockType(StringRef ID) {
317-
return StringSwitch<WebAssembly::ExprType>(ID)
318-
.Case("i32", WebAssembly::ExprType::I32)
319-
.Case("i64", WebAssembly::ExprType::I64)
320-
.Case("f32", WebAssembly::ExprType::F32)
321-
.Case("f64", WebAssembly::ExprType::F64)
322-
.Case("v128", WebAssembly::ExprType::V128)
323-
.Case("exnref", WebAssembly::ExprType::Exnref)
324-
.Case("void", WebAssembly::ExprType::Void)
325-
.Default(WebAssembly::ExprType::Invalid);
316+
WebAssembly::BlockType parseBlockType(StringRef ID) {
317+
// Multivalue block types are handled separately in parseSignature
318+
return StringSwitch<WebAssembly::BlockType>(ID)
319+
.Case("i32", WebAssembly::BlockType::I32)
320+
.Case("i64", WebAssembly::BlockType::I64)
321+
.Case("f32", WebAssembly::BlockType::F32)
322+
.Case("f64", WebAssembly::BlockType::F64)
323+
.Case("v128", WebAssembly::BlockType::V128)
324+
.Case("exnref", WebAssembly::BlockType::Exnref)
325+
.Case("void", WebAssembly::BlockType::Void)
326+
.Default(WebAssembly::BlockType::Invalid);
326327
}
327328

328329
bool parseRegTypeList(SmallVectorImpl<wasm::ValType> &Types) {
@@ -416,7 +417,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
416417
}
417418

418419
void addBlockTypeOperand(OperandVector &Operands, SMLoc NameLoc,
419-
WebAssembly::ExprType BT) {
420+
WebAssembly::BlockType BT) {
420421
Operands.push_back(std::make_unique<WebAssemblyOperand>(
421422
WebAssemblyOperand::Integer, NameLoc, NameLoc,
422423
WebAssemblyOperand::IntOp{static_cast<int64_t>(BT)}));
@@ -456,6 +457,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
456457
// If this instruction is part of a control flow structure, ensure
457458
// proper nesting.
458459
bool ExpectBlockType = false;
460+
bool ExpectFuncType = false;
459461
if (Name == "block") {
460462
push(Block);
461463
ExpectBlockType = true;
@@ -494,6 +496,10 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
494496
if (pop(Name, Function) || ensureEmptyNestingStack())
495497
return true;
496498
} else if (Name == "call_indirect" || Name == "return_call_indirect") {
499+
ExpectFuncType = true;
500+
}
501+
502+
if (ExpectFuncType || (ExpectBlockType && Lexer.is(AsmToken::LParen))) {
497503
// This has a special TYPEINDEX operand which in text we
498504
// represent as a signature, such that we can re-build this signature,
499505
// attach it to an anonymous symbol, which is what WasmObjectWriter
@@ -502,6 +508,8 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
502508
auto Signature = std::make_unique<wasm::WasmSignature>();
503509
if (parseSignature(Signature.get()))
504510
return true;
511+
// Got signature as block type, don't need more
512+
ExpectBlockType = false;
505513
auto &Ctx = getStreamer().getContext();
506514
// The "true" here will cause this to be a nameless symbol.
507515
MCSymbol *Sym = Ctx.createTempSymbol("typeindex", true);
@@ -526,7 +534,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
526534
if (ExpectBlockType) {
527535
// Assume this identifier is a block_type.
528536
auto BT = parseBlockType(Id.getString());
529-
if (BT == WebAssembly::ExprType::Invalid)
537+
if (BT == WebAssembly::BlockType::Invalid)
530538
return error("Unknown block type: ", Id);
531539
addBlockTypeOperand(Operands, NameLoc, BT);
532540
Parser.Lex();
@@ -594,7 +602,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
594602
}
595603
if (ExpectBlockType && Operands.size() == 1) {
596604
// Support blocks with no operands as default to void.
597-
addBlockTypeOperand(Operands, NameLoc, WebAssembly::ExprType::Void);
605+
addBlockTypeOperand(Operands, NameLoc, WebAssembly::BlockType::Void);
598606
}
599607
Parser.Lex();
600608
return false;

llvm/lib/Target/WebAssembly/Disassembler/LLVMBuild.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@
1818
type = Library
1919
name = WebAssemblyDisassembler
2020
parent = WebAssembly
21-
required_libraries = WebAssemblyDesc MCDisassembler WebAssemblyInfo Support
21+
required_libraries = WebAssemblyDesc MCDisassembler WebAssemblyInfo Support MC
2222
add_to_library_groups = WebAssembly

llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "llvm/MC/MCInstrInfo.h"
2525
#include "llvm/MC/MCSubtargetInfo.h"
2626
#include "llvm/MC/MCSymbol.h"
27+
#include "llvm/MC/MCSymbolWasm.h"
2728
#include "llvm/Support/Endian.h"
2829
#include "llvm/Support/LEB128.h"
2930
#include "llvm/Support/TargetRegistry.h"
@@ -213,10 +214,29 @@ MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction(
213214
return MCDisassembler::Fail;
214215
break;
215216
}
216-
// block_type operands (uint8_t).
217+
// block_type operands:
217218
case WebAssembly::OPERAND_SIGNATURE: {
218-
if (!parseImmediate<uint8_t>(MI, Size, Bytes))
219+
int64_t Val;
220+
uint64_t PrevSize = Size;
221+
if (!nextLEB(Val, Bytes, Size, true))
219222
return MCDisassembler::Fail;
223+
if (Val < 0) {
224+
// Negative values are single septet value types or empty types
225+
if (Size != PrevSize + 1) {
226+
MI.addOperand(
227+
MCOperand::createImm(int64_t(WebAssembly::BlockType::Invalid)));
228+
} else {
229+
MI.addOperand(MCOperand::createImm(Val & 0x7f));
230+
}
231+
} else {
232+
// We don't have access to the signature, so create a symbol without one
233+
MCSymbol *Sym = getContext().createTempSymbol("typeindex", true);
234+
auto *WasmSym = cast<MCSymbolWasm>(Sym);
235+
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
236+
const MCExpr *Expr = MCSymbolRefExpr::create(
237+
WasmSym, MCSymbolRefExpr::VK_WASM_TYPEINDEX, getContext());
238+
MI.addOperand(MCOperand::createExpr(Expr));
239+
}
220240
break;
221241
}
222242
// FP operands.

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -272,9 +272,21 @@ void WebAssemblyInstPrinter::printWebAssemblyP2AlignOperand(const MCInst *MI,
272272
void WebAssemblyInstPrinter::printWebAssemblySignatureOperand(const MCInst *MI,
273273
unsigned OpNo,
274274
raw_ostream &O) {
275-
auto Imm = static_cast<unsigned>(MI->getOperand(OpNo).getImm());
276-
if (Imm != wasm::WASM_TYPE_NORESULT)
277-
O << WebAssembly::anyTypeToString(Imm);
275+
const MCOperand &Op = MI->getOperand(OpNo);
276+
if (Op.isImm()) {
277+
auto Imm = static_cast<unsigned>(Op.getImm());
278+
if (Imm != wasm::WASM_TYPE_NORESULT)
279+
O << WebAssembly::anyTypeToString(Imm);
280+
} else {
281+
auto Expr = cast<MCSymbolRefExpr>(Op.getExpr());
282+
auto *Sym = cast<MCSymbolWasm>(&Expr->getSymbol());
283+
if (Sym->getSignature()) {
284+
O << WebAssembly::signatureToString(Sym->getSignature());
285+
} else {
286+
// Disassembler does not currently produce a signature
287+
O << "unknown_type";
288+
}
289+
}
278290
}
279291

280292
// We have various enums representing a subset of these types, use this

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ void WebAssemblyMCCodeEmitter::encodeInstruction(
152152
break;
153153
case WebAssembly::OPERAND_FUNCTION32:
154154
case WebAssembly::OPERAND_OFFSET32:
155+
case WebAssembly::OPERAND_SIGNATURE:
155156
case WebAssembly::OPERAND_TYPEINDEX:
156157
case WebAssembly::OPERAND_GLOBAL:
157158
case WebAssembly::OPERAND_EVENT:

llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,22 @@ enum TOF {
122122
namespace llvm {
123123
namespace WebAssembly {
124124

125-
/// This is used to indicate block signatures.
126-
enum class ExprType : unsigned {
125+
/// Used as immediate MachineOperands for block signatures
126+
enum class BlockType : unsigned {
127+
Invalid = 0x00,
127128
Void = 0x40,
128-
I32 = 0x7F,
129-
I64 = 0x7E,
130-
F32 = 0x7D,
131-
F64 = 0x7C,
132-
V128 = 0x7B,
133-
Exnref = 0x68,
134-
Invalid = 0x00
129+
I32 = unsigned(wasm::ValType::I32),
130+
I64 = unsigned(wasm::ValType::I64),
131+
F32 = unsigned(wasm::ValType::F32),
132+
F64 = unsigned(wasm::ValType::F64),
133+
V128 = unsigned(wasm::ValType::V128),
134+
Exnref = unsigned(wasm::ValType::EXNREF),
135+
// Multivalue blocks (and other non-void blocks) are only emitted when the
136+
// blocks will never be exited and are at the ends of functions (see
137+
// WebAssemblyCFGStackify::fixEndsAtEndOfFunction). They also are never made
138+
// to pop values off the stack, so the exact multivalue signature can always
139+
// be inferred from the return type of the parent function in MCInstLower.
140+
Multivalue = 0xffff,
135141
};
136142

137143
/// Instruction opcodes emitted via means other than CodeGen.

llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp

Lines changed: 19 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -315,12 +315,12 @@ void WebAssemblyCFGStackify::placeBlockMarker(MachineBasicBlock &MBB) {
315315
// br_on_exn 0, $__cpp_exception
316316
// rethrow
317317
// end_block
318-
WebAssembly::ExprType ReturnType = WebAssembly::ExprType::Void;
318+
WebAssembly::BlockType ReturnType = WebAssembly::BlockType::Void;
319319
if (IsBrOnExn) {
320320
const char *TagName = BrOnExn->getOperand(1).getSymbolName();
321321
if (std::strcmp(TagName, "__cpp_exception") != 0)
322322
llvm_unreachable("Only C++ exception is supported");
323-
ReturnType = WebAssembly::ExprType::I32;
323+
ReturnType = WebAssembly::BlockType::I32;
324324
}
325325

326326
auto InsertPos = getLatestInsertPos(Header, BeforeSet, AfterSet);
@@ -406,7 +406,7 @@ void WebAssemblyCFGStackify::placeLoopMarker(MachineBasicBlock &MBB) {
406406
auto InsertPos = getEarliestInsertPos(&MBB, BeforeSet, AfterSet);
407407
MachineInstr *Begin = BuildMI(MBB, InsertPos, MBB.findDebugLoc(InsertPos),
408408
TII.get(WebAssembly::LOOP))
409-
.addImm(int64_t(WebAssembly::ExprType::Void));
409+
.addImm(int64_t(WebAssembly::BlockType::Void));
410410

411411
// Decide where in Header to put the END_LOOP.
412412
BeforeSet.clear();
@@ -575,7 +575,7 @@ void WebAssemblyCFGStackify::placeTryMarker(MachineBasicBlock &MBB) {
575575
MachineInstr *Begin =
576576
BuildMI(*Header, InsertPos, Header->findDebugLoc(InsertPos),
577577
TII.get(WebAssembly::TRY))
578-
.addImm(int64_t(WebAssembly::ExprType::Void));
578+
.addImm(int64_t(WebAssembly::BlockType::Void));
579579

580580
// Decide where in Header to put the END_TRY.
581581
BeforeSet.clear();
@@ -1129,7 +1129,7 @@ bool WebAssemblyCFGStackify::fixUnwindMismatches(MachineFunction &MF) {
11291129
MachineInstr *NestedTry =
11301130
BuildMI(*MBB, *RangeBegin, RangeBegin->getDebugLoc(),
11311131
TII.get(WebAssembly::TRY))
1132-
.addImm(int64_t(WebAssembly::ExprType::Void));
1132+
.addImm(int64_t(WebAssembly::BlockType::Void));
11331133

11341134
// Create the nested EH pad and fill instructions in.
11351135
MachineBasicBlock *NestedEHPad = MF.CreateMachineBasicBlock();
@@ -1231,54 +1231,28 @@ void WebAssemblyCFGStackify::fixEndsAtEndOfFunction(MachineFunction &MF) {
12311231
if (MFI.getResults().empty())
12321232
return;
12331233

1234-
// TODO: Generalize from value types to function types for multivalue
1235-
WebAssembly::ExprType RetType;
1236-
switch (MFI.getResults().front().SimpleTy) {
1237-
case MVT::i32:
1238-
RetType = WebAssembly::ExprType::I32;
1239-
break;
1240-
case MVT::i64:
1241-
RetType = WebAssembly::ExprType::I64;
1242-
break;
1243-
case MVT::f32:
1244-
RetType = WebAssembly::ExprType::F32;
1245-
break;
1246-
case MVT::f64:
1247-
RetType = WebAssembly::ExprType::F64;
1248-
break;
1249-
case MVT::v16i8:
1250-
case MVT::v8i16:
1251-
case MVT::v4i32:
1252-
case MVT::v2i64:
1253-
case MVT::v4f32:
1254-
case MVT::v2f64:
1255-
RetType = WebAssembly::ExprType::V128;
1256-
break;
1257-
case MVT::exnref:
1258-
RetType = WebAssembly::ExprType::Exnref;
1259-
break;
1260-
default:
1261-
llvm_unreachable("unexpected return type");
1262-
}
1234+
// MCInstLower will add the proper types to multivalue signatures based on the
1235+
// function return type
1236+
WebAssembly::BlockType RetType =
1237+
MFI.getResults().size() > 1
1238+
? WebAssembly::BlockType::Multivalue
1239+
: WebAssembly::BlockType(
1240+
WebAssembly::toValType(MFI.getResults().front()));
12631241

12641242
for (MachineBasicBlock &MBB : reverse(MF)) {
12651243
for (MachineInstr &MI : reverse(MBB)) {
12661244
if (MI.isPosition() || MI.isDebugInstr())
12671245
continue;
1268-
if (MI.getOpcode() == WebAssembly::END_BLOCK) {
1269-
if (MFI.getResults().size() > 1)
1270-
report_fatal_error("Multivalue block signatures not implemented yet");
1271-
EndToBegin[&MI]->getOperand(0).setImm(int32_t(RetType));
1272-
continue;
1273-
}
1274-
if (MI.getOpcode() == WebAssembly::END_LOOP) {
1275-
if (MFI.getResults().size() > 1)
1276-
report_fatal_error("Multivalue loop signatures not implemented yet");
1246+
switch (MI.getOpcode()) {
1247+
case WebAssembly::END_BLOCK:
1248+
case WebAssembly::END_LOOP:
1249+
case WebAssembly::END_TRY:
12771250
EndToBegin[&MI]->getOperand(0).setImm(int32_t(RetType));
12781251
continue;
1252+
default:
1253+
// Something other than an `end`. We're done.
1254+
return;
12791255
}
1280-
// Something other than an `end`. We're done.
1281-
return;
12821256
}
12831257
}
12841258
}

llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,21 @@ MCOperand WebAssemblyMCInstLower::lowerSymbolOperand(const MachineOperand &MO,
163163
return MCOperand::createExpr(Expr);
164164
}
165165

166+
MCOperand WebAssemblyMCInstLower::lowerTypeIndexOperand(
167+
SmallVector<wasm::ValType, 1> &&Returns,
168+
SmallVector<wasm::ValType, 4> &&Params) const {
169+
auto Signature = std::make_unique<wasm::WasmSignature>(std::move(Returns),
170+
std::move(Params));
171+
MCSymbol *Sym = Printer.createTempSymbol("typeindex");
172+
auto *WasmSym = cast<MCSymbolWasm>(Sym);
173+
WasmSym->setSignature(Signature.get());
174+
Printer.addSignature(std::move(Signature));
175+
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
176+
const MCExpr *Expr =
177+
MCSymbolRefExpr::create(WasmSym, MCSymbolRefExpr::VK_WASM_TYPEINDEX, Ctx);
178+
return MCOperand::createExpr(Expr);
179+
}
180+
166181
// Return the WebAssembly type associated with the given register class.
167182
static wasm::ValType getType(const TargetRegisterClass *RC) {
168183
if (RC == &WebAssembly::I32RegClass)
@@ -178,6 +193,16 @@ static wasm::ValType getType(const TargetRegisterClass *RC) {
178193
llvm_unreachable("Unexpected register class");
179194
}
180195

196+
static void getFunctionReturns(const MachineInstr *MI,
197+
SmallVectorImpl<wasm::ValType> &Returns) {
198+
const Function &F = MI->getMF()->getFunction();
199+
const TargetMachine &TM = MI->getMF()->getTarget();
200+
Type *RetTy = F.getReturnType();
201+
SmallVector<MVT, 4> CallerRetTys;
202+
computeLegalValueVTs(F, TM, RetTy, CallerRetTys);
203+
valTypesFromMVTs(CallerRetTys, Returns);
204+
}
205+
181206
void WebAssemblyMCInstLower::lower(const MachineInstr *MI,
182207
MCInst &OutMI) const {
183208
OutMI.setOpcode(MI->getOpcode());
@@ -208,8 +233,6 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI,
208233
if (I < Desc.NumOperands) {
209234
const MCOperandInfo &Info = Desc.OpInfo[I];
210235
if (Info.OperandType == WebAssembly::OPERAND_TYPEINDEX) {
211-
MCSymbol *Sym = Printer.createTempSymbol("typeindex");
212-
213236
SmallVector<wasm::ValType, 4> Returns;
214237
SmallVector<wasm::ValType, 4> Params;
215238

@@ -228,26 +251,21 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI,
228251

229252
// return_call_indirect instructions have the return type of the
230253
// caller
231-
if (MI->getOpcode() == WebAssembly::RET_CALL_INDIRECT) {
232-
const Function &F = MI->getMF()->getFunction();
233-
const TargetMachine &TM = MI->getMF()->getTarget();
234-
Type *RetTy = F.getReturnType();
235-
SmallVector<MVT, 4> CallerRetTys;
236-
computeLegalValueVTs(F, TM, RetTy, CallerRetTys);
237-
valTypesFromMVTs(CallerRetTys, Returns);
238-
}
254+
if (MI->getOpcode() == WebAssembly::RET_CALL_INDIRECT)
255+
getFunctionReturns(MI, Returns);
239256

240-
auto *WasmSym = cast<MCSymbolWasm>(Sym);
241-
auto Signature = std::make_unique<wasm::WasmSignature>(std::move(Returns),
242-
std::move(Params));
243-
WasmSym->setSignature(Signature.get());
244-
Printer.addSignature(std::move(Signature));
245-
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
246-
247-
const MCExpr *Expr = MCSymbolRefExpr::create(
248-
WasmSym, MCSymbolRefExpr::VK_WASM_TYPEINDEX, Ctx);
249-
MCOp = MCOperand::createExpr(Expr);
257+
MCOp = lowerTypeIndexOperand(std::move(Returns), std::move(Params));
250258
break;
259+
} else if (Info.OperandType == WebAssembly::OPERAND_SIGNATURE) {
260+
auto BT = static_cast<WebAssembly::BlockType>(MO.getImm());
261+
assert(BT != WebAssembly::BlockType::Invalid);
262+
if (BT == WebAssembly::BlockType::Multivalue) {
263+
SmallVector<wasm::ValType, 1> Returns;
264+
getFunctionReturns(MI, Returns);
265+
MCOp = lowerTypeIndexOperand(std::move(Returns),
266+
SmallVector<wasm::ValType, 4>());
267+
break;
268+
}
251269
}
252270
}
253271
MCOp = MCOperand::createImm(MO.getImm());

0 commit comments

Comments
 (0)