Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
12 changes: 8 additions & 4 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
7 changes: 3 additions & 4 deletions clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down
40 changes: 40 additions & 0 deletions clang/test/CodeGen/WebAssembly/wasm-externref.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
62 changes: 30 additions & 32 deletions clang/test/Sema/wasm-refs-and-tables.c
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
Expand All @@ -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}}
};

Expand All @@ -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)();
Expand All @@ -61,49 +61,47 @@ 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'}}
_Alignof(__externref_t[]); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}}
_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}}
static __externref_t lt2[0]; // expected-error {{WebAssembly table cannot be declared within a function}}
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}}
Expand Down
31 changes: 31 additions & 0 deletions lld/test/wasm/externref-table-defined.s
Original file line number Diff line number Diff line change
@@ -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
30 changes: 30 additions & 0 deletions lld/test/wasm/externref-table-undefined.s
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions lld/wasm/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions lld/wasm/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
43 changes: 43 additions & 0 deletions lld/wasm/SymbolTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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");

Expand Down
3 changes: 3 additions & 0 deletions lld/wasm/SymbolTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class SymbolTable {
InputFile *file, const WasmSignature *sig);

TableSymbol *resolveIndirectFunctionTable(bool required);
TableSymbol *resolveExternrefTable();

void addLazy(StringRef name, InputFile *f);

Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions lld/wasm/Symbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions lld/wasm/Symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading