[WebAssembly] Add initial support for compact imports proposal (#176617)
This change adds initial support to libObject for reading compact imports and support for writing compact imports in the linker. There is minimal testing here since to tools like lllvm-readobj, and obj2yaml don't currently report compact imports any differently. See https://github.com/WebAssembly/compact-import-section
This commit is contained in:
parent
ce94d63f0f
commit
11c2613342
@ -5603,6 +5603,8 @@ def mbulk_memory_opt : Flag<["-"], "mbulk-memory-opt">, Group<m_wasm_Features_Gr
|
||||
def mno_bulk_memory_opt : Flag<["-"], "mno-bulk-memory-opt">, Group<m_wasm_Features_Group>;
|
||||
def mcall_indirect_overlong : Flag<["-"], "mcall-indirect-overlong">, Group<m_wasm_Features_Group>;
|
||||
def mno_call_indirect_overlong : Flag<["-"], "mno-call-indirect-overlong">, Group<m_wasm_Features_Group>;
|
||||
def mcompact_imports : Flag<["-"], "mcompact-imports">, Group<m_wasm_Features_Group>;
|
||||
def mno_compact_imports : Flag<["-"], "mno-compact-imports">, Group<m_wasm_Features_Group>;
|
||||
def mexception_handing : Flag<["-"], "mexception-handling">, Group<m_wasm_Features_Group>;
|
||||
def mno_exception_handing : Flag<["-"], "mno-exception-handling">, Group<m_wasm_Features_Group>;
|
||||
def mextended_const : Flag<["-"], "mextended-const">, Group<m_wasm_Features_Group>;
|
||||
|
||||
@ -56,6 +56,7 @@ bool WebAssemblyTargetInfo::hasFeature(StringRef Feature) const {
|
||||
.Case("bulk-memory", HasBulkMemory)
|
||||
.Case("bulk-memory-opt", HasBulkMemoryOpt)
|
||||
.Case("call-indirect-overlong", HasCallIndirectOverlong)
|
||||
.Case("compact-imports", HasCompactImports)
|
||||
.Case("exception-handling", HasExceptionHandling)
|
||||
.Case("extended-const", HasExtendedConst)
|
||||
.Case("fp16", HasFP16)
|
||||
@ -119,6 +120,8 @@ void WebAssemblyTargetInfo::getTargetDefines(const LangOptions &Opts,
|
||||
Builder.defineMacro("__wasm_tail_call__");
|
||||
if (HasWideArithmetic)
|
||||
Builder.defineMacro("__wasm_wide_arithmetic__");
|
||||
// Note that not all wasm features appear here. For example,
|
||||
// HasCompatctImports
|
||||
|
||||
Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1");
|
||||
Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2");
|
||||
@ -191,6 +194,7 @@ bool WebAssemblyTargetInfo::initFeatureMap(
|
||||
auto addBleedingEdgeFeatures = [&]() {
|
||||
addGenericFeatures();
|
||||
Features["atomics"] = true;
|
||||
Features["compact-imports"] = true;
|
||||
Features["exception-handling"] = true;
|
||||
Features["extended-const"] = true;
|
||||
Features["fp16"] = true;
|
||||
@ -247,6 +251,14 @@ bool WebAssemblyTargetInfo::handleTargetFeatures(
|
||||
HasCallIndirectOverlong = false;
|
||||
continue;
|
||||
}
|
||||
if (Feature == "+compact-imports") {
|
||||
HasCompactImports = true;
|
||||
continue;
|
||||
}
|
||||
if (Feature == "-compact-imports") {
|
||||
HasCompactImports = false;
|
||||
continue;
|
||||
}
|
||||
if (Feature == "+exception-handling") {
|
||||
HasExceptionHandling = true;
|
||||
continue;
|
||||
|
||||
@ -62,6 +62,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {
|
||||
bool HasBulkMemory = false;
|
||||
bool HasBulkMemoryOpt = false;
|
||||
bool HasCallIndirectOverlong = false;
|
||||
bool HasCompactImports = false;
|
||||
bool HasExceptionHandling = false;
|
||||
bool HasExtendedConst = false;
|
||||
bool HasFP16 = false;
|
||||
|
||||
@ -106,3 +106,9 @@
|
||||
|
||||
// WIDE-ARITH: "-target-feature" "+wide-arithmetic"
|
||||
// NO-WIDE-ARITH: "-target-feature" "-wide-arithmetic"
|
||||
|
||||
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mcompact-imports 2>&1 | FileCheck %s -check-prefix=COMPACT-IMPORTS
|
||||
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mno-compact-imports 2>&1 | FileCheck %s -check-prefix=NO-COMPACT-IMPORTS
|
||||
|
||||
// COMPACT-IMPORTS: "-target-feature" "+compact-imports"
|
||||
// NO-COMPACT-IMPORTS: "-target-feature" "-compact-imports"
|
||||
|
||||
27
lld/test/wasm/compact-imports.s
Normal file
27
lld/test/wasm/compact-imports.s
Normal file
@ -0,0 +1,27 @@
|
||||
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
|
||||
# RUN: wasm-ld --experimental-pic --unresolved-symbols=import-dynamic %t.o -o %t.wasm
|
||||
|
||||
.functype foo () -> ()
|
||||
.functype bar () -> ()
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
.functype _start () -> ()
|
||||
call foo
|
||||
call bar
|
||||
end_function
|
||||
|
||||
.section .custom_section.target_features,"",@
|
||||
.int8 1
|
||||
.int8 43
|
||||
.int8 15
|
||||
.ascii "compact-imports"
|
||||
|
||||
# Neither llvm-readobj nor obj2yaml currently report compact imports differently
|
||||
# so the check here is just for the size of the import section. The Size here
|
||||
# is larger than 20 bytes without compact imports enabled.
|
||||
|
||||
# RUN: llvm-readobj --sections %t.wasm | FileCheck %s
|
||||
|
||||
# CHECK: Type: IMPORT (0x2)
|
||||
# CHECK-NEXT: Size: 20
|
||||
@ -241,9 +241,12 @@ void ImportSection::addImport(Symbol *sym) {
|
||||
void ImportSection::writeBody() {
|
||||
raw_ostream &os = bodyOutputStream;
|
||||
|
||||
writeUleb128(os, getNumImports(), "import count");
|
||||
uint32_t numImports = getNumImports();
|
||||
writeUleb128(os, numImports, "import count");
|
||||
|
||||
bool is64 = ctx.arg.is64.value_or(false);
|
||||
std::vector<WasmImport> imports;
|
||||
imports.reserve(numImports);
|
||||
|
||||
if (ctx.arg.memoryImport) {
|
||||
WasmImport import;
|
||||
@ -264,7 +267,7 @@ void ImportSection::writeBody() {
|
||||
import.Memory.Flags |= WASM_LIMITS_FLAG_HAS_PAGE_SIZE;
|
||||
import.Memory.PageSize = ctx.arg.pageSize;
|
||||
}
|
||||
writeImport(os, import);
|
||||
imports.push_back(import);
|
||||
}
|
||||
|
||||
for (const Symbol *sym : importedSymbols) {
|
||||
@ -286,7 +289,7 @@ void ImportSection::writeBody() {
|
||||
import.Kind = WASM_EXTERNAL_TABLE;
|
||||
import.Table = *tableSym->getTableType();
|
||||
}
|
||||
writeImport(os, import);
|
||||
imports.push_back(import);
|
||||
}
|
||||
|
||||
for (const Symbol *sym : gotSymbols) {
|
||||
@ -299,7 +302,34 @@ void ImportSection::writeBody() {
|
||||
else
|
||||
import.Module = "GOT.func";
|
||||
import.Field = sym->getName();
|
||||
writeImport(os, import);
|
||||
imports.push_back(import);
|
||||
}
|
||||
|
||||
bool hasCompactImports =
|
||||
out.targetFeaturesSec->features.contains("compact-imports");
|
||||
uint32_t i = 0;
|
||||
while (i < numImports) {
|
||||
const WasmImport &import = imports[i];
|
||||
if (hasCompactImports) {
|
||||
uint32_t groupSize = 1;
|
||||
for (uint32_t j = i + 1; j < numImports; j++) {
|
||||
if (imports[j].Module == import.Module)
|
||||
groupSize++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (groupSize > 1) {
|
||||
writeStr(os, import.Module, "module name");
|
||||
writeStr(os, "", "empty field name");
|
||||
writeU8(os, 0x7F, "compact imports encoding 1");
|
||||
writeUleb128(os, groupSize, "num compact imports");
|
||||
while (groupSize--) {
|
||||
writeCompactImport(os, imports[i++]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
writeImport(os, imports[i++]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -218,6 +218,10 @@ void writeTableType(raw_ostream &os, const WasmTableType &type) {
|
||||
|
||||
void writeImport(raw_ostream &os, const WasmImport &import) {
|
||||
writeStr(os, import.Module, "import module name");
|
||||
writeCompactImport(os, import);
|
||||
}
|
||||
|
||||
void writeCompactImport(raw_ostream &os, const WasmImport &import) {
|
||||
writeStr(os, import.Field, "import field name");
|
||||
writeU8(os, import.Kind, "import kind");
|
||||
switch (import.Kind) {
|
||||
|
||||
@ -62,6 +62,8 @@ void writeTableType(raw_ostream &os, const llvm::wasm::WasmTableType &type);
|
||||
|
||||
void writeImport(raw_ostream &os, const llvm::wasm::WasmImport &import);
|
||||
|
||||
void writeCompactImport(raw_ostream &os, const llvm::wasm::WasmImport &import);
|
||||
|
||||
void writeExport(raw_ostream &os, const llvm::wasm::WasmExport &export_);
|
||||
|
||||
} // namespace wasm
|
||||
|
||||
@ -250,6 +250,8 @@ private:
|
||||
Error parseSection(WasmSection &Sec);
|
||||
Error parseCustomSection(WasmSection &Sec, ReadContext &Ctx);
|
||||
|
||||
Error parseImport(ReadContext &Ctx, wasm::WasmImport &Im);
|
||||
|
||||
// Standard section types
|
||||
Error parseTypeSection(ReadContext &Ctx);
|
||||
Error parseImportSection(ReadContext &Ctx);
|
||||
|
||||
@ -284,9 +284,9 @@ static Error readInitExpr(wasm::WasmInitExpr &Expr,
|
||||
Expr.Body = ArrayRef<uint8_t>(Start, Ctx.Ptr - Start);
|
||||
return Error::success();
|
||||
default:
|
||||
return make_error<GenericBinaryError>(
|
||||
Twine("invalid opcode in init_expr: ") + Twine(unsigned(Opcode)),
|
||||
object_error::parse_failed);
|
||||
return make_error<GenericBinaryError>("invalid opcode in init_expr: " +
|
||||
Twine(unsigned(Opcode)),
|
||||
object_error::parse_failed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1276,60 +1276,86 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error WasmObjectFile::parseImport(ReadContext &Ctx, wasm::WasmImport &Im) {
|
||||
switch (Im.Kind) {
|
||||
case wasm::WASM_EXTERNAL_FUNCTION:
|
||||
NumImportedFunctions++;
|
||||
Im.SigIndex = readVaruint32(Ctx);
|
||||
if (Im.SigIndex >= Signatures.size())
|
||||
return make_error<GenericBinaryError>("invalid function type",
|
||||
object_error::parse_failed);
|
||||
break;
|
||||
case wasm::WASM_EXTERNAL_GLOBAL:
|
||||
NumImportedGlobals++;
|
||||
Im.Global.Type = readUint8(Ctx);
|
||||
Im.Global.Mutable = readVaruint1(Ctx);
|
||||
break;
|
||||
case wasm::WASM_EXTERNAL_MEMORY:
|
||||
Im.Memory = readLimits(Ctx);
|
||||
if (Im.Memory.Flags & wasm::WASM_LIMITS_FLAG_IS_64)
|
||||
HasMemory64 = true;
|
||||
break;
|
||||
case wasm::WASM_EXTERNAL_TABLE: {
|
||||
Im.Table = readTableType(Ctx);
|
||||
NumImportedTables++;
|
||||
auto ElemType = Im.Table.ElemType;
|
||||
if (ElemType != wasm::ValType::FUNCREF &&
|
||||
ElemType != wasm::ValType::EXTERNREF &&
|
||||
ElemType != wasm::ValType::EXNREF &&
|
||||
ElemType != wasm::ValType::OTHERREF)
|
||||
return make_error<GenericBinaryError>("invalid table element type",
|
||||
object_error::parse_failed);
|
||||
break;
|
||||
}
|
||||
case wasm::WASM_EXTERNAL_TAG:
|
||||
NumImportedTags++;
|
||||
if (readUint8(Ctx) != 0) // Reserved 'attribute' field
|
||||
return make_error<GenericBinaryError>("invalid attribute",
|
||||
object_error::parse_failed);
|
||||
Im.SigIndex = readVaruint32(Ctx);
|
||||
if (Im.SigIndex >= Signatures.size())
|
||||
return make_error<GenericBinaryError>("invalid tag type",
|
||||
object_error::parse_failed);
|
||||
break;
|
||||
default:
|
||||
return make_error<GenericBinaryError>("unexpected import kind: " +
|
||||
Twine(unsigned(Im.Kind)),
|
||||
object_error::parse_failed);
|
||||
}
|
||||
Imports.push_back(Im);
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error WasmObjectFile::parseImportSection(ReadContext &Ctx) {
|
||||
uint32_t Count = readVaruint32(Ctx);
|
||||
uint32_t NumTypes = Signatures.size();
|
||||
Imports.reserve(Count);
|
||||
for (uint32_t I = 0; I < Count; I++) {
|
||||
uint32_t I = 0;
|
||||
while (I < Count) {
|
||||
wasm::WasmImport Im;
|
||||
Im.Module = readString(Ctx);
|
||||
Im.Field = readString(Ctx);
|
||||
Im.Kind = readUint8(Ctx);
|
||||
switch (Im.Kind) {
|
||||
case wasm::WASM_EXTERNAL_FUNCTION:
|
||||
NumImportedFunctions++;
|
||||
Im.SigIndex = readVaruint32(Ctx);
|
||||
if (Im.SigIndex >= NumTypes)
|
||||
return make_error<GenericBinaryError>("invalid function type",
|
||||
object_error::parse_failed);
|
||||
break;
|
||||
case wasm::WASM_EXTERNAL_GLOBAL:
|
||||
NumImportedGlobals++;
|
||||
Im.Global.Type = readUint8(Ctx);
|
||||
Im.Global.Mutable = readVaruint1(Ctx);
|
||||
break;
|
||||
case wasm::WASM_EXTERNAL_MEMORY:
|
||||
Im.Memory = readLimits(Ctx);
|
||||
if (Im.Memory.Flags & wasm::WASM_LIMITS_FLAG_IS_64)
|
||||
HasMemory64 = true;
|
||||
break;
|
||||
case wasm::WASM_EXTERNAL_TABLE: {
|
||||
Im.Table = readTableType(Ctx);
|
||||
NumImportedTables++;
|
||||
auto ElemType = Im.Table.ElemType;
|
||||
if (ElemType != wasm::ValType::FUNCREF &&
|
||||
ElemType != wasm::ValType::EXTERNREF &&
|
||||
ElemType != wasm::ValType::EXNREF &&
|
||||
ElemType != wasm::ValType::OTHERREF)
|
||||
return make_error<GenericBinaryError>("invalid table element type",
|
||||
object_error::parse_failed);
|
||||
break;
|
||||
// 0x7E/0x7F along with an empty Field signals a block of compact imports.
|
||||
if (Im.Kind == 0x7E && Im.Field == "") {
|
||||
return make_error<GenericBinaryError>(
|
||||
"compact import format (0x7E) is not yet supported",
|
||||
object_error::parse_failed);
|
||||
} else if (Im.Kind == 0x7F && Im.Field == "") {
|
||||
uint32_t NumCompactImports = readVaruint32(Ctx);
|
||||
while (NumCompactImports--) {
|
||||
Im.Field = readString(Ctx);
|
||||
Im.Kind = readUint8(Ctx);
|
||||
Error rtn = parseImport(Ctx, Im);
|
||||
if (rtn)
|
||||
return rtn;
|
||||
I++;
|
||||
}
|
||||
} else {
|
||||
Error rtn = parseImport(Ctx, Im);
|
||||
if (rtn)
|
||||
return rtn;
|
||||
I++;
|
||||
}
|
||||
case wasm::WASM_EXTERNAL_TAG:
|
||||
NumImportedTags++;
|
||||
if (readUint8(Ctx) != 0) // Reserved 'attribute' field
|
||||
return make_error<GenericBinaryError>("invalid attribute",
|
||||
object_error::parse_failed);
|
||||
Im.SigIndex = readVaruint32(Ctx);
|
||||
if (Im.SigIndex >= NumTypes)
|
||||
return make_error<GenericBinaryError>("invalid tag type",
|
||||
object_error::parse_failed);
|
||||
break;
|
||||
default:
|
||||
return make_error<GenericBinaryError>("unexpected import kind",
|
||||
object_error::parse_failed);
|
||||
}
|
||||
Imports.push_back(Im);
|
||||
}
|
||||
if (Ctx.Ptr != Ctx.End)
|
||||
return make_error<GenericBinaryError>("import section ended prematurely",
|
||||
|
||||
@ -47,6 +47,7 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
|
||||
bool HasBulkMemory = false;
|
||||
bool HasBulkMemoryOpt = false;
|
||||
bool HasCallIndirectOverlong = false;
|
||||
bool HasCompactImports = false;
|
||||
bool HasExceptionHandling = false;
|
||||
bool HasExtendedConst = false;
|
||||
bool HasFP16 = false;
|
||||
@ -109,6 +110,7 @@ public:
|
||||
bool hasBulkMemory() const { return HasBulkMemory; }
|
||||
bool hasBulkMemoryOpt() const { return HasBulkMemoryOpt; }
|
||||
bool hasCallIndirectOverlong() const { return HasCallIndirectOverlong; }
|
||||
bool hasCompactImports() const { return HasCompactImports; }
|
||||
bool hasExceptionHandling() const { return HasExceptionHandling; }
|
||||
bool hasExtendedConst() const { return HasExtendedConst; }
|
||||
bool hasFP16() const { return HasFP16; }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user