[clang] [llvm] [WebAssembly] Add support for nonnull_extern_ref type (PR #148935)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 15 12:21:12 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-codegen

@llvm/pr-subscribers-clang

Author: jjasmine (badumbatish)

<details>
<summary>Changes</summary>

Landing support for nonnull_extern_ref in https://github.com/llvm/llvm-project/issues/136594.

Unsure if more fixes are needed or split in another patch

Implementation follows footstep of https://github.com/llvm/llvm-project/commit/890146b19206827bc48ee1ae1dc1534ff2ff18d7.

Added test cases.

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


13 Files Affected:

- (modified) clang/include/clang/AST/ASTContext.h (+3) 
- (modified) clang/include/clang/AST/Type.h (+6) 
- (modified) clang/include/clang/Basic/WebAssemblyReferenceTypes.def (+2) 
- (modified) clang/lib/AST/ASTContext.cpp (+3-1) 
- (modified) clang/lib/AST/Type.cpp (+12-1) 
- (modified) clang/lib/CodeGen/CodeGenTypes.cpp (+3) 
- (modified) clang/lib/CodeGen/TargetInfo.h (+4) 
- (modified) clang/lib/CodeGen/Targets/WebAssembly.cpp (+3) 
- (modified) clang/test/CodeGen/WebAssembly/wasm-externref.c (+47) 
- (modified) clang/test/Sema/wasm-refs-and-tables.c (+51) 
- (modified) llvm/include/llvm/IR/IntrinsicsWebAssembly.td (+5) 
- (modified) llvm/include/llvm/IR/Type.h (+1) 
- (modified) llvm/lib/IR/Type.cpp (+7) 


``````````diff
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 2b9cd035623cc..398e878dff90a 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1675,6 +1675,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// Return a WebAssembly externref type.
   QualType getWebAssemblyExternrefType() const;
 
+  /// Return a WebAssembly non null externref type.
+  QualType getWebAssemblyNonNullExternrefType() const;
+
   /// Return the unique reference to a vector type of the specified
   /// element type and size.
   ///
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 21b97102db95a..3e35fa655c4d5 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -1145,6 +1145,9 @@ class QualType {
   /// Returns true if it is a WebAssembly Externref Type.
   bool isWebAssemblyExternrefType() const;
 
+  /// Returns true if it is a WebAssembly non null Externref Type.
+  bool isWebAssemblyNonNullExternrefType() const;
+
   /// Returns true if it is a WebAssembly Funcref Type.
   bool isWebAssemblyFuncrefType() const;
 
@@ -2402,6 +2405,9 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
   /// Check if this is a WebAssembly Externref Type.
   bool isWebAssemblyExternrefType() const;
 
+  /// Check if this is a WebAssembly non null Externref Type.
+  bool isWebAssemblyNonNullExternrefType() const;
+
   /// Returns true if this is a WebAssembly table type: either an array of
   /// reference types, or a pointer to a reference type (which can only be
   /// created by array to pointer decay).
diff --git a/clang/include/clang/Basic/WebAssemblyReferenceTypes.def b/clang/include/clang/Basic/WebAssemblyReferenceTypes.def
index 7c83da15150ce..747eb4034f669 100644
--- a/clang/include/clang/Basic/WebAssemblyReferenceTypes.def
+++ b/clang/include/clang/Basic/WebAssemblyReferenceTypes.def
@@ -36,5 +36,7 @@
 
 WASM_REF_TYPE("__externref_t", "externref_t", WasmExternRef, WasmExternRefTy, 10)
 
+WASM_REF_TYPE("__non_null_externref_t", "non_null_externref_t", WasmNonNullExternRef, WasmNonNullExternRefTy, 10)
+
 #undef WASM_TYPE
 #undef WASM_REF_TYPE
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 679812adcdf12..2322c5112eb73 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -3550,6 +3550,7 @@ static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx,
 #define AMDGPU_TYPE(Name, Id, SingletonId, Width, Align) case BuiltinType::Id:
 #include "clang/Basic/AMDGPUTypes.def"
     case BuiltinType::WasmExternRef:
+    case BuiltinType::WasmNonNullExternRef:
 #define RVV_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
 #include "clang/Basic/RISCVVTypes.def"
       llvm_unreachable("not yet implemented");
@@ -4584,7 +4585,8 @@ ASTContext::getBuiltinVectorTypeInfo(const BuiltinType *Ty) const {
 QualType ASTContext::getWebAssemblyExternrefType() const {
   if (Target->getTriple().isWasm() && Target->hasFeature("reference-types")) {
 #define WASM_REF_TYPE(Name, MangledName, Id, SingletonId, AS)                  \
-  if (BuiltinType::Id == BuiltinType::WasmExternRef)                           \
+  if (BuiltinType::Id == BuiltinType::WasmExternRef ||                         \
+      BuiltinType::Id == BuiltinType::WasmNonNullExternRef)                    \
     return SingletonId;
 #include "clang/Basic/WebAssemblyReferenceTypes.def"
   }
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index e5a1ab2ff8906..b2302311ef291 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2553,6 +2553,12 @@ bool Type::isWebAssemblyExternrefType() const {
   return false;
 }
 
+bool Type::isWebAssemblyNonNullExternrefType() const {
+  if (const auto *BT = getAs<BuiltinType>())
+    return BT->getKind() == BuiltinType::WasmNonNullExternRef;
+  return false;
+}
+
 bool Type::isWebAssemblyTableType() const {
   if (const auto *ATy = dyn_cast<ArrayType>(this))
     return ATy->getElementType().isWebAssemblyReferenceType();
@@ -2922,13 +2928,18 @@ bool QualType::hasNonTrivialToPrimitiveCopyCUnion(const RecordDecl *RD) {
 }
 
 bool QualType::isWebAssemblyReferenceType() const {
-  return isWebAssemblyExternrefType() || isWebAssemblyFuncrefType();
+  return isWebAssemblyExternrefType() || isWebAssemblyNonNullExternrefType() ||
+         isWebAssemblyFuncrefType();
 }
 
 bool QualType::isWebAssemblyExternrefType() const {
   return getTypePtr()->isWebAssemblyExternrefType();
 }
 
+bool QualType::isWebAssemblyNonNullExternrefType() const {
+  return getTypePtr()->isWebAssemblyNonNullExternrefType();
+}
+
 bool QualType::isWebAssemblyFuncrefType() const {
   return getTypePtr()->isFunctionPointerType() &&
          getAddressSpace() == LangAS::wasm_funcref;
diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp
index c98503e4bbd26..515a756c7a755 100644
--- a/clang/lib/CodeGen/CodeGenTypes.cpp
+++ b/clang/lib/CodeGen/CodeGenTypes.cpp
@@ -569,6 +569,9 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
   case BuiltinType::Id: {                                                      \
     if (BuiltinType::Id == BuiltinType::WasmExternRef)                         \
       ResultType = CGM.getTargetCodeGenInfo().getWasmExternrefReferenceType(); \
+    else if (BuiltinType::Id == BuiltinType::WasmNonNullExternRef)             \
+      ResultType =                                                             \
+          CGM.getTargetCodeGenInfo().getWasmNonNullExternrefReferenceType();   \
     else                                                                       \
       llvm_unreachable("Unexpected wasm reference builtin type!");             \
   } break;
diff --git a/clang/lib/CodeGen/TargetInfo.h b/clang/lib/CodeGen/TargetInfo.h
index b4057d369f988..a416e4127ff13 100644
--- a/clang/lib/CodeGen/TargetInfo.h
+++ b/clang/lib/CodeGen/TargetInfo.h
@@ -418,6 +418,10 @@ class TargetCodeGenInfo {
   /// Return the WebAssembly externref reference type.
   virtual llvm::Type *getWasmExternrefReferenceType() const { return nullptr; }
 
+  /// Return the WebAssembly externref reference type.
+  virtual llvm::Type *getWasmNonNullExternrefReferenceType() const {
+    return nullptr;
+  }
   /// Return the WebAssembly funcref reference type.
   virtual llvm::Type *getWasmFuncrefReferenceType() const { return nullptr; }
 
diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp b/clang/lib/CodeGen/Targets/WebAssembly.cpp
index 9217c78a540a3..054dd7c66455c 100644
--- a/clang/lib/CodeGen/Targets/WebAssembly.cpp
+++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp
@@ -89,6 +89,9 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo {
   virtual llvm::Type *getWasmExternrefReferenceType() const override {
     return llvm::Type::getWasm_ExternrefTy(getABIInfo().getVMContext());
   }
+  virtual llvm::Type *getWasmNonNullExternrefReferenceType() const override {
+    return llvm::Type::getWasm_NonNullExternrefTy(getABIInfo().getVMContext());
+  }
   /// Return the WebAssembly funcref reference type.
   virtual llvm::Type *getWasmFuncrefReferenceType() const override {
     return llvm::Type::getWasm_FuncrefTy(getABIInfo().getVMContext());
diff --git a/clang/test/CodeGen/WebAssembly/wasm-externref.c b/clang/test/CodeGen/WebAssembly/wasm-externref.c
index 788438bb4a86a..ae3b4b450eccc 100644
--- a/clang/test/CodeGen/WebAssembly/wasm-externref.c
+++ b/clang/test/CodeGen/WebAssembly/wasm-externref.c
@@ -2,8 +2,10 @@
 // RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature +reference-types -o - -emit-llvm %s | FileCheck %s
 
 typedef __externref_t externref_t;
+typedef __non_null_externref_t nn_externref_t;
 
 void helper(externref_t);
+void helper_2(nn_externref_t);
 
 // CHECK-LABEL: @handle(
 // CHECK-NEXT:  entry:
@@ -16,3 +18,48 @@ void helper(externref_t);
 void handle(externref_t obj) {
   helper(obj);
 }
+
+
+// CHECK-LABEL: @handle_2(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[OBJ_ADDR:%.*]] = alloca ptr addrspace(10), align 1
+// CHECK-NEXT:    store ptr addrspace(10) [[OBJ:%.*]], ptr [[OBJ_ADDR]], align 1
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr addrspace(10), ptr [[OBJ_ADDR]], align 1
+// CHECK-NEXT:    call void @helper_2(ptr addrspace(10) [[TMP0]])
+// CHECK-NEXT:    ret void
+//
+void handle_2(nn_externref_t obj) {
+  helper_2(obj);
+}
+
+
+nn_externref_t socketpair_js_concat(nn_externref_t, nn_externref_t)
+__attribute__((import_module("wasm:js-string"), import_name("concat")));
+
+nn_externref_t get_string_ref(const char *s);
+void print_string_ref(nn_externref_t);
+
+// CHECK-LABEL: @socketpair_example(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[STR1:%.*]] = alloca ptr addrspace(10), align 1
+// CHECK-NEXT:    [[STR2:%.*]] = alloca ptr addrspace(10), align 1
+// CHECK-NEXT:    [[RESULT:%.*]] = alloca ptr addrspace(10), align 1
+// CHECK-NEXT:    [[CALL:%.*]] = call ptr addrspace(10) @get_string_ref(ptr noundef @.str)
+// CHECK-NEXT:    store ptr addrspace(10) [[CALL]], ptr [[STR1]], align 1
+// CHECK-NEXT:    [[CALL1:%.*]] = call ptr addrspace(10) @get_string_ref(ptr noundef @.str.1)
+// CHECK-NEXT:    store ptr addrspace(10) [[CALL1]], ptr [[STR2]], align 1
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr addrspace(10), ptr [[STR1]], align 1
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr addrspace(10), ptr [[STR2]], align 1
+// CHECK-NEXT:    [[CALL2:%.*]] = call ptr addrspace(10) @socketpair_js_concat(ptr addrspace(10) [[TMP0]], ptr addrspace(10) [[TMP1]])
+// CHECK-NEXT:    store ptr addrspace(10) [[CALL2]], ptr [[RESULT]], align 1
+// CHECK-NEXT:    [[TMP2:%.*]] = load ptr addrspace(10), ptr [[RESULT]], align 1
+// CHECK-NEXT:    call void @print_string_ref(ptr addrspace(10) [[TMP2]])
+// CHECK-NEXT:    ret void
+//
+void socketpair_example() {
+    nn_externref_t str1 = get_string_ref("Hello, ");
+    nn_externref_t str2 = get_string_ref("world!");
+    nn_externref_t result = socketpair_js_concat(str1, str2);
+    print_string_ref(result);
+}
+
diff --git a/clang/test/Sema/wasm-refs-and-tables.c b/clang/test/Sema/wasm-refs-and-tables.c
index dd8536c52cd03..0237138ed9eda 100644
--- a/clang/test/Sema/wasm-refs-and-tables.c
+++ b/clang/test/Sema/wasm-refs-and-tables.c
@@ -9,6 +9,10 @@ __externref_t r1;
 extern __externref_t r2;
 static __externref_t r3;
 
+__non_null_externref_t nn_r1;
+extern __non_null_externref_t nn_r2;
+static __non_null_externref_t nn_r3;
+
 __externref_t *t1;               // expected-error {{pointer to WebAssembly reference type is not allowed}}
 __externref_t **t2;              // expected-error {{pointer to WebAssembly reference type is not allowed}}
 __externref_t ******t3;          // expected-error {{pointer to WebAssembly reference type is not allowed}}
@@ -19,10 +23,24 @@ __externref_t t7[0];             // expected-error {{WebAssembly table must be s
 static __externref_t t8[0][0];   // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
 static __externref_t (*t9)[0];   // expected-error {{cannot form a pointer to a WebAssembly table}}
 
+__non_null_externref_t *nn_t1;               // expected-error {{pointer to WebAssembly reference type is not allowed}}
+__non_null_externref_t **nn_t2;              // expected-error {{pointer to WebAssembly reference type is not allowed}}
+__non_null_externref_t ******nn_t3;          // expected-error {{pointer to WebAssembly reference type is not allowed}}
+static __non_null_externref_t nn_t4[3];      // expected-error {{only zero-length WebAssembly tables are currently supported}}
+static __non_null_externref_t nn_t5[];       // expected-error {{only zero-length WebAssembly tables are currently supported}}
+static __non_null_externref_t nn_t6[] = {0}; // expected-error {{only zero-length WebAssembly tables are currently supported}}
+__non_null_externref_t nn_t7[0];             // expected-error {{WebAssembly table must be static}}
+static __non_null_externref_t nn_t8[0][0];   // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
+static __non_null_externref_t (*nn_t9)[0];   // expected-error {{cannot form a pointer to a WebAssembly table}}
+
 static __externref_t table[0];
 static __externref_t other_table[0] = {};
 static __externref_t another_table[] = {}; // expected-error {{only zero-length WebAssembly tables are currently supported}}
 
+static __non_null_externref_t nn_table[0];
+static __non_null_externref_t nn_other_table[0] = {};
+static __non_null_externref_t nn_another_table[] = {}; // expected-error {{only zero-length WebAssembly tables are currently supported}}
+
 struct s {
   __externref_t f1;       // expected-error {{field has sizeless type '__externref_t'}}
   __externref_t f2[0];    // expected-error {{field has sizeless type '__externref_t'}}
@@ -33,6 +51,17 @@ struct s {
   __externref_t (*f7)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
 };
 
+
+struct nn_s {
+  __externref_t nn_f1;       // expected-error {{field has sizeless type '__externref_t'}}
+  __externref_t nn_f2[0];    // expected-error {{field has sizeless type '__externref_t'}}
+  __externref_t nn_f3[];     // expected-error {{field has sizeless type '__externref_t'}}
+  __externref_t nn_f4[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
+  __externref_t *nn_f5;      // expected-error {{pointer to WebAssembly reference type is not allowed}}
+  __externref_t ****nn_f6;   // expected-error {{pointer to WebAssembly reference type is not allowed}}
+  __externref_t (*nn_f7)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
+};
+
 union u {
   __externref_t f1;       // expected-error {{field has sizeless type '__externref_t'}}
   __externref_t f2[0];    // expected-error {{field has sizeless type '__externref_t'}}
@@ -43,16 +72,38 @@ union u {
   __externref_t (*f7)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
 };
 
+
+union nn_u {
+  __externref_t nn_f1;       // expected-error {{field has sizeless type '__externref_t'}}
+  __externref_t nn_f2[0];    // expected-error {{field has sizeless type '__externref_t'}}
+  __externref_t nn_f3[];     // expected-error {{field has sizeless type '__externref_t'}}
+  __externref_t nn_f4[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
+  __externref_t *nn_f5;      // expected-error {{pointer to WebAssembly reference type is not allowed}}
+  __externref_t ****nn_f6;   // expected-error {{pointer to WebAssembly reference type is not allowed}}
+  __externref_t (*f7)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
+};
+
 void illegal_argument_1(__externref_t table[]);     // expected-error {{cannot use WebAssembly table as a function parameter}}
 void illegal_argument_2(__externref_t table[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
 void illegal_argument_3(__externref_t *table);      // expected-error {{pointer to WebAssembly reference type is not allowed}}
 void illegal_argument_4(__externref_t ***table);    // expected-error {{pointer to WebAssembly reference type is not allowed}}
 void illegal_argument_5(__externref_t (*table)[0]); // expected-error {{cannot form a pointer to a WebAssembly table}}
 void illegal_argument_6(__externref_t table[0]);    // expected-error {{cannot use WebAssembly table as a function parameter}}
+                                                    
+void illegal_nn_argument_1(__non_null_externref_t table[]);     // expected-error {{cannot use WebAssembly table as a function parameter}}
+void illegal_nn_argument_2(__non_null_externref_t table[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
+void illegal_nn_argument_3(__non_null_externref_t *table);      // expected-error {{pointer to WebAssembly reference type is not allowed}}
+void illegal_nn_argument_4(__non_null_externref_t ***table);    // expected-error {{pointer to WebAssembly reference type is not allowed}}
+void illegal_nn_argument_5(__non_null_externref_t (*table)[0]); // expected-error {{cannot form a pointer to a WebAssembly table}}
+void illegal_nn_argument_6(__non_null_externref_t table[0]);    // expected-error {{cannot use WebAssembly table as a function parameter}}
 
 __externref_t *illegal_return_1();   // expected-error {{pointer to WebAssembly reference type is not allowed}}
 __externref_t ***illegal_return_2(); // expected-error {{pointer to WebAssembly reference type is not allowed}}
 __externref_t (*illegal_return_3())[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
+                                        
+__non_null_externref_t *illegal_nn_return_1();   // expected-error {{pointer to WebAssembly reference type is not allowed}}
+__non_null_externref_t ***illegal_nn_return_2(); // expected-error {{pointer to WebAssembly reference type is not allowed}}
+__non_null_externref_t (*illegal_nn_return_3())[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
 
 void varargs(int, ...);
 typedef void (*__funcref funcref_t)();
diff --git a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td
index f592ff287a0e3..93510e52af4d5 100644
--- a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td
+++ b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td
@@ -29,6 +29,11 @@ def int_wasm_memory_grow :
 //===----------------------------------------------------------------------===//
 def int_wasm_ref_null_extern :
   DefaultAttrsIntrinsic<[llvm_externref_ty], [], [IntrNoMem]>;
+
+// TODO: Is this correct?
+def int_wasm_ref_nonnull_extern
+    : DefaultAttrsIntrinsic<[llvm_externref_ty], [], [IntrNoMem]>;
+
 def int_wasm_ref_null_func :
   DefaultAttrsIntrinsic<[llvm_funcref_ty], [], [IntrNoMem]>;
 def int_wasm_ref_null_exn:
diff --git a/llvm/include/llvm/IR/Type.h b/llvm/include/llvm/IR/Type.h
index 74dd490729741..d488b1552240a 100644
--- a/llvm/include/llvm/IR/Type.h
+++ b/llvm/include/llvm/IR/Type.h
@@ -476,6 +476,7 @@ class Type {
   // Convenience methods for getting pointer types.
   //
   LLVM_ABI static Type *getWasm_ExternrefTy(LLVMContext &C);
+  LLVM_ABI static Type *getWasm_NonNullExternrefTy(LLVMContext &C);
   LLVM_ABI static Type *getWasm_FuncrefTy(LLVMContext &C);
 
   /// Return a pointer to the current type. This is equivalent to
diff --git a/llvm/lib/IR/Type.cpp b/llvm/lib/IR/Type.cpp
index 5e1bf2863191c..75afb0989c111 100644
--- a/llvm/lib/IR/Type.cpp
+++ b/llvm/lib/IR/Type.cpp
@@ -308,6 +308,13 @@ Type *Type::getWasm_ExternrefTy(LLVMContext &C) {
   return Ty;
 }
 
+Type *Type::getWasm_NonNullExternrefTy(LLVMContext &C) {
+  // opaque pointer in addrspace(10)
+  // TODO: Hey Jasmine, Is this correct?
+  static PointerType *Ty = PointerType::get(C, 10);
+  return Ty;
+}
+
 Type *Type::getWasm_FuncrefTy(LLVMContext &C) {
   // opaque pointer in addrspace(20)
   static PointerType *Ty = PointerType::get(C, 20);

``````````

</details>


https://github.com/llvm/llvm-project/pull/148935


More information about the llvm-commits mailing list