[lld] [llvm] [WebAssembly][Object] Support more elem segment flags (PR #123427)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jan 17 16:14:02 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-webassembly
Author: Derek Schuff (dschuff)
<details>
<summary>Changes</summary>
Some tools (e.g. Rust tooling) produce element segment descriptors with neither
elemkind or element type descriptors, but with init exprs instead of func indices
(this is with the flags value of 4 in
https://webassembly.github.io/spec/core/binary/modules.html#element-section).
LLVM doesn't fully model reference types or the various ways to initialize element
segments, but we do want to correctly parse and skip over all type sections, so
this change updates the object parser to handle that case, and refactors for more
clarity.
Also support parsing files that export imported (undefined) functions.
---
Full diff: https://github.com/llvm/llvm-project/pull/123427.diff
7 Files Affected:
- (modified) lld/wasm/SyntheticSections.cpp (+1-1)
- (modified) llvm/include/llvm/BinaryFormat/Wasm.h (+9-1)
- (modified) llvm/lib/MC/WasmObjectWriter.cpp (+1-1)
- (modified) llvm/lib/Object/WasmObjectFile.cpp (+26-14)
- (modified) llvm/lib/ObjectYAML/WasmEmitter.cpp (+1-1)
- (modified) llvm/lib/ObjectYAML/WasmYAML.cpp (+1-1)
- (modified) llvm/test/Object/Inputs/WASM/multi-table.wasm ()
``````````diff
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index 715fba1ee6da54..7fb44b9f0c0091 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -594,7 +594,7 @@ void ElemSection::writeBody() {
}
writeInitExpr(os, initExpr);
- if (flags & WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND) {
+ if (flags & WASM_ELEM_SEGMENT_MASK_HAS_ELEM_DESC) {
// We only write active function table initializers, for which the elem kind
// is specified to be written as 0x00 and interpreted to mean "funcref".
const uint8_t elemKind = 0;
diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h
index 759e4321250912..19097b458fb93b 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -170,7 +170,7 @@ enum : unsigned {
WASM_ELEM_SEGMENT_HAS_TABLE_NUMBER = 0x02, // if passive == 0
WASM_ELEM_SEGMENT_HAS_INIT_EXPRS = 0x04,
};
-const unsigned WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND = 0x3;
+const unsigned WASM_ELEM_SEGMENT_MASK_HAS_ELEM_DESC = 0x3;
// Feature policy prefixes used in the custom "target_features" section
enum : uint8_t {
@@ -415,6 +415,14 @@ struct WasmDataSegment {
uint32_t Comdat; // from the "comdat info" section
};
+// 3 different element segment modes are encodable. This class is currently
+// only used during decoding (see WasmElemSegment below).
+enum class ElemSegmentMode {
+ Active,
+ Passive,
+ Declarative
+};
+
// Represents a Wasm element segment, with some limitations compared the spec:
// 1) Does not model passive or declarative segments (Segment will end up with
// an Offset field of i32.const 0)
diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp
index 29a8c53d350a40..8ddbe929e68b94 100644
--- a/llvm/lib/MC/WasmObjectWriter.cpp
+++ b/llvm/lib/MC/WasmObjectWriter.cpp
@@ -1019,7 +1019,7 @@ void WasmObjectWriter::writeElemSection(
encodeSLEB128(InitialTableOffset, W->OS);
W->OS << char(wasm::WASM_OPCODE_END);
- if (Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND) {
+ if (Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_DESC) {
// We only write active function table initializers, for which the elem kind
// is specified to be written as 0x00 and interpreted to mean "funcref".
const uint8_t ElemKind = 0;
diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index 2c9b878a4cde93..beed5774813484 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -1440,15 +1440,20 @@ Error WasmObjectFile::parseExportSection(ReadContext &Ctx) {
Info.Flags = 0;
switch (Ex.Kind) {
case wasm::WASM_EXTERNAL_FUNCTION: {
- if (!isDefinedFunctionIndex(Ex.Index))
+ if (!isValidFunctionIndex(Ex.Index))
return make_error<GenericBinaryError>("invalid function export",
object_error::parse_failed);
- getDefinedFunction(Ex.Index).ExportName = Ex.Name;
Info.Kind = wasm::WASM_SYMBOL_TYPE_FUNCTION;
Info.ElementIndex = Ex.Index;
- unsigned FuncIndex = Info.ElementIndex - NumImportedFunctions;
- wasm::WasmFunction &Function = Functions[FuncIndex];
- Signature = &Signatures[Function.SigIndex];
+ if (isDefinedFunctionIndex(Ex.Index)) {
+ getDefinedFunction(Ex.Index).ExportName = Ex.Name;
+ unsigned FuncIndex = Info.ElementIndex - NumImportedFunctions;
+ wasm::WasmFunction &Function = Functions[FuncIndex];
+ Signature = &Signatures[Function.SigIndex];
+ }
+ // Else the function is imported. LLVM object files don't use this
+ // pattern and we still treat this as an undefined symbol, but we want to
+ // parse it without crashing.
break;
}
case wasm::WASM_EXTERNAL_GLOBAL: {
@@ -1645,17 +1650,24 @@ Error WasmObjectFile::parseElemSection(ReadContext &Ctx) {
return make_error<GenericBinaryError>(
"Unsupported flags for element segment", object_error::parse_failed);
- bool IsPassive = (Segment.Flags & wasm::WASM_ELEM_SEGMENT_IS_PASSIVE) != 0;
- bool IsDeclarative =
- IsPassive && (Segment.Flags & wasm::WASM_ELEM_SEGMENT_IS_DECLARATIVE);
+ wasm::ElemSegmentMode Mode;
+ if ((Segment.Flags & wasm::WASM_ELEM_SEGMENT_IS_PASSIVE) == 0) {
+ Mode = wasm::ElemSegmentMode::Active;
+ } else if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_IS_DECLARATIVE) {
+ Mode = wasm::ElemSegmentMode::Declarative;
+ } else {
+ Mode = wasm::ElemSegmentMode::Passive;
+ }
bool HasTableNumber =
- !IsPassive &&
+ Mode == wasm::ElemSegmentMode::Active &&
(Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_TABLE_NUMBER);
+ bool HasElemKind =
+ (Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_DESC) &&
+ !(Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_INIT_EXPRS);
+ bool HasElemType = (Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_DESC) &&
+ (Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_INIT_EXPRS);
bool HasInitExprs =
(Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_INIT_EXPRS);
- bool HasElemKind =
- (Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND) &&
- !HasInitExprs;
if (HasTableNumber)
Segment.TableNumber = readVaruint32(Ctx);
@@ -1666,7 +1678,7 @@ Error WasmObjectFile::parseElemSection(ReadContext &Ctx) {
return make_error<GenericBinaryError>("invalid TableNumber",
object_error::parse_failed);
- if (IsPassive || IsDeclarative) {
+ if (Mode != wasm::ElemSegmentMode::Active) {
Segment.Offset.Extended = false;
Segment.Offset.Inst.Opcode = wasm::WASM_OPCODE_I32_CONST;
Segment.Offset.Inst.Value.Int32 = 0;
@@ -1692,7 +1704,7 @@ Error WasmObjectFile::parseElemSection(ReadContext &Ctx) {
object_error::parse_failed);
Segment.ElemKind = wasm::ValType::FUNCREF;
}
- } else if (HasInitExprs) {
+ } else if (HasElemType) {
auto ElemType = parseValType(Ctx, readVaruint32(Ctx));
Segment.ElemKind = ElemType;
} else {
diff --git a/llvm/lib/ObjectYAML/WasmEmitter.cpp b/llvm/lib/ObjectYAML/WasmEmitter.cpp
index 817d364694b43a..bd016764f58626 100644
--- a/llvm/lib/ObjectYAML/WasmEmitter.cpp
+++ b/llvm/lib/ObjectYAML/WasmEmitter.cpp
@@ -497,7 +497,7 @@ void WasmWriter::writeSectionContent(raw_ostream &OS,
writeInitExpr(OS, Segment.Offset);
- if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND) {
+ if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_DESC) {
// We only support active function table initializers, for which the elem
// kind is specified to be written as 0x00 and interpreted to mean
// "funcref".
diff --git a/llvm/lib/ObjectYAML/WasmYAML.cpp b/llvm/lib/ObjectYAML/WasmYAML.cpp
index 0636e19e05353d..6af66ba62be188 100644
--- a/llvm/lib/ObjectYAML/WasmYAML.cpp
+++ b/llvm/lib/ObjectYAML/WasmYAML.cpp
@@ -381,7 +381,7 @@ void MappingTraits<WasmYAML::ElemSegment>::mapping(
Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_TABLE_NUMBER)
IO.mapOptional("TableNumber", Segment.TableNumber);
if (!IO.outputting() ||
- Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND)
+ Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_DESC)
IO.mapOptional("ElemKind", Segment.ElemKind);
// TODO: Omit "offset" for passive segments? It's neither meaningful nor
// encoded.
diff --git a/llvm/test/Object/Inputs/WASM/multi-table.wasm b/llvm/test/Object/Inputs/WASM/multi-table.wasm
index 47f5d8311cb74f..81e52a2d3e2865 100644
Binary files a/llvm/test/Object/Inputs/WASM/multi-table.wasm and b/llvm/test/Object/Inputs/WASM/multi-table.wasm differ
``````````
</details>
https://github.com/llvm/llvm-project/pull/123427
More information about the llvm-commits
mailing list