-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[WebAssembly][Clang] Add support for pointer to externref #163610
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
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-llvm-mc @llvm/pr-subscribers-lld-wasm Author: Hood Chatham (hoodmane) ChangesAdd support for values of type This is a bit tricky because tables are given type array of externref Patch is 30.07 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/163610.diff 23 Files Affected:
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index f7949e94d227e..2e37d90171ad4 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2613,9 +2613,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 12e5c658e7f3a..98578753d4d04 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 29c9c47d4504c..0e3fc9faee281 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -4774,12 +4774,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 d745cdbf0526f..8e436069bbfdf 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@+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@+2 {{cannot use WebAssembly table as a function parameter}}
- // cpp-error@+1 {{no matching function for call to 'illegal_argument_1'}}
- illegal_argument_1(table);
+ // cpp-error@+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 1c5d21c06f5af..01f659b9b6ed9 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -1493,6 +1493,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 *createDefinedExt...
[truncated]
|
f4b4559
to
8b96ea0
Compare
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`.
8b96ea0
to
d083935
Compare
I think this is a continuation of the work that @pmatos started. So CC'ing him in case he has any time to take a look. Also @dschuff and @tlively Its quite a large and consequential change so perhaps you could describe here (or in the link a bug?) what you use case is and what kind of thing things unlock for you? |
(BTW, the commit title prefix we use for Wasm is |
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
.