diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp @@ -440,6 +440,13 @@ 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 @@ // proper nesting. bool ExpectBlockType = false; bool ExpectFuncType = false; + bool ExpectHeapType = false; if (Name == "block") { push(Block); ExpectBlockType = true; @@ -521,6 +529,8 @@ 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 @@ 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 --- a/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp +++ b/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp @@ -241,6 +241,28 @@ } 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.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.h --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.h @@ -48,6 +48,8 @@ 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/WebAssemblyInstPrinter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp @@ -302,6 +302,29 @@ } } +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/WebAssemblyMCCodeEmitter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp @@ -106,6 +106,7 @@ 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 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -78,6 +78,8 @@ OPERAND_BRLIST, /// 32-bit unsigned table number. OPERAND_TABLE, + /// heap type immediate for ref.null. + OPERAND_HEAPTYPE, }; } // end namespace WebAssembly @@ -140,6 +142,13 @@ 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 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -187,6 +187,11 @@ 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 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td @@ -23,3 +23,15 @@ (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 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -271,6 +271,11 @@ 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 --- /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