[WebAssembly] Implementation of intrinsic for ref.null and HeapType removal

This patch implements the intrinsic for ref.null.
In the process of implementing int_wasm_ref_null_func() and
int_wasm_ref_null_extern() intrinsics, it removes the redundant
HeapType.

This also causes the textual assembler syntax for ref.null to
change. Instead of receiving an argument: `func` or `extern`, the
instruction mnemonic is either ref.null_func or ref.null_extern,
without the need for a further operand.

Reviewed By: tlively

Differential Revision: https://reviews.llvm.org/D114979
This commit is contained in:
Paulo Matos 2021-12-05 13:50:47 +01:00
parent 3ec6b1bfac
commit a96d828510
20 changed files with 74 additions and 112 deletions

View File

@ -332,6 +332,9 @@ def llvm_v16f64_ty : LLVMType<v16f64>; // 16 x double
def llvm_vararg_ty : LLVMType<isVoid>; // this means vararg here
def llvm_externref_ty : LLVMType<externref>;
def llvm_funcref_ty : LLVMType<funcref>;
//===----------------------------------------------------------------------===//
// Intrinsic Definitions.
//===----------------------------------------------------------------------===//

View File

@ -23,6 +23,12 @@ def int_wasm_memory_grow : Intrinsic<[llvm_anyint_ty],
[llvm_i32_ty, LLVMMatchType<0>],
[]>;
//===----------------------------------------------------------------------===//
// ref.null intrinsics
//===----------------------------------------------------------------------===//
def int_wasm_ref_null_extern : Intrinsic<[llvm_externref_ty], [], [IntrNoMem]>;
def int_wasm_ref_null_func : Intrinsic<[llvm_funcref_ty], [], [IntrNoMem]>;
//===----------------------------------------------------------------------===//
// Trapping float-to-int conversions
//===----------------------------------------------------------------------===//

View File

@ -982,6 +982,8 @@ enum IIT_Info {
IIT_AMX = 51,
IIT_PPCF128 = 52,
IIT_V3 = 53,
IIT_EXTERNREF = 54,
IIT_FUNCREF = 55
};
static void DecodeIITType(unsigned &NextElt, ArrayRef<unsigned char> Infos,
@ -1097,6 +1099,14 @@ static void DecodeIITType(unsigned &NextElt, ArrayRef<unsigned char> Infos,
OutputTable.push_back(IITDescriptor::getVector(1024, IsScalableVector));
DecodeIITType(NextElt, Infos, Info, OutputTable);
return;
case IIT_EXTERNREF:
OutputTable.push_back(IITDescriptor::get(IITDescriptor::Pointer, 10));
OutputTable.push_back(IITDescriptor::get(IITDescriptor::Struct, 0));
return;
case IIT_FUNCREF:
OutputTable.push_back(IITDescriptor::get(IITDescriptor::Pointer, 20));
OutputTable.push_back(IITDescriptor::get(IITDescriptor::Integer, 8));
return;
case IIT_PTR:
OutputTable.push_back(IITDescriptor::get(IITDescriptor::Pointer, 0));
DecodeIITType(NextElt, Infos, Info, OutputTable);

View File

@ -571,7 +571,6 @@ public:
// proper nesting.
bool ExpectBlockType = false;
bool ExpectFuncType = false;
bool ExpectHeapType = false;
std::unique_ptr<WebAssemblyOperand> FunctionTable;
if (Name == "block") {
push(Block);
@ -624,8 +623,6 @@ public:
if (parseFunctionTableOperand(&FunctionTable))
return true;
ExpectFuncType = true;
} else if (Name == "ref.null") {
ExpectHeapType = true;
}
if (ExpectFuncType || (ExpectBlockType && Lexer.is(AsmToken::LParen))) {
@ -670,15 +667,6 @@ public:
return error("Unknown block type: ", Id);
addBlockTypeOperand(Operands, NameLoc, BT);
Parser.Lex();
} else if (ExpectHeapType) {
auto HeapType = WebAssembly::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,28 +241,6 @@ 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

@ -366,26 +366,3 @@ 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";
}
}

View File

@ -47,8 +47,6 @@ 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.
std::pair<const char *, uint64_t> getMnemonic(const MCInst *MI) override;

View File

@ -106,9 +106,6 @@ 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:
support::endian::write<uint8_t>(OS, MO.getImm(), support::little);
break;

View File

@ -78,8 +78,6 @@ enum OperandType {
OPERAND_BRLIST,
/// 32-bit unsigned table number.
OPERAND_TABLE,
/// heap type immediate for ref.null.
OPERAND_HEAPTYPE,
};
} // end namespace WebAssembly

