[clang] [lld] [llvm] [Wasm][Clang] Add support for pointer to externref (PR #163610)
Hood Chatham via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 15 12:18:12 PDT 2025
https://github.com/hoodmane updated https://github.com/llvm/llvm-project/pull/163610
>From d083935d54a1a84a0c54426ee38d38e6a9f04af8 Mon Sep 17 00:00:00 2001
From: Hood Chatham <roberthoodchatham at gmail.com>
Date: Wed, 15 Oct 2025 13:53:50 -0400
Subject: [PATCH] [Wasm][Clang] Add support for pointer to externref
Add support for values of type `__externref_t *`.
We add a special table called `__externref_table`. Loads and stores
to externref pointers are converted to table_get and table_set
instructions using `__externref_table`.
This is a bit tricky because tables are given type array of externref
`__externref_t table[]`. Tables are also not first class values and
most operations don't work on them. However, arrays decay to pointers.
Previously since `__externref_t*` was always illegal, this wasn't a
problem. I dealt with this by preventing arrays from decaying to
pointers if the value type of the array is `__externref_t`.
---
clang/lib/AST/Type.cpp | 3 -
clang/lib/Sema/Sema.cpp | 7 +++
clang/lib/Sema/SemaExprCXX.cpp | 12 ++--
clang/lib/Sema/SemaType.cpp | 7 +--
.../test/CodeGen/WebAssembly/wasm-externref.c | 40 ++++++++++++
clang/test/Sema/wasm-refs-and-tables.c | 62 +++++++++----------
lld/test/wasm/externref-table-defined.s | 31 ++++++++++
lld/test/wasm/externref-table-undefined.s | 30 +++++++++
lld/wasm/Config.h | 3 +
lld/wasm/Driver.cpp | 1 +
lld/wasm/SymbolTable.cpp | 43 +++++++++++++
lld/wasm/SymbolTable.h | 3 +
lld/wasm/Symbols.cpp | 1 +
lld/wasm/Symbols.h | 1 +
lld/wasm/Writer.cpp | 11 ++++
llvm/include/llvm/MC/MCSymbolWasm.h | 4 ++
llvm/lib/Object/WasmObjectFile.cpp | 12 +++-
.../WebAssembly/Utils/WasmAddressSpaces.h | 3 +
.../WebAssembly/WebAssemblyISelDAGToDAG.cpp | 43 +++++++++++++
.../WebAssembly/WebAssemblyUtilities.cpp | 25 ++++++++
.../Target/WebAssembly/WebAssemblyUtilities.h | 4 ++
.../CodeGen/WebAssembly/externref-load.ll | 13 ++++
.../CodeGen/WebAssembly/externref-store.ll | 14 +++++
23 files changed, 328 insertions(+), 45 deletions(-)
create mode 100644 lld/test/wasm/externref-table-defined.s
create mode 100644 lld/test/wasm/externref-table-undefined.s
create mode 100644 llvm/test/CodeGen/WebAssembly/externref-load.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/externref-store.ll
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index ee7a68ee8ba7e..48a00d5a8aa12 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2559,9 +2559,6 @@ bool Type::isWebAssemblyTableType() const {
if (const auto *ATy = dyn_cast<ArrayType>(this))
return ATy->getElementType().isWebAssemblyReferenceType();
- if (const auto *PTy = dyn_cast<PointerType>(this))
- return PTy->getPointeeType().isWebAssemblyReferenceType();
-
return false;
}
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 39fa25f66f3b7..f1851a7096f80 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -832,6 +832,13 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty,
}
}
}
+ // WebAssembly tables are not first class values and cannot decay to
+ // pointers. If they are used anywhere they would decay, it is an error.
+ if (ExprTy->isWebAssemblyTableType()) {
+ Diag(E->getExprLoc(), diag::err_wasm_cast_table)
+ << 1 << E->getSourceRange();
+ return ExprError();
+ }
}
if (ImplicitCastExpr *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 0fe242dce45e9..99e12ba0bb936 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -4779,12 +4779,16 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
break;
}
- case ICK_Array_To_Pointer:
+ case ICK_Array_To_Pointer: {
FromType = Context.getArrayDecayedType(FromType);
- From = ImpCastExprToType(From, FromType, CK_ArrayToPointerDecay, VK_PRValue,
- /*BasePath=*/nullptr, CCK)
- .get();
+ auto Expr =
+ ImpCastExprToType(From, FromType, CK_ArrayToPointerDecay, VK_PRValue,
+ /*BasePath=*/nullptr, CCK);
+ if (Expr.isInvalid())
+ return ExprError();
+ From = Expr.get();
break;
+ }
case ICK_HLSL_Array_RValue:
if (ToType->isArrayParameterType()) {
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index a9e7c34de94f4..89230071e1bb5 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -1837,11 +1837,10 @@ QualType Sema::BuildPointerType(QualType T,
if (getLangOpts().OpenCL)
T = deduceOpenCLPointeeAddrSpace(*this, T);
- // In WebAssembly, pointers to reference types and pointers to tables are
- // illegal.
if (getASTContext().getTargetInfo().getTriple().isWasm()) {
- if (T.isWebAssemblyReferenceType()) {
- Diag(Loc, diag::err_wasm_reference_pr) << 0;
+ // In WebAssembly, pointers to tables are illegal.
+ if (T->isWebAssemblyTableType()) {
+ Diag(Loc, diag::err_wasm_table_pr) << 0;
return QualType();
}
diff --git a/clang/test/CodeGen/WebAssembly/wasm-externref.c b/clang/test/CodeGen/WebAssembly/wasm-externref.c
index 788438bb4a86a..9ff517c1fd304 100644
--- a/clang/test/CodeGen/WebAssembly/wasm-externref.c
+++ b/clang/test/CodeGen/WebAssembly/wasm-externref.c
@@ -16,3 +16,43 @@ void helper(externref_t);
void handle(externref_t obj) {
helper(obj);
}
+
+static externref_t __externref_table[0];
+
+externref_t* p = 0;
+
+__attribute__((constructor))
+// CHECK-LABEL: @set_p(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = call ptr addrspace(10) @llvm.wasm.ref.null.extern()
+// CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.wasm.table.grow.externref(ptr addrspace(1) @__externref_table, ptr addrspace(10) [[TMP0]], i32 1)
+// CHECK-NEXT: [[TMP2:%.*]] = inttoptr i32 [[TMP1]] to ptr
+// CHECK-NEXT: store ptr [[TMP2]], ptr @p, align 4
+// CHECK-NEXT: ret void
+//
+void set_p(void) {
+ p = (externref_t *)__builtin_wasm_table_grow(__externref_table, __builtin_wasm_ref_null_extern(), 1);
+}
+
+// CHECK-LABEL: @load_ref(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @p, align 4
+// CHECK-NEXT: [[TMP1:%.*]] = load ptr addrspace(10), ptr [[TMP0]], align 1
+// CHECK-NEXT: ret ptr addrspace(10) [[TMP1]]
+//
+externref_t load_ref() {
+ return *p;
+}
+
+// CHECK-LABEL: @store_ref(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[X_ADDR:%.*]] = alloca ptr addrspace(10), align 1
+// CHECK-NEXT: store ptr addrspace(10) [[X:%.*]], ptr [[X_ADDR]], align 1
+// CHECK-NEXT: [[TMP0:%.*]] = load ptr addrspace(10), ptr [[X_ADDR]], align 1
+// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr @p, align 4
+// CHECK-NEXT: store ptr addrspace(10) [[TMP0]], ptr [[TMP1]], align 1
+// CHECK-NEXT: ret void
+//
+void store_ref(externref_t x) {
+ *p = x;
+}
diff --git a/clang/test/Sema/wasm-refs-and-tables.c b/clang/test/Sema/wasm-refs-and-tables.c
index dd8536c52cd03..6249d372c4344 100644
--- a/clang/test/Sema/wasm-refs-and-tables.c
+++ b/clang/test/Sema/wasm-refs-and-tables.c
@@ -9,9 +9,9 @@ __externref_t r1;
extern __externref_t r2;
static __externref_t 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}}
+__externref_t *t1;
+__externref_t **t2;
+__externref_t ******t3;
static __externref_t t4[3]; // expected-error {{only zero-length WebAssembly tables are currently supported}}
static __externref_t t5[]; // expected-error {{only zero-length WebAssembly tables are currently supported}}
static __externref_t t6[] = {0}; // expected-error {{only zero-length WebAssembly tables are currently supported}}
@@ -28,8 +28,8 @@ struct s {
__externref_t f2[0]; // expected-error {{field has sizeless type '__externref_t'}}
__externref_t f3[]; // expected-error {{field has sizeless type '__externref_t'}}
__externref_t f4[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
- __externref_t *f5; // expected-error {{pointer to WebAssembly reference type is not allowed}}
- __externref_t ****f6; // expected-error {{pointer to WebAssembly reference type is not allowed}}
+ __externref_t *f5;
+ __externref_t ****f6;
__externref_t (*f7)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
};
@@ -38,21 +38,21 @@ union u {
__externref_t f2[0]; // expected-error {{field has sizeless type '__externref_t'}}
__externref_t f3[]; // expected-error {{field has sizeless type '__externref_t'}}
__externref_t f4[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
- __externref_t *f5; // expected-error {{pointer to WebAssembly reference type is not allowed}}
- __externref_t ****f6; // expected-error {{pointer to WebAssembly reference type is not allowed}}
+ __externref_t *f5;
+ __externref_t ****f6;
__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_argument_1(__externref_t table[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
+void illegal_argument_2(__externref_t (*table)[0]); // expected-error {{cannot form a pointer to a WebAssembly table}}
-__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}}
+void okay_argument_1(__externref_t *table);
+void okay_argument_2(__externref_t ***table);
+void okay_argument_3(__externref_t table[0]);
+
+__externref_t *okay_return_1();
+__externref_t ***okay_return_2();
+__externref_t (*illegal_return3())[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
void varargs(int, ...);
typedef void (*__funcref funcref_t)();
@@ -61,15 +61,14 @@ typedef void (*__funcref __funcref funcref_fail_t)(); // expected-warning {{attr
__externref_t func(__externref_t ref) {
&ref; // expected-error {{cannot take address of WebAssembly reference}}
int foo = 40;
- (__externref_t *)(&foo); // expected-error {{pointer to WebAssembly reference type is not allowed}}
- (__externref_t ****)(&foo); // expected-error {{pointer to WebAssembly reference type is not allowed}}
+ (__externref_t ****)(&foo);
sizeof(ref); // expected-error {{invalid application of 'sizeof' to sizeless type '__externref_t'}}
sizeof(__externref_t); // expected-error {{invalid application of 'sizeof' to sizeless type '__externref_t'}}
sizeof(__externref_t[0]); // expected-error {{invalid application of 'sizeof' to WebAssembly table}}
sizeof(table); // expected-error {{invalid application of 'sizeof' to WebAssembly table}}
sizeof(__externref_t[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
- sizeof(__externref_t *); // expected-error {{pointer to WebAssembly reference type is not allowed}}
- sizeof(__externref_t ***); // expected-error {{pointer to WebAssembly reference type is not allowed}};
+ sizeof(__externref_t *);
+ sizeof(__externref_t ***);
// expected-warning at +1 {{'_Alignof' applied to an expression is a GNU extension}}
_Alignof(ref); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}}
_Alignof(__externref_t); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}}
@@ -77,8 +76,8 @@ __externref_t func(__externref_t ref) {
_Alignof(__externref_t[0]); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}}
_Alignof(table); // expected-warning {{'_Alignof' applied to an expression is a GNU extension}} expected-error {{invalid application of 'alignof' to WebAssembly table}}
_Alignof(__externref_t[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
- _Alignof(__externref_t *); // expected-error {{pointer to WebAssembly reference type is not allowed}}
- _Alignof(__externref_t ***); // expected-error {{pointer to WebAssembly reference type is not allowed}};
+ _Alignof(__externref_t *);
+ _Alignof(__externref_t ***);
varargs(1, ref); // expected-error {{cannot pass expression of type '__externref_t' to variadic function}}
__externref_t lt1[0]; // expected-error {{WebAssembly table cannot be declared within a function}}
@@ -86,24 +85,23 @@ __externref_t func(__externref_t ref) {
static __externref_t lt3[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
static __externref_t(*lt4)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
// conly-error at +2 {{cannot use WebAssembly table as a function parameter}}
- // cpp-error at +1 {{no matching function for call to 'illegal_argument_1'}}
- illegal_argument_1(table);
+ // cpp-error at +1 {{no matching function for call to 'okay_argument_3'}}
+ okay_argument_3(table);
varargs(1, table); // expected-error {{cannot use WebAssembly table as a function parameter}}
- table == 1; // expected-error {{invalid operands to binary expression ('__attribute__((address_space(1))) __externref_t[0]' and 'int')}}
- 1 >= table; // expected-error {{invalid operands to binary expression ('int' and '__attribute__((address_space(1))) __externref_t[0]')}}
- table == other_table; // expected-error {{invalid operands to binary expression ('__attribute__((address_space(1))) __externref_t[0]' and '__attribute__((address_space(1))) __externref_t[0]')}}
- table !=- table; // expected-error {{invalid argument type '__attribute__((address_space(1))) __externref_t *' to unary expression}}
- !table; // expected-error {{invalid argument type '__attribute__((address_space(1))) __externref_t *' to unary expression}}
+ table == 1; // expected-error {{cannot cast from a WebAssembly table}}
+ 1 >= table; // expected-error {{cannot cast from a WebAssembly table}}
+ table == other_table; // expected-error {{cannot cast from a WebAssembly table}}
+ table !=- table; // expected-error {{cannot cast from a WebAssembly table}}
+ !table; // expected-error {{cannot cast from a WebAssembly table}}
1 && table; // expected-error {{invalid operands to binary expression ('int' and '__attribute__((address_space(1))) __externref_t[0]')}}
table || 1; // expected-error {{invalid operands to binary expression ('__attribute__((address_space(1))) __externref_t[0]' and 'int')}}
- 1 ? table : table; // expected-error {{cannot use a WebAssembly table within a branch of a conditional expression}}
- table ? : other_table; // expected-error {{cannot use a WebAssembly table within a branch of a conditional expression}}
+ table ? : other_table; // expected-error {{cannot cast from a WebAssembly table}}
(void *)table; // expected-error {{cannot cast from a WebAssembly table}}
void *u;
u = table; // expected-error {{cannot assign a WebAssembly table}}
void *v = table; // expected-error {{cannot assign a WebAssembly table}}
&table; // expected-error {{cannot form a reference to a WebAssembly table}}
- (void)table;
+ (void)table; // conly-error {{cannot cast from a WebAssembly table}}
table[0]; // expected-error {{cannot subscript a WebAssembly table}}
table[0] = ref; // expected-error {{cannot subscript a WebAssembly table}}
diff --git a/lld/test/wasm/externref-table-defined.s b/lld/test/wasm/externref-table-defined.s
new file mode 100644
index 0000000000000..41bf7491c1271
--- /dev/null
+++ b/lld/test/wasm/externref-table-defined.s
@@ -0,0 +1,31 @@
+# RUN: llvm-mc -filetype=obj -mattr=+reference-types -triple=wasm32-unknown-unknown %s -o %t.o
+# RUN: wasm-ld -o %t.wasm %t.o
+# RUN: obj2yaml %t.wasm | FileCheck %s
+
+.tabletype __externref_table, externref
+__externref_table:
+
+.globl _start
+_start:
+ .functype _start () -> ()
+ i32.const 0
+ i32.load p
+ table.get __externref_table
+ drop
+ end_function
+
+
+
+.section .bss.p,"",@
+.globl p
+.p2align 2, 0x0
+p:
+ .int32 0
+ .size p, 4
+
+# CHECK: - Type: TABLE
+# CHECK-NEXT: Tables:
+# CHECK-NEXT: - Index: 0
+# CHECK-NEXT: ElemType: EXTERNREF
+# CHECK-NEXT: Limits:
+# CHECK-NEXT: Minimum: 0x0
diff --git a/lld/test/wasm/externref-table-undefined.s b/lld/test/wasm/externref-table-undefined.s
new file mode 100644
index 0000000000000..e1aacd674dd88
--- /dev/null
+++ b/lld/test/wasm/externref-table-undefined.s
@@ -0,0 +1,30 @@
+# RUN: llvm-mc -filetype=obj -mattr=+reference-types -triple=wasm32-unknown-unknown %s -o %t.o
+# RUN: wasm-ld -o %t.wasm %t.o
+# RUN: obj2yaml %t.wasm | FileCheck %s
+
+.tabletype __externref_table, externref
+
+.globl _start
+_start:
+ .functype _start () -> ()
+ i32.const 0
+ i32.load p
+ table.get __externref_table
+ drop
+ end_function
+
+
+
+.section .bss.p,"",@
+.globl p
+.p2align 2, 0x0
+p:
+ .int32 0
+ .size p, 4
+
+# CHECK: - Type: TABLE
+# CHECK-NEXT: Tables:
+# CHECK-NEXT: - Index: 0
+# CHECK-NEXT: ElemType: EXTERNREF
+# CHECK-NEXT: Limits:
+# CHECK-NEXT: Minimum: 0x0
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index 9d903e0c799db..d09cd58389d08 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -248,6 +248,9 @@ struct Ctx {
// Used as an address space for function pointers, with each function that
// is used as a function pointer being allocated a slot.
TableSymbol *indirectFunctionTable;
+ // __externref_table
+ // Used as an address space for pointers to externref.
+ TableSymbol *externrefTable;
};
WasmSym sym;
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 9c0e1b58e62f9..602dc565daa22 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -1496,6 +1496,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
// Provide the indirect function table if needed.
ctx.sym.indirectFunctionTable =
symtab->resolveIndirectFunctionTable(/*required =*/false);
+ ctx.sym.externrefTable = symtab->resolveExternrefTable();
if (errorCount())
return;
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index 91677b34ea2ca..041900b0c566e 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -818,6 +818,19 @@ TableSymbol *SymbolTable::createDefinedIndirectFunctionTable(StringRef name) {
return sym;
}
+TableSymbol *SymbolTable::createDefinedExternrefTable(StringRef name) {
+ const uint32_t invalidIndex = -1;
+ WasmLimits limits{0, 0, 0, 0}; // Set by the writer.
+ WasmTableType type{ValType::EXTERNREF, limits};
+ WasmTable desc{invalidIndex, type, name};
+ InputTable *table = make<InputTable>(desc, nullptr);
+ uint32_t flags = ctx.arg.exportTable ? 0 : WASM_SYMBOL_VISIBILITY_HIDDEN;
+ TableSymbol *sym = addSyntheticTable(name, flags, table);
+ sym->markLive();
+ sym->forceExport = ctx.arg.exportTable;
+ return sym;
+}
+
// Whether or not we need an indirect function table is usually a function of
// whether an input declares a need for it. However sometimes it's possible for
// no input to need the indirect function table, but then a late
@@ -859,6 +872,36 @@ TableSymbol *SymbolTable::resolveIndirectFunctionTable(bool required) {
return nullptr;
}
+TableSymbol *SymbolTable::resolveExternrefTable() {
+ Symbol *existing = find(externrefTableName);
+ if (existing) {
+ if (!isa<TableSymbol>(existing)) {
+ error(Twine("reserved symbol must be of type table: `") +
+ externrefTableName + "`");
+ return nullptr;
+ }
+ if (existing->isDefined()) {
+ error(Twine("reserved symbol must not be defined in input files: `") +
+ externrefTableName + "`");
+ return nullptr;
+ }
+ }
+
+ if (ctx.arg.importTable) {
+ if (existing) {
+ existing->importModule = defaultModule;
+ existing->importName = externrefTableName;
+ return cast<TableSymbol>(existing);
+ }
+ } else if ((existing && existing->isLive())) {
+ // A defined table is required. The existing table is
+ // guaranteed to be undefined due to the check above.
+ return createDefinedExternrefTable(externrefTableName);
+ }
+
+ return nullptr;
+}
+
void SymbolTable::addLazy(StringRef name, InputFile *file) {
LLVM_DEBUG(dbgs() << "addLazy: " << name << "\n");
diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h
index 5d09d8b685716..004933ca575c4 100644
--- a/lld/wasm/SymbolTable.h
+++ b/lld/wasm/SymbolTable.h
@@ -85,6 +85,7 @@ class SymbolTable {
InputFile *file, const WasmSignature *sig);
TableSymbol *resolveIndirectFunctionTable(bool required);
+ TableSymbol *resolveExternrefTable();
void addLazy(StringRef name, InputFile *f);
@@ -116,6 +117,8 @@ class SymbolTable {
TableSymbol *createDefinedIndirectFunctionTable(StringRef name);
TableSymbol *createUndefinedIndirectFunctionTable(StringRef name);
+ TableSymbol *createDefinedExternrefTable(StringRef name);
+ TableSymbol *createUndefinedExternrefTable(StringRef name);
// Maps symbol names to index into the symVector. -1 means that symbols
// is to not yet in the vector but it should have tracing enabled if it is
diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index f2040441e6257..f77ce35c4bae2 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -434,6 +434,7 @@ void printTraceSymbol(Symbol *sym) {
const char *defaultModule = "env";
const char *functionTableName = "__indirect_function_table";
+const char *externrefTableName = "__externref_table";
const char *memoryName = "memory";
} // namespace wasm
diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index 55ee21939ce07..e6d1c4ff1d00c 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -25,6 +25,7 @@ extern const char *defaultModule;
// The name under which to import or export the wasm table.
extern const char *functionTableName;
+extern const char *externrefTableName;
// The name under which to import or export the wasm memory.
extern const char *memoryName;
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 9a5b56fc52e2f..bfd74a8221009 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -946,6 +946,15 @@ static void finalizeIndirectFunctionTable() {
ctx.sym.indirectFunctionTable->setLimits(limits);
}
+static void finalizeExternrefTable() {
+ if (!ctx.sym.externrefTable)
+ return;
+
+ uint32_t tableSize = 0;
+ WasmLimits limits = {0, tableSize, 0, 0};
+ ctx.sym.externrefTable->setLimits(limits);
+}
+
static void scanRelocations() {
for (ObjFile *file : ctx.objectFiles) {
LLVM_DEBUG(dbgs() << "scanRelocations: " << file->getName() << "\n");
@@ -1773,6 +1782,8 @@ void Writer::run() {
scanRelocations();
log("-- finalizeIndirectFunctionTable");
finalizeIndirectFunctionTable();
+ log("-- finalizeExternrefTable");
+ finalizeExternrefTable();
log("-- createSyntheticInitFunctions");
createSyntheticInitFunctions();
log("-- assignIndexes");
diff --git a/llvm/include/llvm/MC/MCSymbolWasm.h b/llvm/include/llvm/MC/MCSymbolWasm.h
index 5c9f14e5e6d64..3e7953cc66ba4 100644
--- a/llvm/include/llvm/MC/MCSymbolWasm.h
+++ b/llvm/include/llvm/MC/MCSymbolWasm.h
@@ -116,6 +116,10 @@ class MCSymbolWasm : public MCSymbol {
return isTable() && hasTableType() &&
getTableType().ElemType == wasm::ValType::FUNCREF;
}
+ bool isExternrefTable() const {
+ return isTable() && hasTableType() &&
+ getTableType().ElemType == wasm::ValType::EXTERNREF;
+ }
void setFunctionTable(bool is64) {
setType(wasm::WASM_SYMBOL_TYPE_TABLE);
uint8_t flags =
diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index ee7a3068af91d..5ed5a1d7f2c9a 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -1098,7 +1098,11 @@ Error WasmObjectFile::parseRelocSection(StringRef Name, ReadContext &Ctx) {
case wasm::R_WASM_MEMORY_ADDR_REL_SLEB:
case wasm::R_WASM_MEMORY_ADDR_TLS_SLEB:
case wasm::R_WASM_MEMORY_ADDR_LOCREL_I32:
- if (!isValidDataSymbol(Reloc.Index))
+ // Allow table symbols in debug sections (for debug info referencing
+ // tables)
+ if (!isValidDataSymbol(Reloc.Index) &&
+ !(Section.Type == wasm::WASM_SEC_CUSTOM &&
+ isValidTableSymbol(Reloc.Index)))
return badReloc("invalid data relocation");
Reloc.Addend = readVarint32(Ctx);
break;
@@ -1107,7 +1111,11 @@ Error WasmObjectFile::parseRelocSection(StringRef Name, ReadContext &Ctx) {
case wasm::R_WASM_MEMORY_ADDR_I64:
case wasm::R_WASM_MEMORY_ADDR_REL_SLEB64:
case wasm::R_WASM_MEMORY_ADDR_TLS_SLEB64:
- if (!isValidDataSymbol(Reloc.Index))
+ // Allow table symbols in debug sections (for debug info referencing
+ // tables)
+ if (!isValidDataSymbol(Reloc.Index) &&
+ !(Section.Type == wasm::WASM_SEC_CUSTOM &&
+ isValidTableSymbol(Reloc.Index)))
return badReloc("invalid data relocation");
Reloc.Addend = readVarint64(Ctx);
break;
diff --git a/llvm/lib/Target/WebAssembly/Utils/WasmAddressSpaces.h b/llvm/lib/Target/WebAssembly/Utils/WasmAddressSpaces.h
index 2239badca69c3..189b31898b49c 100644
--- a/llvm/lib/Target/WebAssembly/Utils/WasmAddressSpaces.h
+++ b/llvm/lib/Target/WebAssembly/Utils/WasmAddressSpaces.h
@@ -40,6 +40,9 @@ inline bool isWasmVarAddressSpace(unsigned AS) {
inline bool isValidAddressSpace(unsigned AS) {
return isDefaultAddressSpace(AS) || isWasmVarAddressSpace(AS);
}
+inline bool isExternrefAddressSpace(unsigned AS) {
+ return AS == WASM_ADDRESS_SPACE_EXTERNREF;
+}
} // namespace WebAssembly
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
index 2541b0433ab59..9e7b25219fa96 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
@@ -227,6 +227,49 @@ void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
return;
}
+ case ISD::LOAD: {
+ LoadSDNode *LN = cast<LoadSDNode>(Node);
+ EVT VT = LN->getValueType(0);
+
+ if (VT == MVT::externref) {
+ MCSymbol *Table = WebAssembly::getOrCreateExternrefTableSymbol(
+ MF.getContext(), Subtarget);
+ SDValue TableSym = CurDAG->getMCSymbol(Table, PtrVT);
+ SDValue Ptr = LN->getOperand(1);
+
+ MachineSDNode *TableGet = CurDAG->getMachineNode(
+ WebAssembly::TABLE_GET_EXTERNREF, DL, MVT::externref, MVT::Other,
+ TableSym, Ptr, LN->getChain());
+
+ ReplaceNode(Node, TableGet);
+ CurDAG->RemoveDeadNode(Node);
+ return;
+ }
+ break;
+ }
+
+ case ISD::STORE: {
+ StoreSDNode *SN = cast<StoreSDNode>(Node);
+ SDValue Value = SN->getOperand(1);
+ EVT VT = Value.getValueType();
+
+ if (VT == MVT::externref) {
+ MCSymbol *Table = WebAssembly::getOrCreateExternrefTableSymbol(
+ MF.getContext(), Subtarget);
+ SDValue TableSym = CurDAG->getMCSymbol(Table, PtrVT);
+ SDValue Ptr = SN->getOperand(2);
+
+ MachineSDNode *TableSet = CurDAG->getMachineNode(
+ WebAssembly::TABLE_SET_EXTERNREF, DL, MVT::Other,
+ {TableSym, Ptr, Value, SN->getChain()});
+
+ ReplaceNode(Node, TableSet);
+ CurDAG->RemoveDeadNode(Node);
+ return;
+ }
+ break;
+ }
+
case ISD::INTRINSIC_WO_CHAIN: {
unsigned IntNo = Node->getConstantOperandVal(0);
switch (IntNo) {
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
index 890486778e700..f4fc9f02f9047 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
@@ -120,6 +120,31 @@ MCSymbolWasm *WebAssembly::getOrCreateFunctionTableSymbol(
return Sym;
}
+MCSymbolWasm *WebAssembly::getOrCreateExternrefTableSymbol(
+ MCContext &Ctx, const WebAssemblySubtarget *Subtarget) {
+ StringRef Name = "__externref_table";
+ auto *Sym = static_cast<MCSymbolWasm *>(Ctx.lookupSymbol(Name));
+ if (Sym) {
+ // If the symbol exists but doesn't have the proper table type, set it now
+ if (Sym->isExternrefTable()) {
+ // Type is correct all is good
+ } else if (!Sym->getType().has_value()) {
+ // Symbol has no type set yet. This happens if it was declared like
+ // static __externref_t __externref_table[0];
+ Sym->setType(wasm::WASM_SYMBOL_TYPE_TABLE);
+ Sym->setTableType(wasm::ValType::EXTERNREF);
+ } else {
+ Ctx.reportError(SMLoc(), "symbol is not a wasm externref table");
+ }
+ } else {
+ Sym = static_cast<MCSymbolWasm *>(Ctx.getOrCreateSymbol(Name));
+ Sym->setType(wasm::WASM_SYMBOL_TYPE_TABLE);
+ Sym->setTableType(wasm::ValType::EXTERNREF);
+ }
+ // MVP object files can't have symtab entries for tables.
+ return Sym;
+}
+
MCSymbolWasm *WebAssembly::getOrCreateFuncrefCallTableSymbol(
MCContext &Ctx, const WebAssemblySubtarget *Subtarget) {
StringRef Name = "__funcref_call_table";
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
index 046b1b5db2a79..795374cd1249f 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
@@ -56,6 +56,10 @@ MCSymbolWasm *
getOrCreateFuncrefCallTableSymbol(MCContext &Ctx,
const WebAssemblySubtarget *Subtarget);
+MCSymbolWasm *
+getOrCreateExternrefTableSymbol(MCContext &Ctx,
+ const WebAssemblySubtarget *Subtarget);
+
/// Find a catch instruction from an EH pad. Returns null if no catch
/// instruction found or the catch is in an invalid location.
MachineInstr *findCatch(MachineBasicBlock *EHPad);
diff --git a/llvm/test/CodeGen/WebAssembly/externref-load.ll b/llvm/test/CodeGen/WebAssembly/externref-load.ll
new file mode 100644
index 0000000000000..14f4e8be04a65
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/externref-load.ll
@@ -0,0 +1,13 @@
+; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s 2>&1 | FileCheck %s
+
+%externref = type ptr addrspace(10)
+
+; CHECK-LABEL: load_ref:
+; CHECK-NEXT: .functype load_ref (i32) -> (externref)
+; CHECK-NEXT: local.get 0
+; CHECK-NEXT: table.get __externref_table
+define %externref @load_ref(ptr %p) {
+entry:
+ %1 = load %externref, ptr %p
+ ret %externref %1
+}
diff --git a/llvm/test/CodeGen/WebAssembly/externref-store.ll b/llvm/test/CodeGen/WebAssembly/externref-store.ll
new file mode 100644
index 0000000000000..3cfdaaa0733fb
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/externref-store.ll
@@ -0,0 +1,14 @@
+; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s 2>&1 | FileCheck %s
+
+%externref = type ptr addrspace(10)
+
+; CHECK-LABEL: store_ref:
+; CHECK-NEXT: .functype store_ref (i32, externref) -> ()
+; CHECK-NEXT: local.get 0
+; CHECK-NEXT: local.get 1
+; CHECK-NEXT: table.set __externref_table
+define void @store_ref(ptr %p, %externref %x) {
+entry:
+ store %externref %x, ptr %p
+ ret void
+}
More information about the cfe-commits
mailing list