Skip to content

[WebAssembly][Clang] Add __builtin_wasm_ref_is_null_extern #139580

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 14, 2025

Conversation

hoodmane
Copy link
Contributor

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.

cc @sbc100 @pmatos

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.
@llvmbot llvmbot added clang Clang issues not falling into any other category backend:WebAssembly clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:codegen IR generation bugs: mangling, exceptions, etc. labels May 12, 2025
@llvmbot
Copy link
Member

llvmbot commented May 12, 2025

@llvm/pr-subscribers-backend-webassembly

Author: Hood Chatham (hoodmane)

Changes

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.

cc @sbc100 @pmatos


Full diff: https://github.com/llvm/llvm-project/pull/139580.diff

7 Files Affected:

  • (modified) clang/include/clang/Basic/BuiltinsWebAssembly.def (+1)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+2)
  • (modified) clang/include/clang/Sema/SemaWasm.h (+1)
  • (modified) clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp (+5)
  • (modified) clang/lib/Sema/SemaWasm.cpp (+24-2)
  • (modified) clang/test/CodeGen/builtins-wasm.c (+6)
  • (modified) clang/test/Sema/builtins-wasm.c (+4)
diff --git a/clang/include/clang/Basic/BuiltinsWebAssembly.def b/clang/include/clang/Basic/BuiltinsWebAssembly.def
index ab480369b3820..e2afcc08064b2 100644
--- a/clang/include/clang/Basic/BuiltinsWebAssembly.def
+++ b/clang/include/clang/Basic/BuiltinsWebAssembly.def
@@ -192,6 +192,7 @@ TARGET_BUILTIN(__builtin_wasm_replace_lane_f16x8, "V8hV8hIif", "nc", "fp16")
 // 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 e5a7cdc14a737..d4abc46ae58ee 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12991,6 +12991,8 @@ def err_wasm_reftype_multidimensional_array : Error<
   "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 8841fdff23035..2123e073516cb 100644
