[WebAssembly][Clang] Add  __builtin_wasm_ref_is_null_extern (#139580)

I also fixed __builtin_wasm_ref_null_extern() to generate a diagnostic
when it gets an argument. It seems like `SemaRef.checkArgCount()` has a
bug that makes it unable to check for 0 args.
diff --git a/clang/include/clang/Basic/BuiltinsWebAssembly.def b/clang/include/clang/Basic/BuiltinsWebAssembly.def
index ab48036..e2afcc0 100644
--- a/clang/include/clang/Basic/BuiltinsWebAssembly.def
+++ b/clang/include/clang/Basic/BuiltinsWebAssembly.def
@@ -192,6 +192,7 @@
 // in which case the argument spec (second argument) is unused.
 
 TARGET_BUILTIN(__builtin_wasm_ref_null_extern, "i", "nct", "reference-types")
+TARGET_BUILTIN(__builtin_wasm_ref_is_null_extern, "ii", "nct", "reference-types")
 
 // A funcref represented as a function pointer with the funcref attribute
 // attached to the type, therefore SemaChecking will check for the right
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d6afd5a..87c2f57 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13012,6 +13012,8 @@
   "multi-dimensional arrays of WebAssembly references are not allowed">;
 def err_wasm_builtin_arg_must_be_table_type : Error <
   "%ordinal0 argument must be a WebAssembly table">;
+def err_wasm_builtin_arg_must_be_externref_type : Error <
+  "%ordinal0 argument must be an externref">;
 def err_wasm_builtin_arg_must_match_table_element_type : Error <
   "%ordinal0 argument must match the element type of the WebAssembly table in the %ordinal1 argument">;
 def err_wasm_builtin_arg_must_be_integer_type : Error <
diff --git a/clang/include/clang/Sema/SemaWasm.h b/clang/include/clang/Sema/SemaWasm.h
index 8841fdf..2123e07 100644
--- a/clang/include/clang/Sema/SemaWasm.h
+++ b/clang/include/clang/Sema/SemaWasm.h
@@ -29,6 +29,7 @@
                                            CallExpr *TheCall);
 
   bool BuiltinWasmRefNullExtern(CallExpr *TheCall);
+  bool BuiltinWasmRefIsNullExtern(CallExpr *TheCall);
   bool BuiltinWasmRefNullFunc(CallExpr *TheCall);
   bool BuiltinWasmTableGet(CallExpr *TheCall);
   bool BuiltinWasmTableSet(CallExpr *TheCall);
diff --git a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
index 698f432..b7fd70e 100644
--- a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
@@ -209,6 +209,11 @@
     Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_ref_null_extern);
     return Builder.CreateCall(Callee);
   }
+  case WebAssembly::BI__builtin_wasm_ref_is_null_extern: {
+    Value *Src = EmitScalarExpr(E->getArg(0));
+    Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_ref_is_null_extern);
+    return Builder.CreateCall(Callee, {Src});
+  }
   case WebAssembly::BI__builtin_wasm_ref_null_func: {
     Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_ref_null_func);
     return Builder.CreateCall(Callee);
diff --git a/clang/lib/Sema/SemaWasm.cpp b/clang/lib/Sema/SemaWasm.cpp
index 3362e1d..6faea24 100644
--- a/clang/lib/Sema/SemaWasm.cpp
+++ b/clang/lib/Sema/SemaWasm.cpp
@@ -54,12 +54,27 @@
 bool SemaWasm::BuiltinWasmRefNullExtern(CallExpr *TheCall) {
   if (SemaRef.checkArgCount(TheCall, /*DesiredArgCount=*/0))
     return true;
-
   TheCall->setType(getASTContext().getWebAssemblyExternrefType());
 
   return false;
 }
 
+bool SemaWasm::BuiltinWasmRefIsNullExtern(CallExpr *TheCall) {
+  if (SemaRef.checkArgCount(TheCall, 1)) {
+    return true;
+  }
+
+  Expr *ArgExpr = TheCall->getArg(0);
+  if (!ArgExpr->getType().isWebAssemblyExternrefType()) {
+    SemaRef.Diag(ArgExpr->getBeginLoc(),
+                 diag::err_wasm_builtin_arg_must_be_externref_type)
+        << 1 << ArgExpr->getSourceRange();
+    return true;
+  }
+
+  return false;
+}
+
 bool SemaWasm::BuiltinWasmRefNullFunc(CallExpr *TheCall) {
   ASTContext &Context = getASTContext();
   if (SemaRef.checkArgCount(TheCall, /*DesiredArgCount=*/0))
@@ -220,6 +235,8 @@
     return BuiltinWasmRefNullExtern(TheCall);
   case WebAssembly::BI__builtin_wasm_ref_null_func:
     return BuiltinWasmRefNullFunc(TheCall);
+  case WebAssembly::BI__builtin_wasm_ref_is_null_extern:
+    return BuiltinWasmRefIsNullExtern(TheCall);
   case WebAssembly::BI__builtin_wasm_table_get:
     return BuiltinWasmTableGet(TheCall);
   case WebAssembly::BI__builtin_wasm_table_set:
diff --git a/clang/test/CodeGen/builtins-wasm.c b/clang/test/CodeGen/builtins-wasm.c
index 263cfd3..4a44a9a 100644
--- a/clang/test/CodeGen/builtins-wasm.c
+++ b/clang/test/CodeGen/builtins-wasm.c
@@ -741,6 +741,12 @@
   // WEBASSEMBLY-NEXT: ret
 }
 
+int externref_is_null(__externref_t arg) {
+  return __builtin_wasm_ref_is_null_extern(arg);
+  // WEBASSEMBLY: tail call i32 @llvm.wasm.ref.is_null.extern(ptr addrspace(10) %arg)
+  // WEBASSEMBLY-NEXT: ret
+}
+
 void *tp (void) {
   return __builtin_thread_pointer ();
   // WEBASSEMBLY: call {{.*}} @llvm.thread.pointer()
diff --git a/clang/test/Sema/builtins-wasm.c b/clang/test/Sema/builtins-wasm.c
index 1aae365..31e5291 100644
--- a/clang/test/Sema/builtins-wasm.c
+++ b/clang/test/Sema/builtins-wasm.c
@@ -8,6 +8,9 @@
 void test_ref_null() {
   funcref_t func = __builtin_wasm_ref_null_func(0); // expected-error {{too many arguments to function call, expected 0, have 1}}
   __externref_t ref = __builtin_wasm_ref_null_extern(0); // expected-error {{too many arguments to function call, expected 0, have 1}}
+  __builtin_wasm_ref_is_null_extern(ref, 1); // expected-error {{too many arguments to function call, expected 1, have 2}}
+  __builtin_wasm_ref_is_null_extern(); // expected-error {{too few arguments to function call, expected 1, have 0}}
+  __builtin_wasm_ref_is_null_extern(1); // expected-error {{1st argument must be an externref}}
 }
 
 void test_table_size(__externref_t ref, void *ptr, int arr[]) {