Index: llvm/include/llvm/IR/IntrinsicsWebAssembly.td =================================================================== --- llvm/include/llvm/IR/IntrinsicsWebAssembly.td +++ llvm/include/llvm/IR/IntrinsicsWebAssembly.td @@ -31,6 +31,8 @@ //===----------------------------------------------------------------------===// 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]>; +def int_wasm_ref_is_null_func : Intrinsic<[llvm_i32_ty], [llvm_funcref_ty], [IntrNoMem]>; //===----------------------------------------------------------------------===// // Table intrinsics Index: llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp =================================================================== --- llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp +++ llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp @@ -308,6 +308,11 @@ Stack.insert(Stack.end(), Sig->Params.begin(), Sig->Params.end()); } else if (Name == "unreachable") { Unreachable = true; + } else if (Name == "ref.is_null") { + // FIXME: overly permissive - accepts any value. + if (popType(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 Index: llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td =================================================================== --- llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td +++ llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td @@ -27,8 +27,18 @@ vt#".select\t$dst, $lhs, $rhs, $cond", vt#".select", 0x1b>, Requires<[HasReferenceTypes]>; + // Define register variants of ref.is_null for each reference type. A + // single stack variant of the instruction is defined later. + let isCodeGenOnly = 1 in + def REF_IS_NULL_#rc : NI<(outs I32:$dst), (ins rc:$ref), + [(set I32:$dst, (!cast("int_wasm_ref_is_null_" # ht) rc:$ref))], + "false", + "">, + Requires<[HasReferenceTypes]>; } +def REF_IS_NULL_S : NI<(outs), (ins), [], "true", "ref.is_null", 0xd1>; + defm "" : REF_I; defm "" : REF_I; Index: llvm/test/CodeGen/WebAssembly/ref-null.ll =================================================================== --- llvm/test/CodeGen/WebAssembly/ref-null.ll +++ llvm/test/CodeGen/WebAssembly/ref-null.ll @@ -6,6 +6,8 @@ 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: @@ -24,3 +26,23 @@ %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-NEXT: .functype ref_is_null_extern (externref) -> (i32) + %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-NEXT: .functype ref_is_null_func (funcref) -> (i32) + %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 +} Index: llvm/test/MC/WebAssembly/reference-types.s =================================================================== --- llvm/test/MC/WebAssembly/reference-types.s +++ 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] Index: llvm/test/MC/WebAssembly/type-checker-errors.s =================================================================== --- llvm/test/MC/WebAssembly/type-checker-errors.s +++ llvm/test/MC/WebAssembly/type-checker-errors.s @@ -468,6 +468,19 @@ # 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 value + 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.