[clang] 24c7a10 - [Clang][WebAssembly] Fix WASM tables to allow `__funcref` function pointers (#178720)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Feb 4 09:55:41 PST 2026
Author: Demetrius Kanios
Date: 2026-02-04T09:55:36-08:00
New Revision: 24c7a10730a0c8ddce16741e9996bf11fc6ef885
URL: https://github.com/llvm/llvm-project/commit/24c7a10730a0c8ddce16741e9996bf11fc6ef885
DIFF: https://github.com/llvm/llvm-project/commit/24c7a10730a0c8ddce16741e9996bf11fc6ef885.diff
LOG: [Clang][WebAssembly] Fix WASM tables to allow `__funcref` function pointers (#178720)
Allows __funcref pointers to be used as the element type for WASM tables
in Clang (static, global, zero-length arrays of a reference type).
Modifies `QualType::isWebAssemblyFuncrefType` to correctly look at the
addrspace of the pointee, rather than the pointer type.
Related: #140933
Added:
clang/test/CodeGen/WebAssembly/builtins-table-externref.c
clang/test/CodeGen/WebAssembly/builtins-table-funcref.c
clang/test/Sema/wasm-funcref-table.c
Modified:
clang/lib/AST/Type.cpp
clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
Removed:
clang/test/CodeGen/WebAssembly/builtins-table.c
################################################################################
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 53082bcf78f6a..dcdbb62f9d62b 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2947,7 +2947,8 @@ bool QualType::isWebAssemblyExternrefType() const {
bool QualType::isWebAssemblyFuncrefType() const {
return getTypePtr()->isFunctionPointerType() &&
- getAddressSpace() == LangAS::wasm_funcref;
+ (getTypePtr()->getPointeeType().getAddressSpace() ==
+ LangAS::wasm_funcref);
}
QualType::PrimitiveDefaultInitializeKind
diff --git a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
index 1a1889a4139d3..edaba6e5998fc 100644
--- a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
@@ -633,8 +633,8 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID,
Function *Callee;
if (E->getArg(1)->getType().isWebAssemblyExternrefType())
Callee = CGM.getIntrinsic(Intrinsic::wasm_table_grow_externref);
- else if (E->getArg(2)->getType().isWebAssemblyFuncrefType())
- Callee = CGM.getIntrinsic(Intrinsic::wasm_table_fill_funcref);
+ else if (E->getArg(1)->getType().isWebAssemblyFuncrefType())
+ Callee = CGM.getIntrinsic(Intrinsic::wasm_table_grow_funcref);
else
llvm_unreachable(
"Unexpected reference type for __builtin_wasm_table_grow");
diff --git a/clang/test/CodeGen/WebAssembly/builtins-table.c b/clang/test/CodeGen/WebAssembly/builtins-table-externref.c
similarity index 100%
rename from clang/test/CodeGen/WebAssembly/builtins-table.c
rename to clang/test/CodeGen/WebAssembly/builtins-table-externref.c
diff --git a/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c b/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c
new file mode 100644
index 0000000000000..b4f729669a795
--- /dev/null
+++ b/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c
@@ -0,0 +1,69 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature
+// RUN: %clang_cc1 -triple wasm32 -target-feature +reference-types -disable-O0-optnone -emit-llvm %s -o - | opt -S -passes=mem2reg | FileCheck %s
+// REQUIRES: webassembly-registered-target
+
+typedef void (*__funcref funcref_t)();
+static funcref_t table[0];
+
+// CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_get
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = call ptr addrspace(20) @llvm.wasm.table.get.funcref(ptr addrspace(1) @table, i32 [[INDEX]])
+// CHECK-NEXT: ret ptr addrspace(20) [[TMP0]]
+//
+funcref_t test_builtin_wasm_table_get(int index) {
+ return __builtin_wasm_table_get(table, index);
+}
+
+// CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_set
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(20) noundef [[REF:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: call void @llvm.wasm.table.set.funcref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(20) [[REF]])
+// CHECK-NEXT: ret void
+//
+void test_builtin_wasm_table_set(int index, funcref_t ref) {
+ return __builtin_wasm_table_set(table, index, ref);
+}
+
+// CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_size
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = call i32 @llvm.wasm.table.size(ptr addrspace(1) @table)
+// CHECK-NEXT: ret i32 [[TMP0]]
+//
+int test_builtin_wasm_table_size() {
+ return __builtin_wasm_table_size(table);
+}
+
+
+// CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_grow
+// CHECK-SAME: (ptr addrspace(20) noundef [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.funcref(ptr addrspace(1) @table, ptr addrspace(20) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT: ret i32 [[TMP0]]
+//
+int test_builtin_wasm_table_grow(funcref_t ref, int nelem) {
+ return __builtin_wasm_table_grow(table, ref, nelem);
+}
+
+// CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_fill
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(20) noundef [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: call void @llvm.wasm.table.fill.funcref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(20) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT: ret void
+//
+void test_builtin_wasm_table_fill(int index, funcref_t ref, int nelem) {
+ __builtin_wasm_table_fill(table, index, ref, nelem);
+}
+
+static funcref_t other_table[0];
+
+// CHECK-LABEL: define {{[^@]+}}@test_table_copy
+// CHECK-SAME: (i32 noundef [[DST_IDX:%.*]], i32 noundef [[SRC_IDX:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: call void @llvm.wasm.table.copy(ptr addrspace(1) @table, ptr addrspace(1) @other_table, i32 [[SRC_IDX]], i32 [[DST_IDX]], i32 [[NELEM]])
+// CHECK-NEXT: ret void
+//
+void test_table_copy(int dst_idx, int src_idx, int nelem) {
+ __builtin_wasm_table_copy(table, other_table, dst_idx, src_idx, nelem);
+}
diff --git a/clang/test/Sema/wasm-funcref-table.c b/clang/test/Sema/wasm-funcref-table.c
new file mode 100644
index 0000000000000..9b4d53b8bbf08
--- /dev/null
+++ b/clang/test/Sema/wasm-funcref-table.c
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -triple wasm32 -target-feature +reference-types -fsyntax-only -verify %s
+
+typedef void (*__funcref fn_funcref)(void);
+
+// Valid funcref table declaration (zero-length, static)
+static fn_funcref valid_table[0]; // no error expected
+
+// Invalid: non-zero length
+static fn_funcref bad_table[1]; // expected-error {{only zero-length WebAssembly tables are currently supported}}
+
+// Array subscript on funcref table should be rejected
+void test_subscript(void) {
+ (void)valid_table[0]; // expected-error {{cannot subscript a WebAssembly table}}
+}
+
+// Original reproducer from https://github.com/llvm/llvm-project/issues/140933
+// The declaration should be rejected (not static, non-zero length)
+extern fn_funcref issue_table[1]; // expected-error {{WebAssembly table must be static}}
More information about the cfe-commits
mailing list