[llvm] cb778e9 - [WebAssembly] Implement ref.is_null MC layer support and codegen
Alex Bradbury via llvm-commits
llvm-commits at lists.llvm.org
Thu May 12 23:08:26 PDT 2022
Author: Alex Bradbury
Date: 2022-05-13T07:08:10+01:00
New Revision: cb778e9328292b7cfa4e0b43f26e4d1091923a39
URL: https://github.com/llvm/llvm-project/commit/cb778e9328292b7cfa4e0b43f26e4d1091923a39
DIFF: https://github.com/llvm/llvm-project/commit/cb778e9328292b7cfa4e0b43f26e4d1091923a39.diff
LOG: [WebAssembly] Implement ref.is_null MC layer support and codegen
Custom type-checking (in WebAssemblyAsmTypeCheck.cpp) is used to
workaround the fact that separate variants of the instruction are
defined for externref and funcref.
Based on an initial patch by Paulo Matos <pmatos at igalia.com>.
Differential Revision: https://reviews.llvm.org/D123484
Added:
Modified:
llvm/include/llvm/IR/IntrinsicsWebAssembly.td
llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp
llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h
llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h
llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td
llvm/test/CodeGen/WebAssembly/ref-null.ll
llvm/test/MC/WebAssembly/reference-types.s
llvm/test/MC/WebAssembly/type-checker-errors.s
Removed:
################################################################################
diff --git a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td
index 5436f45d345f..2f3edfe80657 100644
--- a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td
+++ b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td
@@ -31,6 +31,10 @@ def int_wasm_memory_grow : Intrinsic<[llvm_anyint_ty],
//===----------------------------------------------------------------------===//
def int_wasm_ref_null_extern : Intrinsic<[llvm_externref_ty], [], [IntrNoMem]>;
def int_wasm_ref_null_func : Intrinsic<[llvm_funcref_ty], [], [IntrNoMem]>;
+def int_wasm_ref_is_null_extern : Intrinsic<[llvm_i32_ty], [llvm_externref_ty],
+ [IntrNoMem], "llvm.wasm.ref.is_null.extern">;
+def int_wasm_ref_is_null_func : Intrinsic<[llvm_i32_ty], [llvm_funcref_ty],
+ [IntrNoMem], "llvm.wasm.ref.is_null.func">;
//===----------------------------------------------------------------------===//
// Table intrinsics
diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp
index 465267abab2c..84dc19f636bf 100644
--- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp
+++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp
@@ -102,6 +102,19 @@ bool WebAssemblyAsmTypeCheck::popType(SMLoc ErrorLoc,
return false;
}
+bool WebAssemblyAsmTypeCheck::popRefType(SMLoc ErrorLoc) {
+ if (Stack.empty()) {
+ return typeError(ErrorLoc, StringRef("empty stack while popping reftype"));
+ }
+ auto PVT = Stack.pop_back_val();
+ if (!WebAssembly::isRefType(PVT)) {
+ return typeError(ErrorLoc, StringRef("popped ") +
+ WebAssembly::typeToString(PVT) +
+ ", expected reftype");
+ }
+ return false;
+}
+
bool WebAssemblyAsmTypeCheck::getLocal(SMLoc ErrorLoc, const MCInst &Inst,
wasm::ValType &Type) {
auto Local = static_cast<size_t>(Inst.getOperand(0).getImm());
@@ -308,6 +321,10 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
Stack.insert(Stack.end(), Sig->Params.begin(), Sig->Params.end());
} else if (Name == "unreachable") {
Unreachable = true;
+ } else if (Name == "ref.is_null") {
+ if (popRefType(ErrorLoc))
+ return true;
+ Stack.push_back(wasm::ValType::I32);
} else {
// The current instruction is a stack instruction which doesn't have
// explicit operands that indicate push/pop types, so we get those from
diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h
index 033287b7445b..3be966b5739c 100644
--- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h
+++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h
@@ -39,6 +39,7 @@ class WebAssemblyAsmTypeCheck final {
void dumpTypeStack(Twine Msg);
bool typeError(SMLoc ErrorLoc, const Twine &Msg);
bool popType(SMLoc ErrorLoc, Optional<wasm::ValType> EVT);
+ bool popRefType(SMLoc ErrorLoc);
bool getLocal(SMLoc ErrorLoc, const MCInst &Inst, wasm::ValType &Type);
bool checkEnd(SMLoc ErrorLoc, bool PopVals = false);
bool checkSig(SMLoc ErrorLoc, const wasm::WasmSignature &Sig);
diff --git a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h
index cdb95d48398d..8fc67d37925c 100644
--- a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h
+++ b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h
@@ -80,6 +80,10 @@ inline bool isRefType(const Type *Ty) {
return isFuncrefType(Ty) || isExternrefType(Ty);
}
+inline bool isRefType(wasm::ValType Type) {
+ return Type == wasm::ValType::EXTERNREF || Type == wasm::ValType::FUNCREF;
+}
+
// Convert StringRef to ValType / HealType / BlockType
Optional<wasm::ValType> parseType(StringRef Type);
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td
index 76a88caafc47..608963d58863 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td
@@ -27,6 +27,12 @@ multiclass REF_I<WebAssemblyRegClass rc, ValueType vt, string ht> {
vt#".select\t$dst, $lhs, $rhs, $cond",
vt#".select", 0x1b>,
Requires<[HasReferenceTypes]>;
+ defm REF_IS_NULL_#rc
+ : I<(outs I32:$dst), (ins rc:$ref), (outs), (ins),
+ [(set I32:$dst, (!cast<Intrinsic>("int_wasm_ref_is_null_" # ht) rc:$ref))],
+ "ref.is_null\t$ref",
+ "ref.is_null", 0xd1>,
+ Requires<[HasReferenceTypes]>;
}
defm "" : REF_I<FUNCREF, funcref, "func">;
diff --git a/llvm/test/CodeGen/WebAssembly/ref-null.ll b/llvm/test/CodeGen/WebAssembly/ref-null.ll
index d8c356b66acc..710bd4f1f7d9 100644
--- a/llvm/test/CodeGen/WebAssembly/ref-null.ll
+++ b/llvm/test/CodeGen/WebAssembly/ref-null.ll
@@ -1,4 +1,5 @@
-; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s | FileCheck %s
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc --mtriple=wasm32-unknown-unknown -mattr=+reference-types < %s | FileCheck %s
%extern = type opaque
%externref = type %extern addrspace(10)* ;; addrspace 10 is nonintegral
@@ -6,21 +7,59 @@
declare %externref @llvm.wasm.ref.null.extern() nounwind
declare %funcref @llvm.wasm.ref.null.func() nounwind
+declare i32 @llvm.wasm.ref.is_null.extern(%externref) nounwind
+declare i32 @llvm.wasm.ref.is_null.func(%funcref) 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
+; CHECK: .functype get_null_extern () -> (externref)
+; CHECK-NEXT: # %bb.0:
+; CHECK-NEXT: ref.null_extern
+; CHECK-NEXT: # fallthrough-return
%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
+; CHECK: .functype get_null_func () -> (funcref)
+; CHECK-NEXT: # %bb.0:
+; CHECK-NEXT: ref.null_func
+; CHECK-NEXT: # fallthrough-return
%null = call %funcref @llvm.wasm.ref.null.func()
ret %funcref %null
}
+
+define i32 @ref_is_null_extern(%externref %eref) {
+; CHECK-LABEL: ref_is_null_extern:
+; CHECK: .functype ref_is_null_extern (externref) -> (i32)
+; CHECK-NEXT: # %bb.0:
+; CHECK-NEXT: ref.null_extern
+; CHECK-NEXT: ref.is_null
+; CHECK-NEXT: local.get 0
+; CHECK-NEXT: ref.is_null
+; CHECK-NEXT: i32.add
+; CHECK-NEXT: # fallthrough-return
+ %null = call %externref @llvm.wasm.ref.null.extern()
+ %is_null = call i32 @llvm.wasm.ref.is_null.extern(%externref %null)
+ %arg_is_null = call i32 @llvm.wasm.ref.is_null.extern(%externref %eref)
+ %res = add i32 %is_null, %arg_is_null
+ ret i32 %res
+}
+
+define i32 @ref_is_null_func(%funcref %fref) {
+; CHECK-LABEL: ref_is_null_func:
+; CHECK: .functype ref_is_null_func (funcref) -> (i32)
+; CHECK-NEXT: # %bb.0:
+; CHECK-NEXT: ref.null_func
+; CHECK-NEXT: ref.is_null
+; CHECK-NEXT: local.get 0
+; CHECK-NEXT: ref.is_null
+; CHECK-NEXT: i32.add
+; CHECK-NEXT: # fallthrough-return
+ %null = call %funcref @llvm.wasm.ref.null.func()
+ %is_null = call i32 @llvm.wasm.ref.is_null.func(%funcref %null)
+ %arg_is_null = call i32 @llvm.wasm.ref.is_null.func(%funcref %fref)
+ %res = add i32 %is_null, %arg_is_null
+ ret i32 %res
+}
diff --git a/llvm/test/MC/WebAssembly/reference-types.s b/llvm/test/MC/WebAssembly/reference-types.s
index 48673d23173d..ab3e3ee6b155 100644
--- a/llvm/test/MC/WebAssembly/reference-types.s
+++ b/llvm/test/MC/WebAssembly/reference-types.s
@@ -1,6 +1,16 @@
# 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-LABEL:ref_is_null:
+# CHECK: ref.is_null # encoding: [0xd1]
+ref_is_null:
+ .functype ref_is_null () -> (i32, i32)
+ ref.null_extern
+ ref.is_null
+ ref.null_func
+ ref.is_null
+ end_function
+
# CHECK-LABEL: ref_null_test:
# CHECK: ref.null_func # encoding: [0xd0,0x70]
# CHECK: ref.null_extern # encoding: [0xd0,0x6f]
diff --git a/llvm/test/MC/WebAssembly/type-checker-errors.s b/llvm/test/MC/WebAssembly/type-checker-errors.s
index 1197a49ffa74..dcb9dc16133b 100644
--- a/llvm/test/MC/WebAssembly/type-checker-errors.s
+++ b/llvm/test/MC/WebAssembly/type-checker-errors.s
@@ -468,6 +468,26 @@ catch_superfluous_value_at_end:
# CHECK: :[[@LINE+1]]:3: error: 1 superfluous return values
end_function
+ref_is_null_empty_stack_while_popping:
+ .functype ref_is_null_empty_stack_while_popping () -> ()
+# CHECK: [[@LINE+1]]:3: error: empty stack while popping reftype
+ ref.is_null
+ end_function
+
+ref_is_null_type_mismatch:
+ .functype ref_is_null_type_mismatch () -> ()
+ i32.const 1
+# CHECK: [[@LINE+1]]:3: error: popped i32, expected reftype
+ ref.is_null
+ end_function
+
+ref_is_null_pushes_i32:
+ .functype ref_is_null_pushes_i32 () -> (i64)
+ ref.null_func
+ ref.is_null
+# CHECK: :[[@LINE+1]]:3: error: popped i32, expected i64
+ end_function
+
# For the other instructions, the type checker checks vs the operands in the
# instruction definition. Perform some simple checks for these rather than
# exhaustively testing all instructions.
More information about the llvm-commits
mailing list