--- a/clang/include/clang/Sema/SemaWasm.h
+++ b/clang/include/clang/Sema/SemaWasm.h
@@ -29,6 +29,7 @@ class SemaWasm : public SemaBase {
                                            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 698f43215a1be..b7fd70e855d40 100644
--- a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
@@ -209,6 +209,11 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID,
     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 c0fa05bc17609..4157e179c97d6 100644
--- a/clang/lib/Sema/SemaWasm.cpp
+++ b/clang/lib/Sema/SemaWasm.cpp
@@ -52,14 +52,34 @@ static bool CheckWasmBuiltinArgIsInteger(Sema &S, CallExpr *E,
 }
 
 bool SemaWasm::BuiltinWasmRefNullExtern(CallExpr *TheCall) {
-  if (TheCall->getNumArgs() != 0)
+  if (TheCall->getNumArgs() != 0) {
+    Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_many_args)
+        << 0 /*function call*/ << /*expected*/ 0 << TheCall->getNumArgs()
+        << /*is non object*/ 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 (TheCall->getNumArgs() != 0) {
@@ -224,6 +244,8 @@ bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI,
     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 263cfd3ab4c69..4a44a9a88df11 100644
--- a/clang/test/CodeGen/builtins-wasm.c
+++ b/clang/test/CodeGen/builtins-wasm.c
@@ -741,6 +741,12 @@ __externref_t externref_null() {
   // 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 beb430616233a..31e5291d3ae5e 100644
--- a/clang/test/Sema/builtins-wasm.c
+++ b/clang/test/Sema/builtins-wasm.c
@@ -7,6 +7,10 @@ static __externref_t table[0];
 typedef void (*__funcref funcref_t)();
 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[]) {

@llvmbot
Copy link
Member

llvmbot commented May 12, 2025

@llvm/pr-subscribers-clang

Author: Hood Chatham (hoodmane)

Changes

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.

cc @sbc100 @pmatos


Full diff: https://github.com/llvm/llvm-project/pull/139580.diff

7 Files Affected:

  • (modified) clang/include/clang/Basic/BuiltinsWebAssembly.def (+1)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+2)
  • (modified) clang/include/clang/Sema/SemaWasm.h (+1)
  • (modified) clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp (+5)
  • (modified) clang/lib/Sema/SemaWasm.cpp (+24-2)
  • (modified) clang/test/CodeGen/builtins-wasm.c (+6)
  • (modified) clang/test/Sema/builtins-wasm.c (+4)
diff --git a/clang/include/clang/Basic/BuiltinsWebAssembly.def b/clang/include/clang/Basic/BuiltinsWebAssembly.def
index ab480369b3820..e2afcc08064b2 100644
--- a/clang/include/clang/Basic/BuiltinsWebAssembly.def
+++ b/clang/include/clang/Basic/BuiltinsWebAssembly.def
@@ -192,6 +192,7 @@ TARGET_BUILTIN(__builtin_wasm_replace_lane_f16x8, "V8hV8hIif", "nc", "fp16")
 // 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 e5a7cdc14a737..d4abc46ae58ee 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12991,6 +12991,8 @@ def err_wasm_reftype_multidimensional_array : Error<
   "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 8841fdff23035..2123e073516cb 100644
--- a/clang/include/clang/Sema/SemaWasm.h
+++ b/clang/include/clang/Sema/SemaWasm.h
@@ -29,6 +29,7 @@ class SemaWasm : public SemaBase {
                                            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 698f43215a1be..b7fd70e855d40 100644
--- a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
@@ -209,6 +209,11 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID,
     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 c0fa05bc17609..4157e179c97d6 100644
--- a/clang/lib/Sema/SemaWasm.cpp
+++ b/clang/lib/Sema/SemaWasm.cpp
@@ -52,14 +52,34 @@ static bool CheckWasmBuiltinArgIsInteger(Sema &S, CallExpr *E,
 }
 
 bool SemaWasm::BuiltinWasmRefNullExtern(CallExpr *TheCall) {
-  if (TheCall->getNumArgs() != 0)
+  if (TheCall->getNumArgs() != 0) {
+    Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_many_args)
+        << 0 /*function call*/ << /*expected*/ 0 << TheCall->getNumArgs()
+        << /*is non object*/ 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 (TheCall->getNumArgs() != 0) {
@@ -224,6 +244,8 @@ bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI,
     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 263cfd3ab4c69..4a44a9a88df11 100644
--- a/clang/test/CodeGen/builtins-wasm.c
+++ b/clang/test/CodeGen/builtins-wasm.c
@@ -741,6 +741,12 @@ __externref_t externref_null() {
   // 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 beb430616233a..31e5291d3ae5e 100644
--- a/clang/test/Sema/builtins-wasm.c
+++ b/clang/test/Sema/builtins-wasm.c
@@ -7,6 +7,10 @@ static __externref_t table[0];
 typedef void (*__funcref funcref_t)();
 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[]) {

@llvmbot
Copy link
Member

llvmbot commented May 12, 2025

@llvm/pr-subscribers-clang-codegen

Author: Hood Chatham (hoodmane)

Changes

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.

cc @sbc100 @pmatos


Full diff: https://github.com/llvm/llvm-project/pull/139580.diff

7 Files Affected:

  • (modified) clang/include/clang/Basic/BuiltinsWebAssembly.def (+1)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+2)
  • (modified) clang/include/clang/Sema/SemaWasm.h (+1)
  • (modified) clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp (+5)
  • (modified) clang/lib/Sema/SemaWasm.cpp (+24-2)
  • (modified) clang/test/CodeGen/builtins-wasm.c (+6)
  • (modified) clang/test/Sema/builtins-wasm.c (+4)
diff --git a/clang/include/clang/Basic/BuiltinsWebAssembly.def b/clang/include/clang/Basic/BuiltinsWebAssembly.def
index ab480369b3820..e2afcc08064b2 100644
--- a/clang/include/clang/Basic/BuiltinsWebAssembly.def
+++ b/clang/include/clang/Basic/BuiltinsWebAssembly.def
@@ -192,6 +192,7 @@ TARGET_BUILTIN(__builtin_wasm_replace_lane_f16x8, "V8hV8hIif", "nc", "fp16")
 // 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 e5a7cdc14a737..d4abc46ae58ee 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12991,6 +12991,8 @@ def err_wasm_reftype_multidimensional_array : Error<
   "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 8841fdff23035..2123e073516cb 100644
--- a/clang/include/clang/Sema/SemaWasm.h
+++ b/clang/include/clang/Sema/SemaWasm.h
@@ -29,6 +29,7 @@ class SemaWasm : public SemaBase {
                                            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 698f43215a1be..b7fd70e855d40 100644
--- a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
@@ -209,6 +209,11 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID,
     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 c0fa05bc17609..4157e179c97d6 100644
--- a/clang/lib/Sema/SemaWasm.cpp
+++ b/clang/lib/Sema/SemaWasm.cpp
@@ -52,14 +52,34 @@ static bool CheckWasmBuiltinArgIsInteger(Sema &S, CallExpr *E,
 }
 
 bool SemaWasm::BuiltinWasmRefNullExtern(CallExpr *TheCall) {
-  if (TheCall->getNumArgs() != 0)
+  if (TheCall->getNumArgs() != 0) {
+    Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_many_args)
+        << 0 /*function call*/ << /*expected*/ 0 << TheCall->getNumArgs()
+        << /*is non object*/ 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 (TheCall->getNumArgs() != 0) {
@@ -224,6 +244,8 @@ bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI,
     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 263cfd3ab4c69..4a44a9a88df11 100644
--- a/clang/test/CodeGen/builtins-wasm.c
+++ b/clang/test/CodeGen/builtins-wasm.c
@@ -741,6 +741,12 @@ __externref_t externref_null() {
   // 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 beb430616233a..31e5291d3ae5e 100644
--- a/clang/test/Sema/builtins-wasm.c
+++ b/clang/test/Sema/builtins-wasm.c
@@ -7,6 +7,10 @@ static __externref_t table[0];
 typedef void (*__funcref funcref_t)();
 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[]) {

Copy link

github-actions bot commented May 12, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@sbc100
Copy link
Collaborator

sbc100 commented May 12, 2025

(Nit: We normally spell out [WebAssembly] in PR titles)

@sbc100 sbc100 requested review from pmatos and tlively May 12, 2025 17:16
@hoodmane hoodmane changed the title [Wasm][Clang] Add __builtin_wasm_ref_is_null_extern [WebAssembly][Clang] Add __builtin_wasm_ref_is_null_extern May 12, 2025
@@ -168,7 +168,7 @@ bool Sema::checkArgCount(CallExpr *Call, unsigned DesiredArgCount) {

return Diag(Range.getBegin(), diag::err_typecheck_call_too_many_args)
<< 0 /*function call*/ << DesiredArgCount << ArgCount
<< /*is non object*/ 0 << Call->getArg(1)->getSourceRange();
<< /*is non object*/ 0 << Range;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wasm stuff LGTM, but I don't really know anything about SemaChecking.cpp so someone else might have to review this?

Copy link
Contributor Author

@hoodmane hoodmane May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to have fixed a real bug in the labeling:
Before:

$ ./bin/clang -c a.c 
a.c:2:30: error: too many arguments to function call, expected 2, have 4
    2 |   __builtin_annotation(1, 2, 3, 4);
      |                           ~  ^
1 error generated.

After:

$ ./bin/clang -c a.c 
a.c:2:30: error: too many arguments to function call, expected 2, have 4
    2 |   __builtin_annotation(1, 2, 3, 4);
      |                              ^~~~
1 error generated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I split this change into a separate PR?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds like a good idea yes.

hoodmane added a commit to hoodmane/llvm-project that referenced this pull request May 12, 2025
…ro arguments with one argument

In this case, `Call->getArg(1)` will trap when trying to format the diagnostic.
It also improves the rendering of the diagnostic some of the time:
Before:
```
$ ./bin/clang -c a.c
a.c:2:30: error: too many arguments to function call, expected 2, have 4
    2 |   __builtin_annotation(1, 2, 3, 4);
      |                           ~  ^
```
After:
```
$ ./bin/clang -c a.c
a.c:2:30: error: too many arguments to function call, expected 2, have 4
    2 |   __builtin_annotation(1, 2, 3, 4);
      |                              ^~~~
```

Split from llvm#139580
@shafik
Copy link
Collaborator

shafik commented May 13, 2025

(Nit: We normally spell out [WebAssembly] in PR titles)

This is useful for folks to filter.

erichkeane pushed a commit that referenced this pull request May 13, 2025
When calling a function that expects zero arguments with one argument,
`Call->getArg(1)` will trap when trying to format the diagnostic.

This also seems to improve the rendering of the diagnostic some of the
time. Before:
```
$ ./bin/clang -c a.c
a.c:2:30: error: too many arguments to function call, expected 2, have 4
    2 |   __builtin_annotation(1, 2, 3, 4);
      |                           ~  ^
```
After:
```
$ ./bin/clang -c a.c
a.c:2:30: error: too many arguments to function call, expected 2, have 4
    2 |   __builtin_annotation(1, 2, 3, 4);
      |                              ^~~~
```

Split from #139580.

---------

Co-authored-by: Mariya Podchishchaeva <[email protected]>
Copy link
Collaborator

@tlively tlively left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WebAssembly part LGTM, too.

@hoodmane
Copy link
Contributor Author

Can someone merge when ci is green?

@dschuff dschuff merged commit e29b70e into llvm:main May 14, 2025
11 checks passed
@hoodmane hoodmane deleted the wasm-ref-is-null-extern branch May 14, 2025 13:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:WebAssembly clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants