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(this)) return ATy->getElementType().isWebAssemblyReferenceType(); - if (const auto *PTy = dyn_cast(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(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@+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 9c0e1b58e62f9..602dc565daa22 100644 --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -1496,6 +1496,7 @@ void LinkerDriver::linkerMain(ArrayRef 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(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(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(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(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(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(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(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 +}