[WebAssembly] Implement ref.null

This patch adds a new "heap type" operand kind to the WebAssembly MC
layer, used by ref.null. Currently the possible values are "extern" and
"func"; when typed function references come, though, this operand may be
a type index.

Note that the "heap type" production is still known as "refedtype" in
the draft proposal; changing its name in the spec is
ongoing (https://github.com/WebAssembly/reference-types/issues/123).

The register form of ref.null is still untested.

Differential Revision: https://reviews.llvm.org/D90608
This commit is contained in:
Andy Wingo 2020-11-03 10:46:23 -08:00 committed by Thomas Lively
parent ca5b31502c
commit 107c3a12d6
10 changed files with 118 additions and 0 deletions

View File

@ -440,6 +440,13 @@ public:
return false;
}
WebAssembly::HeapType parseHeapType(StringRef Id) {
return StringSwitch<WebAssembly::HeapType>(Id)
.Case("extern", WebAssembly::HeapType::Externref)
.Case("func", WebAssembly::HeapType::Funcref)
.Default(WebAssembly::HeapType::Invalid);
}
void addBlockTypeOperand(OperandVector &Operands, SMLoc NameLoc,
WebAssembly::BlockType BT) {
Operands.push_back(std::make_unique<WebAssemblyOperand>(
@ -482,6 +489,7 @@ public:
// proper nesting.
bool ExpectBlockType = false;
bool ExpectFuncType = false;
bool ExpectHeapType = false;
if (Name == "block") {
push(Block);
ExpectBlockType = true;
@ -521,6 +529,8 @@ public:
return true;
} else if (Name == "call_indirect" || Name == "return_call_indirect") {
ExpectFuncType = true;
} else if (Name == "ref.null") {
ExpectHeapType = true;
}
if (ExpectFuncType || (ExpectBlockType && Lexer.is(AsmToken::LParen))) {
@ -562,6 +572,15 @@ public:
return error("Unknown block type: ", Id);
addBlockTypeOperand(Operands, NameLoc, BT);
Parser.Lex();
} else if (ExpectHeapType) {
auto HeapType = parseHeapType(Id.getString());
if (HeapType == WebAssembly::HeapType::Invalid) {
return error("Expected a heap type: ", Id);
}
Operands.push_back(std::make_unique<WebAssemblyOperand>(
WebAssemblyOperand::Integer, Id.getLoc(), Id.getEndLoc(),
WebAssemblyOperand::IntOp{static_cast<int64_t>(HeapType)}));
Parser.Lex();
} else {
// Assume this identifier is a label.
const MCExpr *Val;

View File

@ -241,6 +241,28 @@ MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction(
}
break;
}
// heap_type operands, for e.g. ref.null:
case WebAssembly::OPERAND_HEAPTYPE: {
int64_t Val;
uint64_t PrevSize = Size;
if (!nextLEB(Val, Bytes, Size, true))
return MCDisassembler::Fail;
if (Val < 0 && Size == PrevSize + 1) {
// The HeapType encoding is like BlockType, in that encodings that
// decode as negative values indicate ValTypes. In practice we expect
// either wasm::ValType::EXTERNREF or wasm::ValType::FUNCREF here.
//
// The positive SLEB values are reserved for future expansion and are
// expected to be type indices in the typed function references
// proposal, and should disassemble as MCSymbolRefExpr as in BlockType
// above.
MI.addOperand(MCOperand::createImm(Val & 0x7f));
} else {
MI.addOperand(
MCOperand::createImm(int64_t(WebAssembly::HeapType::Invalid)));
}
break;
}
// FP operands.
case WebAssembly::OPERAND_F32IMM: {
if (!parseImmediate<float>(MI, Size, Bytes))

View File

@ -302,6 +302,29 @@ void WebAssemblyInstPrinter::printWebAssemblySignatureOperand(const MCInst *MI,
}
}
void WebAssemblyInstPrinter::printWebAssemblyHeapTypeOperand(const MCInst *MI,
unsigned OpNo,
raw_ostream &O) {
const MCOperand &Op = MI->getOperand(OpNo);
if (Op.isImm()) {
switch (Op.getImm()) {
case long(wasm::ValType::EXTERNREF):
O << "extern";
break;
case long(wasm::ValType::FUNCREF):
O << "func";
break;
default:
O << "unsupported_heap_type_value";
break;
}
} else {
// Typed function references and other subtypes of funcref and externref
// currently unimplemented.
O << "unsupported_heap_type_operand";
}
}
// We have various enums representing a subset of these types, use this
// function to convert any of them to text.
const char *WebAssembly::anyTypeToString(unsigned Ty) {

View File

@ -48,6 +48,8 @@ public:
raw_ostream &O);
void printWebAssemblySignatureOperand(const MCInst *MI, unsigned OpNo,
raw_ostream &O);
void printWebAssemblyHeapTypeOperand(const MCInst *MI, unsigned OpNo,
raw_ostream &O);
// Autogenerated by tblgen.
void printInstruction(const MCInst *MI, uint64_t Address, raw_ostream &O);

View File

@ -106,6 +106,7 @@ void WebAssemblyMCCodeEmitter::encodeInstruction(
encodeSLEB128(int64_t(MO.getImm()), OS);
break;
case WebAssembly::OPERAND_SIGNATURE:
case WebAssembly::OPERAND_HEAPTYPE:
OS << uint8_t(MO.getImm());
break;
case WebAssembly::OPERAND_VEC_I8IMM:

View File

@ -78,6 +78,8 @@ enum OperandType {
OPERAND_BRLIST,
/// 32-bit unsigned table number.
OPERAND_TABLE,
/// heap type immediate for ref.null.
OPERAND_HEAPTYPE,
};
} // end namespace WebAssembly
@ -140,6 +142,13 @@ enum class BlockType : unsigned {
Multivalue = 0xffff,
};
/// Used as immediate MachineOperands for heap types, e.g. for ref.null.
enum class HeapType : unsigned {
Invalid = 0x00,
Externref = unsigned(wasm::ValType::EXTERNREF),
Funcref = unsigned(wasm::ValType::FUNCREF),
};
/// Instruction opcodes emitted via means other than CodeGen.
static const unsigned Nop = 0x01;
static const unsigned End = 0x0b;

View File

@ -187,6 +187,11 @@ def Signature : Operand<i32> {
let PrintMethod = "printWebAssemblySignatureOperand";
}
let OperandType = "OPERAND_HEAPTYPE" in
def HeapType : Operand<i32> {
let PrintMethod = "printWebAssemblyHeapTypeOperand";
}
let OperandType = "OPERAND_TYPEINDEX" in
def TypeIndex : Operand<i32>;

View File

@ -23,3 +23,15 @@ def : Pat<(select (i32 (setne I32:$cond, 0)), EXNREF:$lhs, EXNREF:$rhs),
(SELECT_EXNREF EXNREF:$lhs, EXNREF:$rhs, I32:$cond)>;
def : Pat<(select (i32 (seteq I32:$cond, 0)), EXNREF:$lhs, EXNREF:$rhs),
(SELECT_EXNREF EXNREF:$rhs, EXNREF:$lhs, I32:$cond)>;
multiclass REF<WebAssemblyRegClass rt> {
defm REF_NULL_#rt : I<(outs rt:$res), (ins HeapType:$heaptype),
(outs), (ins HeapType:$heaptype),
[],
"ref.null\t$res, $heaptype",
"ref.null\t$heaptype",
0xd0>;
}
defm "" : REF<FUNCREF>, Requires<[HasReferenceTypes]>;
defm "" : REF<EXTERNREF>, Requires<[HasReferenceTypes]>;

View File

@ -271,6 +271,11 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI,
SmallVector<wasm::ValType, 4>());
break;
}
} else if (Info.OperandType == WebAssembly::OPERAND_HEAPTYPE) {
auto HT = static_cast<WebAssembly::HeapType>(MO.getImm());
assert(HT != WebAssembly::HeapType::Invalid);
// With typed function references, this will need a case for type
// index operands. Otherwise, fall through.
}
}
MCOp = MCOperand::createImm(MO.getImm());

View File

@ -0,0 +1,20 @@
# RUN: llvm-mc -show-encoding -triple=wasm32-unknown-unknown -mattr=+reference-types < %s | FileCheck %s
# RUN: llvm-mc -show-encoding -triple=wasm64-unknown-unknown -mattr=+reference-types < %s | FileCheck %s
# CHECK: ref_null_externref:
# CHECK-NEXT: .functype ref_null_externref () -> (externref)
# CHECK: ref.null extern # encoding: [0xd0,0x6f]
# CHECK-NEXT: end_function
ref_null_externref:
.functype ref_null_externref () -> (externref)
ref.null extern
end_function
# CHECK: ref_null_funcref:
# CHECK-NEXT: .functype ref_null_funcref () -> (funcref)
# CHECK: ref.null func # encoding: [0xd0,0x70]
# CHECK-NEXT: end_function
ref_null_funcref:
.functype ref_null_funcref () -> (funcref)
ref.null func
end_function