Skip to content

Commit bad0cc8

Browse files
authored
Add initial support for compact import section. NFC (#2685)
See https://github.com/WebAssembly/compact-import-section For this initial commit I've just added a single simple test in form of test/binary/compact-imports.txt. Once we update the `testsuite` repo we can pull in the official tests.
1 parent e9affb4 commit bad0cc8

19 files changed

+408
-143
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ Wabt has been compiled to JavaScript via emscripten. Some of the functionality i
6363
| [extended-const][] | `--enable-extended-const` | ||||||
6464
| [relaxed-simd][] | `--enable-relaxed-simd` | ||||| |
6565
| [custom-page-sizes][] | `--enable-custom-page-sizes`| ||||||
66+
| [compact-imports][] | `--enable-compact-imports` | || || | |
6667
| [function-references][] | `--enable-function-references` | ||||| |
6768

6869
[exception handling]: https://github.com/WebAssembly/exception-handling
@@ -82,6 +83,7 @@ Wabt has been compiled to JavaScript via emscripten. Some of the functionality i
8283
[relaxed-simd]: https://github.com/WebAssembly/relaxed-simd
8384
[custom-page-sizes]: https://github.com/WebAssembly/custom-page-sizes
8485
[function-references]: https://github.com/WebAssembly/function-references
86+
[compact-imports]: https://github.com/WebAssembly/compact-import-section
8587

8688
## Cloning
8789

include/wabt/feature.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,4 @@ WABT_FEATURE(multi_memory, "multi-memory", false, "Multi-mem
4141
WABT_FEATURE(extended_const, "extended-const", false, "Extended constant expressions")
4242
WABT_FEATURE(relaxed_simd, "relaxed-simd", false, "Relaxed SIMD")
4343
WABT_FEATURE(custom_page_sizes, "custom-page-sizes", false, "Custom page sizes")
44+
WABT_FEATURE(compact_imports, "compact-imports", false, "Compact import section")

src/binary-reader.cc

Lines changed: 97 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ class BinaryReader {
177177
[[nodiscard]] Result ReadCustomSection(Index section_index,
178178
Offset section_size);
179179
[[nodiscard]] Result ReadTypeSection(Offset section_size);
180+
[[nodiscard]] Result ReadImport(Index i,
181+
std::string_view module_name,
182+
std::string_view field_name,
183+
ExternalKind kind);
180184
[[nodiscard]] Result ReadImportSection(Offset section_size);
181185
[[nodiscard]] Result ReadFunctionSection(Offset section_size);
182186
[[nodiscard]] Result ReadTableSection(Offset section_size);
@@ -2687,72 +2691,111 @@ Result BinaryReader::ReadTypeSection(Offset section_size) {
26872691
return Result::Ok;
26882692
}
26892693

2694+
Result BinaryReader::ReadImport(Index i,
2695+
std::string_view module_name,
2696+
std::string_view field_name,
2697+
ExternalKind kind) {
2698+
CALLBACK(OnImport, i, kind, module_name, field_name);
2699+
switch (kind) {
2700+
case ExternalKind::Func: {
2701+
Index sig_index;
2702+
CHECK_RESULT(ReadIndex(&sig_index, "import signature index"));
2703+
CALLBACK(OnImportFunc, i, module_name, field_name, num_func_imports_,
2704+
sig_index);
2705+
num_func_imports_++;
2706+
break;
2707+
}
2708+
2709+
case ExternalKind::Table: {
2710+
Limits elem_limits;
2711+
Type elem_type;
2712+
CHECK_RESULT(ReadRefType(&elem_type, "table elem type"));
2713+
CHECK_RESULT(ReadTable(&elem_limits));
2714+
CALLBACK(OnImportTable, i, module_name, field_name, num_table_imports_,
2715+
elem_type, &elem_limits);
2716+
num_table_imports_++;
2717+
break;
2718+
}
2719+
2720+
case ExternalKind::Memory: {
2721+
Limits page_limits;
2722+
uint32_t page_size;
2723+
CHECK_RESULT(ReadMemory(&page_limits, &page_size));
2724+
CALLBACK(OnImportMemory, i, module_name, field_name, num_memory_imports_,
2725+
&page_limits, page_size);
2726+
num_memory_imports_++;
2727+
break;
2728+
}
2729+
2730+
case ExternalKind::Global: {
2731+
Type type;
2732+
bool mutable_;
2733+
CHECK_RESULT(ReadGlobalHeader(&type, &mutable_));
2734+
CALLBACK(OnImportGlobal, i, module_name, field_name, num_global_imports_,
2735+
type, mutable_);
2736+
num_global_imports_++;
2737+
break;
2738+
}
2739+
2740+
case ExternalKind::Tag: {
2741+
Index sig_index;
2742+
ERROR_UNLESS(options_.features.exceptions_enabled(),
2743+
"invalid import tag kind: exceptions not allowed");
2744+
CHECK_RESULT(ReadTagType(&sig_index));
2745+
CALLBACK(OnImportTag, i, module_name, field_name, num_tag_imports_,
2746+
sig_index);
2747+
num_tag_imports_++;
2748+
break;
2749+
}
2750+
}
2751+
2752+
return Result::Ok;
2753+
}
2754+
26902755
Result BinaryReader::ReadImportSection(Offset section_size) {
26912756
CALLBACK(BeginImportSection, section_size);
26922757
Index num_imports;
26932758
CHECK_RESULT(ReadCount(&num_imports, "import count"));
26942759
CALLBACK(OnImportCount, num_imports);
2695-
for (Index i = 0; i < num_imports; ++i) {
2760+
Index i = 0;
2761+
while (i < num_imports) {
26962762
std::string_view module_name;
26972763
CHECK_RESULT(ReadStr(&module_name, "import module name"));
26982764
std::string_view field_name;
26992765
CHECK_RESULT(ReadStr(&field_name, "import field name"));
27002766

2701-
ExternalKind kind;
2702-
CHECK_RESULT(ReadExternalKind(&kind, "import kind", "import"));
2703-
CALLBACK(OnImport, i, kind, module_name, field_name);
2704-
2705-
switch (kind) {
2706-
case ExternalKind::Func: {
2707-
Index sig_index;
2708-
CHECK_RESULT(ReadIndex(&sig_index, "import signature index"));
2709-
CALLBACK(OnImportFunc, i, module_name, field_name, num_func_imports_,
2710-
sig_index);
2711-
num_func_imports_++;
2712-
break;
2713-
}
2714-
2715-
case ExternalKind::Table: {
2716-
Type elem_type;
2717-
Limits elem_limits;
2718-
CHECK_RESULT(ReadRefType(&elem_type, "table elem type"));
2719-
CHECK_RESULT(ReadTable(&elem_limits));
2720-
CALLBACK(OnImportTable, i, module_name, field_name, num_table_imports_,
2721-
elem_type, &elem_limits);
2722-
num_table_imports_++;
2723-
break;
2724-
}
2725-
2726-
case ExternalKind::Memory: {
2727-
Limits page_limits;
2728-
uint32_t page_size;
2729-
CHECK_RESULT(ReadMemory(&page_limits, &page_size));
2730-
CALLBACK(OnImportMemory, i, module_name, field_name,
2731-
num_memory_imports_, &page_limits, page_size);
2732-
num_memory_imports_++;
2733-
break;
2734-
}
2767+
uint8_t kind_u8;
2768+
CHECK_RESULT(ReadU8(&kind_u8, "import kind"));
27352769

2736-
case ExternalKind::Global: {
2737-
Type type;
2738-
bool mutable_;
2739-
CHECK_RESULT(ReadGlobalHeader(&type, &mutable_));
2740-
CALLBACK(OnImportGlobal, i, module_name, field_name,
2741-
num_global_imports_, type, mutable_);
2742-
num_global_imports_++;
2743-
break;
2744-
}
2745-
2746-
case ExternalKind::Tag: {
2747-
ERROR_UNLESS(options_.features.exceptions_enabled(),
2748-
"invalid import tag kind: exceptions not allowed");
2749-
Index sig_index;
2750-
CHECK_RESULT(ReadTagType(&sig_index));
2751-
CALLBACK(OnImportTag, i, module_name, field_name, num_tag_imports_,
2752-
sig_index);
2753-
num_tag_imports_++;
2754-
break;
2770+
ExternalKind kind;
2771+
if (field_name == "" && (kind_u8 == 0x7F || kind_u8 == 0x7E)) {
2772+
ERROR_UNLESS(options_.features.compact_imports_enabled(),
2773+
"module uses compact imports, but feature not enabled");
2774+
Index num_compact_imports;
2775+
if (kind_u8 == 0x7E) {
2776+
// Read the import kind once and re-used for each of num_compact_imports
2777+
CHECK_RESULT(ReadExternalKind(&kind, "compact import kind", "import"));
2778+
CHECK_RESULT(ReadCount(&num_compact_imports, "compact import count"));
2779+
for (Index j = 0; j < num_compact_imports; ++j) {
2780+
CHECK_RESULT(ReadStr(&field_name, "compact import field name"));
2781+
CHECK_RESULT(ReadImport(i++, module_name, field_name, kind));
2782+
}
2783+
} else {
2784+
CHECK_RESULT(ReadCount(&num_compact_imports, "compact import count"));
2785+
for (Index j = 0; j < num_compact_imports; ++j) {
2786+
CHECK_RESULT(ReadStr(&field_name, "compact import field name"));
2787+
CHECK_RESULT(
2788+
ReadExternalKind(&kind, "compact import kind", "import"));
2789+
CHECK_RESULT(ReadImport(i++, module_name, field_name, kind));
2790+
}
27552791
}
2792+
} else {
2793+
// Normal non-compact import
2794+
// kind_u8 was not one of the special values above so rewind one
2795+
// byte so we can read it with ReadExternalKind
2796+
state_.offset--;
2797+
CHECK_RESULT(ReadExternalKind(&kind, "import kind", "import"));
2798+
CHECK_RESULT(ReadImport(i++, module_name, field_name, kind));
27562799
}
27572800
}
27582801

src/binary-writer.cc

Lines changed: 66 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,7 @@ class BinaryWriter {
435435
void WriteTagType(const Tag* tag);
436436
void WriteRelocSection(const RelocSection* reloc_section);
437437
void WriteLinkingSection();
438+
void WriteImport(const Import* import);
438439
template <typename T>
439440
void WriteNames(const std::vector<T*>& elems, NameSectionSubsection type);
440441
void WriteCodeMetadataSections();
@@ -1401,6 +1402,33 @@ void BinaryWriter::WriteNames(const std::vector<T*>& elems,
14011402
EndSubsection();
14021403
}
14031404

1405+
void BinaryWriter::WriteImport(const Import* import) {
1406+
switch (import->kind()) {
1407+
case ExternalKind::Func:
1408+
WriteU32Leb128(
1409+
stream_,
1410+
module_->GetFuncTypeIndex(cast<FuncImport>(import)->func.decl),
1411+
"import signature index");
1412+
break;
1413+
1414+
case ExternalKind::Table:
1415+
WriteTable(&cast<TableImport>(import)->table);
1416+
break;
1417+
1418+
case ExternalKind::Memory:
1419+
WriteMemory(&cast<MemoryImport>(import)->memory);
1420+
break;
1421+
1422+
case ExternalKind::Global:
1423+
WriteGlobalHeader(&cast<GlobalImport>(import)->global);
1424+
break;
1425+
1426+
case ExternalKind::Tag:
1427+
WriteTagType(&cast<TagImport>(import)->tag);
1428+
break;
1429+
}
1430+
}
1431+
14041432
Result BinaryWriter::WriteModule() {
14051433
stream_->WriteU32(WABT_BINARY_MAGIC, "WASM_BINARY_MAGIC");
14061434
stream_->WriteU32(WABT_BINARY_VERSION, "WASM_BINARY_VERSION");
@@ -1466,37 +1494,48 @@ Result BinaryWriter::WriteModule() {
14661494
BeginKnownSection(BinarySection::Import);
14671495
WriteU32Leb128(stream_, module_->imports.size(), "num imports");
14681496

1469-
for (size_t i = 0; i < module_->imports.size(); ++i) {
1497+
size_t i = 0;
1498+
while (i < module_->imports.size()) {
14701499
const Import* import = module_->imports[i];
14711500
WriteHeader("import header", i);
14721501
WriteStr(stream_, import->module_name, "import module name",
14731502
PrintChars::Yes);
1474-
WriteStr(stream_, import->field_name, "import field name",
1475-
PrintChars::Yes);
1476-
stream_->WriteU8Enum(import->kind(), "import kind");
1477-
switch (import->kind()) {
1478-
case ExternalKind::Func:
1479-
WriteU32Leb128(
1480-
stream_,
1481-
module_->GetFuncTypeIndex(cast<FuncImport>(import)->func.decl),
1482-
"import signature index");
1483-
break;
1484-
1485-
case ExternalKind::Table:
1486-
WriteTable(&cast<TableImport>(import)->table);
1487-
break;
1488-
1489-
case ExternalKind::Memory:
1490-
WriteMemory(&cast<MemoryImport>(import)->memory);
1491-
break;
1492-
1493-
case ExternalKind::Global:
1494-
WriteGlobalHeader(&cast<GlobalImport>(import)->global);
1495-
break;
1496-
1497-
case ExternalKind::Tag:
1498-
WriteTagType(&cast<TagImport>(import)->tag);
1499-
break;
1503+
bool compact = false;
1504+
if (options_.features.compact_imports_enabled()) {
1505+
// Write compact imports when they are available.
1506+
// Currently we only support grouping by module name (0x7F mode)
1507+
// and not the module name + kind grouping (0x7E mode).
1508+
size_t group_size = 1;
1509+
size_t j = i + 1;
1510+
while (j < module_->imports.size() &&
1511+
import->module_name == module_->imports[j]->module_name) {
1512+
group_size++;
1513+
j++;
1514+
}
1515+
// Use compact imports iff we have a continuous sequence of more than
1516+
// one import with the same module name.
1517+
if (group_size > 1) {
1518+
compact = true;
1519+
WriteStr(stream_, "", "empty field name", PrintChars::Yes);
1520+
stream_->WriteU8(0x7F, "compact import marker");
1521+
WriteU32Leb128(stream_, group_size, "import group size");
1522+
while (group_size--) {
1523+
WriteHeader("compact import header", i);
1524+
const Import* import = module_->imports[i];
1525+
WriteStr(stream_, import->field_name, "import field name",
1526+
PrintChars::Yes);
1527+
stream_->WriteU8Enum(import->kind(), "import kind");
1528+
WriteImport(import);
1529+
i++;
1530+
}
1531+
}
1532+
}
1533+
if (!compact) {
1534+
WriteStr(stream_, import->field_name, "import field name",
1535+
PrintChars::Yes);
1536+
stream_->WriteU8Enum(import->kind(), "import kind");
1537+
WriteImport(import);
1538+
i++;
15001539
}
15011540
}
15021541
EndSection();

src/tools/wasm2c.cc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,12 @@ static const char s_description[] =
5858
)";
5959

6060
static const std::string supported_features[] = {
61-
"multi-memory", "multi-value",
62-
"sign-extension", "saturating-float-to-int",
63-
"exceptions", "memory64",
64-
"extended-const", "simd",
65-
"threads", "tail-call",
66-
"custom-page-sizes"};
61+
"multi-memory", "multi-value",
62+
"sign-extension", "saturating-float-to-int",
63+
"exceptions", "memory64",
64+
"extended-const", "simd",
65+
"threads", "tail-call",
66+
"custom-page-sizes", "compact-imports"};
6767

6868
static bool IsFeatureSupported(const std::string& feature) {
6969
return std::find(std::begin(supported_features), std::end(supported_features),

src/wabt.post.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const FEATURES = Object.freeze({
3737
'multi_memory': false,
3838
'extended_const': false,
3939
'relaxed_simd': false,
40+
'compact_imports': false,
4041
});
4142

4243
/// If value is not undefined, return it. Otherwise return default_.

test/binary/compact-imports.txt

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
;;; TOOL: run-gen-wasm
2+
;;; ARGS: -v
3+
;;; ARGS1: --enable-compact-import
4+
;;; ARGS2: --enable-compact-import
5+
magic
6+
version
7+
section(TYPE) { count[1] function params[0] results[1] i32 }
8+
section(IMPORT) {
9+
count[6]
10+
str("mod") str("func") func_kind type[0]
11+
str("mod") str("") compact[0x7F]
12+
count[3]
13+
str("func2") func_kind type[0]
14+
str("func3") func_kind type[0]
15+
str("func4") func_kind type[0]
16+
str("mod") str("") compact[0x7E]
17+
func_kind
18+
count[2]
19+
str("func5") type[0]
20+
str("func6") type[0]
21+
}
22+
(;; STDERR ;;;
23+
BeginModule(version: 1)
24+
BeginTypeSection(5)
25+
OnTypeCount(1)
26+
OnFuncType(index: 0, params: [], results: [i32])
27+
EndTypeSection
28+
BeginImportSection(65)
29+
OnImportCount(6)
30+
OnImport(index: 0, kind: func, module: "mod", field: "func")
31+
OnImportFunc(import_index: 0, func_index: 0, sig_index: 0)
32+
OnImport(index: 1, kind: func, module: "mod", field: "func2")
33+
OnImportFunc(import_index: 1, func_index: 1, sig_index: 0)
34+
OnImport(index: 2, kind: func, module: "mod", field: "func3")
35+
OnImportFunc(import_index: 2, func_index: 2, sig_index: 0)
36+
OnImport(index: 3, kind: func, module: "mod", field: "func4")
37+
OnImportFunc(import_index: 3, func_index: 3, sig_index: 0)
38+
OnImport(index: 4, kind: func, module: "mod", field: "func5")
39+
OnImportFunc(import_index: 4, func_index: 4, sig_index: 0)
40+
OnImport(index: 5, kind: func, module: "mod", field: "func6")
41+
OnImportFunc(import_index: 5, func_index: 5, sig_index: 0)
42+
EndImportSection
43+
EndModule
44+
;;; STDERR ;;)
45+
(;; STDOUT ;;;
46+
(module
47+
(type (;0;) (func (result i32)))
48+
(import "mod" "func" (func (;0;) (type 0)))
49+
(import "mod" "func2" (func (;1;) (type 0)))
50+
(import "mod" "func3" (func (;2;) (type 0)))
51+
(import "mod" "func4" (func (;3;) (type 0)))
52+
(import "mod" "func5" (func (;4;) (type 0)))
53+
(import "mod" "func6" (func (;5;) (type 0))))
54+
;;; STDOUT ;;)

0 commit comments

Comments
 (0)