[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:
parent
ca5b31502c
commit
107c3a12d6
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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]>;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue