[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