View File

@ -41,13 +41,6 @@ Optional<wasm::ValType> WebAssembly::parseType(StringRef Type) {
return Optional<wasm::ValType>();
}
WebAssembly::HeapType WebAssembly::parseHeapType(StringRef Type) {
return StringSwitch<WebAssembly::HeapType>(Type)
.Case("extern", WebAssembly::HeapType::Externref)
.Case("func", WebAssembly::HeapType::Funcref)
.Default(WebAssembly::HeapType::Invalid);
}
WebAssembly::BlockType WebAssembly::parseBlockType(StringRef Type) {
// Multivalue block types are handled separately in parseSignature
return StringSwitch<WebAssembly::BlockType>(Type)

View File

@ -41,17 +41,9 @@ 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),
};
// Convert StringRef to ValType / HealType / BlockType
Optional<wasm::ValType> parseType(StringRef Type);
HeapType parseHeapType(StringRef Type);
BlockType parseBlockType(StringRef Type);
MVT parseMVT(StringRef Type);

View File

@ -644,8 +644,7 @@ LowerCallResults(MachineInstr &CallResults, DebugLoc DL, MachineBasicBlock *BB,
Register RegFuncref =
MF.getRegInfo().createVirtualRegister(&WebAssembly::FUNCREFRegClass);
MachineInstr *RefNull =
BuildMI(MF, DL, TII.get(WebAssembly::REF_NULL_FUNCREF), RegFuncref)
.addImm(static_cast<int32_t>(WebAssembly::HeapType::Funcref));
BuildMI(MF, DL, TII.get(WebAssembly::REF_NULL_FUNCREF), RegFuncref);
BB->insertAfter(Const0->getIterator(), RefNull);
MachineInstr *TableSet =

View File

@ -202,11 +202,6 @@ 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

@ -11,13 +11,14 @@
///
//===----------------------------------------------------------------------===//
multiclass REF_I<WebAssemblyRegClass rc, ValueType vt> {
defm REF_NULL_#rc : I<(outs rc:$res), (ins HeapType:$heaptype),
(outs), (ins HeapType:$heaptype),
[],
"ref.null\t$res, $heaptype",
"ref.null\t$heaptype",
0xd0>,
multiclass REF_I<WebAssemblyRegClass rc, ValueType vt, string ht> {
defm REF_NULL_#rc : I<(outs rc:$dst), (ins),
(outs), (ins),
[(set rc:$dst, (!cast<Intrinsic>("int_wasm_ref_null_" # ht)))],
"ref.null_" # ht # "$dst",
"ref.null_" # ht,
!cond(!eq(ht, "func") : 0xd070,
!eq(ht, "extern") : 0xd06f)>,
Requires<[HasReferenceTypes]>;
defm SELECT_#rc: I<(outs rc:$dst), (ins rc:$lhs, rc:$rhs, I32:$cond),
(outs), (ins),
@ -28,8 +29,8 @@ multiclass REF_I<WebAssemblyRegClass rc, ValueType vt> {
Requires<[HasReferenceTypes]>;
}
defm "" : REF_I<FUNCREF, funcref>;
defm "" : REF_I<EXTERNREF, externref>;
defm "" : REF_I<FUNCREF, funcref, "func">;
defm "" : REF_I<EXTERNREF, externref, "extern">;
foreach rc = [FUNCREF, EXTERNREF] in {
def : Pat<(select (i32 (setne I32:$cond, 0)), rc:$lhs, rc:$rhs),

View File

@ -275,11 +275,6 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI,
SmallVector<wasm::ValType, 4>());
break;
}
} else if (Info.OperandType == WebAssembly::OPERAND_HEAPTYPE) {
assert(static_cast<WebAssembly::HeapType>(MO.getImm()) !=
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

@ -18,7 +18,7 @@ define void @call_funcref(%funcref %ref) {
; CHECK-NEXT: i32.const 0
; CHECK-NEXT: call_indirect __funcref_call_table, () -> ()
; CHECK-NEXT: i32.const 0
; CHECK-NEXT: ref.null func
; CHECK-NEXT: ref.null_func
; CHECK-NEXT: table.set __funcref_call_table
; CHECK-NEXT: end_function

View File

@ -23,7 +23,7 @@ define void @call_funcref_from_table(i32 %i) {
; CHECK-NEXT: i32.const 0
; CHECK-NEXT: call_indirect __funcref_call_table, () -> ()
; CHECK-NEXT: i32.const 0
; CHECK-NEXT: ref.null func
; CHECK-NEXT: ref.null_func
; CHECK-NEXT: table.set __funcref_call_table
; CHECK-NEXT: end_function

View File

@ -0,0 +1,26 @@
; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s | FileCheck %s
%extern = type opaque
%externref = type %extern addrspace(10)* ;; addrspace 10 is nonintegral
%funcref = type i8 addrspace(20)* ;; addrspace 20 is nonintegral
declare %externref @llvm.wasm.ref.null.extern() nounwind
declare %funcref @llvm.wasm.ref.null.func() nounwind
define %externref @get_null_extern() {
; CHECK-LABEL: get_null_extern:
; CHECK-NEXT: .functype get_null_extern () -> (externref)
; CHECK-NEXT: ref.null_extern
; CHECK-NEXT: end_function
%null = call %externref @llvm.wasm.ref.null.extern()
ret %externref %null
}
define %funcref @get_null_func() {
; CHECK-LABEL: get_null_func:
; CHECK-NEXT: .functype get_null_func () -> (funcref)
; CHECK-NEXT: ref.null_func
; CHECK-NEXT: end_function
%null = call %funcref @llvm.wasm.ref.null.func()
ret %funcref %null
}

View File

@ -2,13 +2,13 @@
# RUN: llvm-mc -show-encoding -triple=wasm64-unknown-unknown -mattr=+reference-types < %s | FileCheck %s
# CHECK-LABEL: ref_null_test:
# CHECK: ref.null func # encoding: [0xd0,0x70]
# CHECK: ref.null extern # encoding: [0xd0,0x6f]
# CHECK: ref.null_func # encoding: [0xd0,0x70]
# CHECK: ref.null_extern # encoding: [0xd0,0x6f]
ref_null_test:
.functype ref_null_test () -> ()
ref.null func
ref.null_func
drop
ref.null extern
ref.null_extern
drop
end_function
@ -31,13 +31,13 @@ ref_sig_test_externref:
# CHECK: externref.select # encoding: [0x1b]
ref_select_test:
.functype ref_select_test () -> ()
ref.null func
ref.null func
ref.null_func
ref.null_func
i32.const 0
funcref.select
drop
ref.null extern
ref.null extern
ref.null_extern
ref.null_extern
i32.const 0
externref.select
drop
@ -50,8 +50,8 @@ ref_block_test:
.functype ref_block_test () -> (externref, funcref)
block funcref
block externref
ref.null extern
ref.null_extern
end_block
ref.null func
ref.null_func
end_block
end_function

View File

@ -252,6 +252,8 @@ enum IIT_Info {
IIT_AMX = 51,
IIT_PPCF128 = 52,
IIT_V3 = 53,
IIT_EXTERNREF = 54,
IIT_FUNCREF = 55
};
static void EncodeFixedValueType(MVT::SimpleValueType VT,
@ -285,6 +287,10 @@ static void EncodeFixedValueType(MVT::SimpleValueType VT,
case MVT::Other: return Sig.push_back(IIT_EMPTYSTRUCT);
// MVT::isVoid is used to represent varargs here.
case MVT::isVoid: return Sig.push_back(IIT_VARARG);
case MVT::externref:
return Sig.push_back(IIT_EXTERNREF);
case MVT::funcref:
return Sig.push_back(IIT_FUNCREF);
}
}