From 107c3a12d627f12a23f138a00d6aabe9de7402f7 Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Tue, 3 Nov 2020 10:46:23 -0800 Subject: [PATCH] [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 --- .../WebAssembly/AsmParser/WebAssemblyAsmParser.cpp | 19 ++++++++++++++++++ .../Disassembler/WebAssemblyDisassembler.cpp | 22 +++++++++++++++++++++ .../MCTargetDesc/WebAssemblyInstPrinter.cpp | 23 ++++++++++++++++++++++ .../MCTargetDesc/WebAssemblyInstPrinter.h | 2 ++ .../MCTargetDesc/WebAssemblyMCCodeEmitter.cpp | 1 + .../MCTargetDesc/WebAssemblyMCTargetDesc.h | 9 +++++++++ .../lib/Target/WebAssembly/WebAssemblyInstrInfo.td | 5 +++++ llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td | 12 +++++++++++ .../Target/WebAssembly/WebAssemblyMCInstLower.cpp | 5 +++++ llvm/test/MC/WebAssembly/reference-types.s | 20 +++++++++++++++++++ 10 files changed, 118 insertions(+) create mode 100644 llvm/test/MC/WebAssembly/reference-types.s diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp index 92e8559..2920a49 100644 --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp @@ -440,6 +440,13 @@ public: return false; } + WebAssembly::HeapType parseHeapType(StringRef Id) { + return StringSwitch(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( @@ -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::Integer, Id.getLoc(), Id.getEndLoc(), + WebAssemblyOperand::IntOp{static_cast(HeapType)})); + Parser.Lex(); } else { // Assume this identifier is a label. const MCExpr *Val; diff --git a/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp b/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp index 8c93b59..1b7cc09 100644 --- a/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp +++ b/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp @@ -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(MI, Size, Bytes)) diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp index 86c894e..0812ee0 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp @@ -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) { diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.h index 1387a192..f40cef2 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.h @@ -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); diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp index 3e8cce4..55bf5d1 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp @@ -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: diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h index 2dcd312..d6c3f74 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -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; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td index 94c1df3..b57d6fd 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -187,6 +187,11 @@ def Signature : Operand { let PrintMethod = "printWebAssemblySignatureOperand"; } +let OperandType = "OPERAND_HEAPTYPE" in +def HeapType : Operand { + let PrintMethod = "printWebAssemblyHeapTypeOperand"; +} + let OperandType = "OPERAND_TYPEINDEX" in def TypeIndex : Operand; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td index 14d7237..32b0e71 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td @@ -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 { + 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, Requires<[HasReferenceTypes]>; +defm "" : REF, Requires<[HasReferenceTypes]>; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp index 7774a0d..09d1b96 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -271,6 +271,11 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI, SmallVector()); break; } + } else if (Info.OperandType == WebAssembly::OPERAND_HEAPTYPE) { + auto HT = static_cast(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()); diff --git a/llvm/test/MC/WebAssembly/reference-types.s b/llvm/test/MC/WebAssembly/reference-types.s new file mode 100644 index 0000000..cd4d767 --- /dev/null +++ b/llvm/test/MC/WebAssembly/reference-types.s @@ -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 -- 2.7.4