forked from OSchip/llvm-project
[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:
parent
3ec6b1bfac
commit
a96d828510
|
@ -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.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -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
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue