Skip to content

Commit 8b96ea0

Browse files
committed
[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`.
1 parent 86b89a6 commit 8b96ea0

File tree

23 files changed

+329
-45
lines changed

23 files changed

+329
-45
lines changed

clang/lib/AST/Type.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2559,9 +2559,6 @@ bool Type::isWebAssemblyTableType() const {
25592559
if (const auto *ATy = dyn_cast<ArrayType>(this))
25602560
return ATy->getElementType().isWebAssemblyReferenceType();
25612561

2562-
if (const auto *PTy = dyn_cast<PointerType>(this))
2563-
return PTy->getPointeeType().isWebAssemblyReferenceType();
2564-
25652562
return false;
25662563
}
25672564

clang/lib/Sema/Sema.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,13 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty,
832832
}
833833
}
834834
}
835+
// WebAssembly tables are not first class values and cannot decay to
836+
// pointers. If they are used anywhere they would decay, it is an error.
837+
if (ExprTy->isWebAssemblyTableType()) {
838+
Diag(E->getExprLoc(), diag::err_wasm_cast_table)
839+
<< 1 << E->getSourceRange();
840+
return ExprError();
841+
}
835842
}
836843

837844
if (ImplicitCastExpr *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4779,12 +4779,16 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
47794779
break;
47804780
}
47814781

4782-
case ICK_Array_To_Pointer:
4782+
case ICK_Array_To_Pointer: {
47834783
FromType = Context.getArrayDecayedType(FromType);
4784-
From = ImpCastExprToType(From, FromType, CK_ArrayToPointerDecay, VK_PRValue,
4785-
/*BasePath=*/nullptr, CCK)
4786-
.get();
4784+
auto Expr =
4785+
ImpCastExprToType(From, FromType, CK_ArrayToPointerDecay, VK_PRValue,
4786+
/*BasePath=*/nullptr, CCK);
4787+
if (Expr.isInvalid())
4788+
return ExprError();
4789+
From = Expr.get();
47874790
break;
4791+
}
47884792

47894793
case ICK_HLSL_Array_RValue:
47904794
if (ToType->isArrayParameterType()) {

clang/lib/Sema/SemaType.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1837,11 +1837,10 @@ QualType Sema::BuildPointerType(QualType T,
18371837
if (getLangOpts().OpenCL)
18381838
T = deduceOpenCLPointeeAddrSpace(*this, T);
18391839

1840-
// In WebAssembly, pointers to reference types and pointers to tables are
1841-
// illegal.
18421840
if (getASTContext().getTargetInfo().getTriple().isWasm()) {
1843-
if (T.isWebAssemblyReferenceType()) {
1844-
Diag(Loc, diag::err_wasm_reference_pr) << 0;
1841+
// In WebAssembly, pointers to tables are illegal.
1842+
if (T->isWebAssemblyTableType()) {
1843+
Diag(Loc, diag::err_wasm_table_pr) << 0;
18451844
return QualType();
18461845
}
18471846

clang/test/CodeGen/WebAssembly/wasm-externref.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,43 @@ void helper(externref_t);
1616
void handle(externref_t obj) {
1717
helper(obj);
1818
}
19+
20+
static externref_t __externref_table[0];
21+
22+
externref_t* p = 0;
23+
24+
__attribute__((constructor))
25+
// CHECK-LABEL: @set_p(
26+
// CHECK-NEXT: entry:
27+
// CHECK-NEXT: [[TMP0:%.*]] = call ptr addrspace(10) @llvm.wasm.ref.null.extern()
28+
// CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.wasm.table.grow.externref(ptr addrspace(1) @__externref_table, ptr addrspace(10) [[TMP0]], i32 1)
29+
// CHECK-NEXT: [[TMP2:%.*]] = inttoptr i32 [[TMP1]] to ptr
30+
// CHECK-NEXT: store ptr [[TMP2]], ptr @p, align 4
31+
// CHECK-NEXT: ret void
32+
//
33+
void set_p(void) {
34+
p = (externref_t *)__builtin_wasm_table_grow(__externref_table, __builtin_wasm_ref_null_extern(), 1);
35+
}
36+
37+
// CHECK-LABEL: @load_ref(
38+
// CHECK-NEXT: entry:
39+
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @p, align 4
40+
// CHECK-NEXT: [[TMP1:%.*]] = load ptr addrspace(10), ptr [[TMP0]], align 1
41+
// CHECK-NEXT: ret ptr addrspace(10) [[TMP1]]
42+
//
43+
externref_t load_ref() {
44+
return *p;
45+
}
46+
47+
// CHECK-LABEL: @store_ref(
48+
// CHECK-NEXT: entry:
49+
// CHECK-NEXT: [[X_ADDR:%.*]] = alloca ptr addrspace(10), align 1
50+
// CHECK-NEXT: store ptr addrspace(10) [[X:%.*]], ptr [[X_ADDR]], align 1
51+
// CHECK-NEXT: [[TMP0:%.*]] = load ptr addrspace(10), ptr [[X_ADDR]], align 1
52+
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr @p, align 4
53+
// CHECK-NEXT: store ptr addrspace(10) [[TMP0]], ptr [[TMP1]], align 1
54+
// CHECK-NEXT: ret void
55+
//
56+
void store_ref(externref_t x) {
57+
*p = x;
58+
}

clang/test/Sema/wasm-refs-and-tables.c

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ __externref_t r1;
99
extern __externref_t r2;
1010
static __externref_t r3;
1111

12-
__externref_t *t1; // expected-error {{pointer to WebAssembly reference type is not allowed}}
13-
__externref_t **t2; // expected-error {{pointer to WebAssembly reference type is not allowed}}
14-
__externref_t ******t3; // expected-error {{pointer to WebAssembly reference type is not allowed}}
12+
__externref_t *t1;
13+
__externref_t **t2;
14+
__externref_t ******t3;
1515
static __externref_t t4[3]; // expected-error {{only zero-length WebAssembly tables are currently supported}}
1616
static __externref_t t5[]; // expected-error {{only zero-length WebAssembly tables are currently supported}}
1717
static __externref_t t6[] = {0}; // expected-error {{only zero-length WebAssembly tables are currently supported}}
@@ -28,8 +28,8 @@ struct s {
2828
__externref_t f2[0]; // expected-error {{field has sizeless type '__externref_t'}}
2929
__externref_t f3[]; // expected-error {{field has sizeless type '__externref_t'}}
3030
__externref_t f4[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
31-
__externref_t *f5; // expected-error {{pointer to WebAssembly reference type is not allowed}}
32-
__externref_t ****f6; // expected-error {{pointer to WebAssembly reference type is not allowed}}
31+
__externref_t *f5;
32+
__externref_t ****f6;
3333
__externref_t (*f7)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
3434
};
3535

@@ -38,21 +38,21 @@ union u {
3838
__externref_t f2[0]; // expected-error {{field has sizeless type '__externref_t'}}
3939
__externref_t f3[]; // expected-error {{field has sizeless type '__externref_t'}}
4040
__externref_t f4[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
41-
__externref_t *f5; // expected-error {{pointer to WebAssembly reference type is not allowed}}
42-
__externref_t ****f6; // expected-error {{pointer to WebAssembly reference type is not allowed}}
41+
__externref_t *f5;
42+
__externref_t ****f6;
4343
__externref_t (*f7)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
4444
};
4545

46-
void illegal_argument_1(__externref_t table[]); // expected-error {{cannot use WebAssembly table as a function parameter}}
47-
void illegal_argument_2(__externref_t table[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
48-
void illegal_argument_3(__externref_t *table); // expected-error {{pointer to WebAssembly reference type is not allowed}}
49-
void illegal_argument_4(__externref_t ***table); // expected-error {{pointer to WebAssembly reference type is not allowed}}
50-
void illegal_argument_5(__externref_t (*table)[0]); // expected-error {{cannot form a pointer to a WebAssembly table}}
51-
void illegal_argument_6(__externref_t table[0]); // expected-error {{cannot use WebAssembly table as a function parameter}}
46+
void illegal_argument_1(__externref_t table[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
47+
void illegal_argument_2(__externref_t (*table)[0]); // expected-error {{cannot form a pointer to a WebAssembly table}}
5248

53-
__externref_t *illegal_return_1(); // expected-error {{pointer to WebAssembly reference type is not allowed}}
54-
__externref_t ***illegal_return_2(); // expected-error {{pointer to WebAssembly reference type is not allowed}}
55-
__externref_t (*illegal_return_3())[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
49+
void okay_argument_1(__externref_t *table);
50+
void okay_argument_2(__externref_t ***table);
51+
void okay_argument_3(__externref_t table[0]);
52+
53+
__externref_t *okay_return_1();
54+
__externref_t ***okay_return_2();
55+
__externref_t (*illegal_return3())[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
5656

5757
void varargs(int, ...);
5858
typedef void (*__funcref funcref_t)();
@@ -61,49 +61,47 @@ typedef void (*__funcref __funcref funcref_fail_t)(); // expected-warning {{attr
6161
__externref_t func(__externref_t ref) {
6262
&ref; // expected-error {{cannot take address of WebAssembly reference}}
6363
int foo = 40;
64-
(__externref_t *)(&foo); // expected-error {{pointer to WebAssembly reference type is not allowed}}
65-
(__externref_t ****)(&foo); // expected-error {{pointer to WebAssembly reference type is not allowed}}
64+
(__externref_t ****)(&foo);
6665
sizeof(ref); // expected-error {{invalid application of 'sizeof' to sizeless type '__externref_t'}}
6766
sizeof(__externref_t); // expected-error {{invalid application of 'sizeof' to sizeless type '__externref_t'}}
6867
sizeof(__externref_t[0]); // expected-error {{invalid application of 'sizeof' to WebAssembly table}}
6968
sizeof(table); // expected-error {{invalid application of 'sizeof' to WebAssembly table}}
7069
sizeof(__externref_t[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
71-
sizeof(__externref_t *); // expected-error {{pointer to WebAssembly reference type is not allowed}}
72-
sizeof(__externref_t ***); // expected-error {{pointer to WebAssembly reference type is not allowed}};
70+
sizeof(__externref_t *);
71+
sizeof(__externref_t ***);
7372
// expected-warning@+1 {{'_Alignof' applied to an expression is a GNU extension}}
7473
_Alignof(ref); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}}
7574
_Alignof(__externref_t); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}}
7675
_Alignof(__externref_t[]); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}}
7776
_Alignof(__externref_t[0]); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}}
7877
_Alignof(table); // expected-warning {{'_Alignof' applied to an expression is a GNU extension}} expected-error {{invalid application of 'alignof' to WebAssembly table}}
7978
_Alignof(__externref_t[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
80-
_Alignof(__externref_t *); // expected-error {{pointer to WebAssembly reference type is not allowed}}
81-
_Alignof(__externref_t ***); // expected-error {{pointer to WebAssembly reference type is not allowed}};
79+
_Alignof(__externref_t *);
80+
_Alignof(__externref_t ***);
8281
varargs(1, ref); // expected-error {{cannot pass expression of type '__externref_t' to variadic function}}
8382

8483
__externref_t lt1[0]; // expected-error {{WebAssembly table cannot be declared within a function}}
8584
static __externref_t lt2[0]; // expected-error {{WebAssembly table cannot be declared within a function}}
8685
static __externref_t lt3[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}}
8786
static __externref_t(*lt4)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}}
8887
// conly-error@+2 {{cannot use WebAssembly table as a function parameter}}
89-
// cpp-error@+1 {{no matching function for call to 'illegal_argument_1'}}
90-
illegal_argument_1(table);
88+
// cpp-error@+1 {{no matching function for call to 'okay_argument_3'}}
89+
okay_argument_3(table);
9190
varargs(1, table); // expected-error {{cannot use WebAssembly table as a function parameter}}
92-
table == 1; // expected-error {{invalid operands to binary expression ('__attribute__((address_space(1))) __externref_t[0]' and 'int')}}
93-
1 >= table; // expected-error {{invalid operands to binary expression ('int' and '__attribute__((address_space(1))) __externref_t[0]')}}
94-
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]')}}
95-
table !=- table; // expected-error {{invalid argument type '__attribute__((address_space(1))) __externref_t *' to unary expression}}
96-
!table; // expected-error {{invalid argument type '__attribute__((address_space(1))) __externref_t *' to unary expression}}
91+
table == 1; // expected-error {{cannot cast from a WebAssembly table}}
92+
1 >= table; // expected-error {{cannot cast from a WebAssembly table}}
93+
table == other_table; // expected-error {{cannot cast from a WebAssembly table}}
94+
table !=- table; // expected-error {{cannot cast from a WebAssembly table}}
95+
!table; // expected-error {{cannot cast from a WebAssembly table}}
9796
1 && table; // expected-error {{invalid operands to binary expression ('int' and '__attribute__((address_space(1))) __externref_t[0]')}}
9897
table || 1; // expected-error {{invalid operands to binary expression ('__attribute__((address_space(1))) __externref_t[0]' and 'int')}}
99-
1 ? table : table; // expected-error {{cannot use a WebAssembly table within a branch of a conditional expression}}
100-
table ? : other_table; // expected-error {{cannot use a WebAssembly table within a branch of a conditional expression}}
98+
table ? : other_table; // expected-error {{cannot cast from a WebAssembly table}}
10199
(void *)table; // expected-error {{cannot cast from a WebAssembly table}}
102100
void *u;
103101
u = table; // expected-error {{cannot assign a WebAssembly table}}
104102
void *v = table; // expected-error {{cannot assign a WebAssembly table}}
105103
&table; // expected-error {{cannot form a reference to a WebAssembly table}}
106-
(void)table;
104+
(void)table; // conly-error {{cannot cast from a WebAssembly table}}
107105

108106
table[0]; // expected-error {{cannot subscript a WebAssembly table}}
109107
table[0] = ref; // expected-error {{cannot subscript a WebAssembly table}}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# RUN: llvm-mc -filetype=obj -mattr=+reference-types -triple=wasm32-unknown-unknown %s -o %t.o
2+
# RUN: wasm-ld -o %t.wasm %t.o
3+
# RUN: obj2yaml %t.wasm | FileCheck %s
4+
5+
.tabletype __externref_table, externref
6+
__externref_table:
7+
8+
.globl _start
9+
_start:
10+
.functype _start () -> ()
11+
i32.const 0
12+
i32.load p
13+
table.get __externref_table
14+
drop
15+
end_function
16+
17+
18+
19+
.section .bss.p,"",@
20+
.globl p
21+
.p2align 2, 0x0
22+
p:
23+
.int32 0
24+
.size p, 4
25+
26+
# CHECK: - Type: TABLE
27+
# CHECK-NEXT: Tables:
28+
# CHECK-NEXT: - Index: 0
29+
# CHECK-NEXT: ElemType: EXTERNREF
30+
# CHECK-NEXT: Limits:
31+
# CHECK-NEXT: Minimum: 0x0
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# RUN: llvm-mc -filetype=obj -mattr=+reference-types -triple=wasm32-unknown-unknown %s -o %t.o
2+
# RUN: wasm-ld -o %t.wasm %t.o
3+
# RUN: obj2yaml %t.wasm | FileCheck %s
4+
5+
.tabletype __externref_table, externref
6+
7+
.globl _start
8+
_start:
9+
.functype _start () -> ()
10+
i32.const 0
11+
i32.load p
12+
table.get __externref_table
13+
drop
14+
end_function
15+
16+
17+
18+
.section .bss.p,"",@
19+
.globl p
20+
.p2align 2, 0x0
21+
p:
22+
.int32 0
23+
.size p, 4
24+
25+
# CHECK: - Type: TABLE
26+
# CHECK-NEXT: Tables:
27+
# CHECK-NEXT: - Index: 0
28+
# CHECK-NEXT: ElemType: EXTERNREF
29+
# CHECK-NEXT: Limits:
30+
# CHECK-NEXT: Minimum: 0x0

lld/wasm/Config.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,9 @@ struct Ctx {
248248
// Used as an address space for function pointers, with each function that
249249
// is used as a function pointer being allocated a slot.
250250
TableSymbol *indirectFunctionTable;
251+
// __externref_table
252+
// Used as an address space for pointers to externref.
253+
TableSymbol *externrefTable;
251254
};
252255
WasmSym sym;
253256

lld/wasm/Driver.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,6 +1496,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
14961496
// Provide the indirect function table if needed.
14971497
ctx.sym.indirectFunctionTable =
14981498
symtab->resolveIndirectFunctionTable(/*required =*/false);
1499+
ctx.sym.externrefTable = symtab->resolveExternrefTable();
14991500

15001501
if (errorCount())
15011502
return;

0 commit comments

Comments
 (0)