Skip to content

Commit 2b60f8a

Browse files
authored
Add table64 lowering pass (#6595)
Changes to wasm-validator.cpp here are mostly for consistency between elem and data segment validation.
1 parent 2cc5e06 commit 2b60f8a

11 files changed

+274
-67
lines changed

src/passes/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ set(passes_SOURCES
105105
ReorderGlobals.cpp
106106
ReorderLocals.cpp
107107
ReReloop.cpp
108+
Table64Lowering.cpp
108109
TrapMode.cpp
109110
TypeGeneralizing.cpp
110111
TypeRefining.cpp

src/passes/Memory64Lowering.cpp

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,10 @@ struct Memory64Lowering : public WalkerPass<PostWalker<Memory64Lowering>> {
3838
return;
3939
}
4040
auto& module = *getModule();
41-
auto memory = module.getMemory(memoryName);
41+
auto* memory = module.getMemory(memoryName);
4242
if (memory->is64()) {
4343
assert(ptr->type == Type::i64);
44-
Builder builder(module);
45-
ptr = builder.makeUnary(UnaryOp::WrapInt64, ptr);
44+
ptr = Builder(module).makeUnary(UnaryOp::WrapInt64, ptr);
4645
}
4746
}
4847

@@ -51,12 +50,11 @@ struct Memory64Lowering : public WalkerPass<PostWalker<Memory64Lowering>> {
5150
return;
5251
}
5352
auto& module = *getModule();
54-
auto memory = module.getMemory(memoryName);
53+
auto* memory = module.getMemory(memoryName);
5554
if (memory->is64()) {
5655
assert(ptr->type == Type::i64);
5756
ptr->type = Type::i32;
58-
Builder builder(module);
59-
ptr = builder.makeUnary(UnaryOp::ExtendUInt32, ptr);
57+
ptr = Builder(module).makeUnary(UnaryOp::ExtendUInt32, ptr);
6058
}
6159
}
6260

@@ -66,9 +64,9 @@ struct Memory64Lowering : public WalkerPass<PostWalker<Memory64Lowering>> {
6664

6765
void visitMemorySize(MemorySize* curr) {
6866
auto& module = *getModule();
69-
auto memory = module.getMemory(curr->memory);
67+
auto* memory = module.getMemory(curr->memory);
7068
if (memory->is64()) {
71-
auto size = static_cast<Expression*>(curr);
69+
auto* size = static_cast<Expression*>(curr);
7270
extendAddress64(size, curr->memory);
7371
curr->type = Type::i32;
7472
replaceCurrent(size);
@@ -77,10 +75,10 @@ struct Memory64Lowering : public WalkerPass<PostWalker<Memory64Lowering>> {
7775

7876
void visitMemoryGrow(MemoryGrow* curr) {
7977
auto& module = *getModule();
80-
auto memory = module.getMemory(curr->memory);
78+
auto* memory = module.getMemory(curr->memory);
8179
if (memory->is64()) {
8280
wrapAddress64(curr->delta, curr->memory);
83-
auto size = static_cast<Expression*>(curr);
81+
auto* size = static_cast<Expression*>(curr);
8482
extendAddress64(size, curr->memory);
8583
curr->type = Type::i32;
8684
replaceCurrent(size);
@@ -129,34 +127,39 @@ struct Memory64Lowering : public WalkerPass<PostWalker<Memory64Lowering>> {
129127
}
130128

131129
void visitDataSegment(DataSegment* segment) {
132-
if (!segment->isPassive) {
133-
if (auto* c = segment->offset->dynCast<Const>()) {
134-
c->value = Literal(static_cast<uint32_t>(c->value.geti64()));
135-
c->type = Type::i32;
136-
} else if (auto* get = segment->offset->dynCast<GlobalGet>()) {
137-
auto& module = *getModule();
138-
auto* g = module.getGlobal(get->name);
139-
if (g->imported() && g->base == MEMORY_BASE) {
140-
ImportInfo info(module);
141-
auto* memoryBase32 = info.getImportedGlobal(g->module, MEMORY_BASE32);
142-
if (!memoryBase32) {
143-
Builder builder(module);
144-
memoryBase32 = builder
145-
.makeGlobal(MEMORY_BASE32,
146-
Type::i32,
147-
builder.makeConst(int32_t(0)),
148-
Builder::Immutable)
149-
.release();
150-
memoryBase32->module = g->module;
151-
memoryBase32->base = MEMORY_BASE32;
152-
module.addGlobal(memoryBase32);
153-
}
154-
// Use this alternative import when initializing the segment.
155-
assert(memoryBase32);
156-
get->type = Type::i32;
157-
get->name = memoryBase32->name;
130+
if (segment->isPassive) {
131+
// passive segments don't have any offset to adjust
132+
return;
133+
}
134+
135+
if (auto* c = segment->offset->dynCast<Const>()) {
136+
c->value = Literal(static_cast<uint32_t>(c->value.geti64()));
137+
c->type = Type::i32;
138+
} else if (auto* get = segment->offset->dynCast<GlobalGet>()) {
139+
auto& module = *getModule();
140+
auto* g = module.getGlobal(get->name);
141+
if (g->imported() && g->base == MEMORY_BASE) {
142+
ImportInfo info(module);
143+
auto* memoryBase32 = info.getImportedGlobal(g->module, MEMORY_BASE32);
144+
if (!memoryBase32) {
145+
Builder builder(module);
146+
memoryBase32 = builder
147+
.makeGlobal(MEMORY_BASE32,
148+
Type::i32,
149+
builder.makeConst(int32_t(0)),
150+
Builder::Immutable)
151+
.release();
152+
memoryBase32->module = g->module;
153+
memoryBase32->base = MEMORY_BASE32;
154+
module.addGlobal(memoryBase32);
158155
}
156+
// Use this alternative import when initializing the segment.
157+
assert(memoryBase32);
158+
get->type = Type::i32;
159+
get->name = memoryBase32->name;
159160
}
161+
} else {
162+
WASM_UNREACHABLE("unexpected elem offset");
160163
}
161164
}
162165

src/passes/Table64Lowering.cpp

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright 2024 WebAssembly Community Group participants
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
//
18+
// Lowers a module with a 64-bit table to one with a 32-bit table.
19+
//
20+
// This pass can be deleted once table64 is implemented in Wasm engines:
21+
// https://github.com/WebAssembly/memory64/issues/51
22+
//
23+
24+
#include "ir/bits.h"
25+
#include "ir/import-utils.h"
26+
#include "pass.h"
27+
#include "wasm-builder.h"
28+
#include "wasm.h"
29+
30+
namespace wasm {
31+
32+
static Name TABLE_BASE("__table_base");
33+
static Name TABLE_BASE32("__table_base32");
34+
35+
struct Table64Lowering : public WalkerPass<PostWalker<Table64Lowering>> {
36+
37+
void wrapAddress64(Expression*& ptr, Name tableName) {
38+
if (ptr->type == Type::unreachable) {
39+
return;
40+
}
41+
auto& module = *getModule();
42+
auto* table = module.getTable(tableName);
43+
if (table->is64()) {
44+
assert(ptr->type == Type::i64);
45+
ptr = Builder(module).makeUnary(UnaryOp::WrapInt64, ptr);
46+
}
47+
}
48+
49+
void extendAddress64(Expression*& ptr, Name tableName) {
50+
if (ptr->type == Type::unreachable) {
51+
return;
52+
}
53+
auto& module = *getModule();
54+
auto* table = module.getTable(tableName);
55+
if (table->is64()) {
56+
assert(ptr->type == Type::i64);
57+
ptr->type = Type::i32;
58+
ptr = Builder(module).makeUnary(UnaryOp::ExtendUInt32, ptr);
59+
}
60+
}
61+
62+
void visitTableSize(TableSize* curr) {
63+
auto& module = *getModule();
64+
auto* table = module.getTable(curr->table);
65+
if (table->is64()) {
66+
auto* size = static_cast<Expression*>(curr);
67+
extendAddress64(size, curr->table);
68+
replaceCurrent(size);
69+
}
70+
}
71+
72+
void visitTableGrow(TableGrow* curr) {
73+
auto& module = *getModule();
74+
auto* table = module.getTable(curr->table);
75+
if (table->is64()) {
76+
wrapAddress64(curr->delta, curr->table);
77+
auto* size = static_cast<Expression*>(curr);
78+
extendAddress64(size, curr->table);
79+
replaceCurrent(size);
80+
}
81+
}
82+
83+
void visitTableFill(TableFill* curr) {
84+
wrapAddress64(curr->dest, curr->table);
85+
wrapAddress64(curr->size, curr->table);
86+
}
87+
88+
void visitTableCopy(TableCopy* curr) {
89+
wrapAddress64(curr->dest, curr->destTable);
90+
wrapAddress64(curr->source, curr->sourceTable);
91+
wrapAddress64(curr->size, curr->destTable);
92+
}
93+
94+
void visitCallIndirect(CallIndirect* curr) {
95+
wrapAddress64(curr->target, curr->table);
96+
}
97+
98+
void visitTable(Table* table) {
99+
// This is visited last.
100+
if (table->is64()) {
101+
table->indexType = Type::i32;
102+
}
103+
}
104+
105+
void visitElementSegment(ElementSegment* segment) {
106+
if (segment->table.isNull()) {
107+
// Passive segments don't have any offset to update.
108+
return;
109+
}
110+
111+
if (auto* c = segment->offset->dynCast<Const>()) {
112+
c->value = Literal(static_cast<uint32_t>(c->value.geti64()));
113+
c->type = Type::i32;
114+
} else if (auto* get = segment->offset->dynCast<GlobalGet>()) {
115+
auto& module = *getModule();
116+
auto* g = module.getGlobal(get->name);
117+
if (g->imported() && g->base == TABLE_BASE) {
118+
ImportInfo info(module);
119+
auto* memoryBase32 = info.getImportedGlobal(g->module, TABLE_BASE32);
120+
if (!memoryBase32) {
121+
Builder builder(module);
122+
memoryBase32 = builder
123+
.makeGlobal(TABLE_BASE32,
124+
Type::i32,
125+
builder.makeConst(int32_t(0)),
126+
Builder::Immutable)
127+
.release();
128+
memoryBase32->module = g->module;
129+
memoryBase32->base = TABLE_BASE32;
130+
module.addGlobal(memoryBase32);
131+
}
132+
// Use this alternative import when initializing the segment.
133+
assert(memoryBase32);
134+
get->type = Type::i32;
135+
get->name = memoryBase32->name;
136+
}
137+
} else {
138+
WASM_UNREACHABLE("unexpected elem offset");
139+
}
140+
}
141+
};
142+
143+
Pass* createTable64LoweringPass() { return new Table64Lowering(); }
144+
145+
} // namespace wasm

src/passes/pass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,9 @@ void PassRegistry::registerPasses() {
251251
"lower loads and stores to a 64-bit memory to instead use a "
252252
"32-bit one",
253253
createMemory64LoweringPass);
254+
registerPass("table64-lowering",
255+
"lower 64-bit tables 32-bit ones",
256+
createTable64LoweringPass);
254257
registerPass("memory-packing",
255258
"packs memory into separate segments, skipping zeros",
256259
createMemoryPackingPass);

src/passes/passes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ Pass* createStripEHPass();
166166
Pass* createStubUnsupportedJSOpsPass();
167167
Pass* createSSAifyPass();
168168
Pass* createSSAifyNoMergePass();
169+
Pass* createTable64LoweringPass();
169170
Pass* createTranslateToExnrefPass();
170171
Pass* createTrapModeClamp();
171172
Pass* createTrapModeJS();

src/wasm/wasm-validator.cpp

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3754,25 +3754,14 @@ static void validateDataSegments(Module& module, ValidationInfo& info) {
37543754
"active segment must have a valid memory name")) {
37553755
continue;
37563756
}
3757-
if (memory->is64()) {
3758-
if (!info.shouldBeEqual(segment->offset->type,
3759-
Type(Type::i64),
3760-
segment->offset,
3761-
"segment offset should be i64")) {
3762-
continue;
3763-
}
3764-
} else {
3765-
if (!info.shouldBeEqual(segment->offset->type,
3766-
Type(Type::i32),
3767-
segment->offset,
3768-
"segment offset should be i32")) {
3769-
continue;
3770-
}
3771-
}
3757+
info.shouldBeEqual(segment->offset->type,
3758+
memory->indexType,
3759+
segment->offset,
3760+
"segment offset must match memory index type");
37723761
info.shouldBeTrue(
37733762
Properties::isValidConstantExpression(module, segment->offset),
37743763
segment->offset,
3775-
"memory segment offset should be constant");
3764+
"memory segment offset must be constant");
37763765
FunctionValidator(module, &info).validate(segment->offset);
37773766
}
37783767
}
@@ -3846,31 +3835,30 @@ static void validateTables(Module& module, ValidationInfo& info) {
38463835
"elem",
38473836
"Non-nullable reference types are not yet supported for tables");
38483837

3849-
if (segment->table.is()) {
3838+
bool isPassive = !segment->table.is();
3839+
if (isPassive) {
3840+
info.shouldBeTrue(
3841+
!segment->offset, "elem", "passive segment should not have an offset");
3842+
} else {
38503843
auto table = module.getTableOrNull(segment->table);
38513844
info.shouldBeTrue(table != nullptr,
38523845
"elem",
38533846
"element segment must have a valid table name");
3854-
info.shouldBeTrue(!!segment->offset,
3855-
"elem",
3856-
"table segment offset should have an offset");
3847+
info.shouldBeTrue(
3848+
!!segment->offset, "elem", "table segment offset must have an offset");
38573849
info.shouldBeEqual(segment->offset->type,
3858-
Type(Type::i32),
3850+
table->indexType,
38593851
segment->offset,
3860-
"element segment offset should be i32");
3852+
"element segment offset must match table index type");
38613853
info.shouldBeTrue(
38623854
Properties::isValidConstantExpression(module, segment->offset),
38633855
segment->offset,
3864-
"table segment offset should be constant");
3856+
"table segment offset must be constant");
38653857
info.shouldBeTrue(
38663858
Type::isSubType(segment->type, table->type),
38673859
"elem",
38683860
"element segment type must be a subtype of the table type");
38693861
validator.validate(segment->offset);
3870-
} else {
3871-
info.shouldBeTrue(!segment->offset,
3872-
"elem",
3873-
"non-table segment offset should have no offset");
38743862
}
38753863
for (auto* expr : segment->data) {
38763864
info.shouldBeTrue(Properties::isValidConstantExpression(module, expr),

src/wasm/wasm.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -851,8 +851,6 @@ void TableSize::finalize() {
851851
void TableGrow::finalize() {
852852
if (delta->type == Type::unreachable || value->type == Type::unreachable) {
853853
type = Type::unreachable;
854-
} else {
855-
type = Type::i32;
856854
}
857855
}
858856

test/lit/help/wasm-opt.test

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,8 @@
505505
;; CHECK-NEXT:
506506
;; CHECK-NEXT: --symbolmap (alias for print-function-map)
507507
;; CHECK-NEXT:
508+
;; CHECK-NEXT: --table64-lowering lower 64-bit tables 32-bit ones
509+
;; CHECK-NEXT:
508510
;; CHECK-NEXT: --translate-to-exnref translate old Phase 3 EH
509511
;; CHECK-NEXT: instructions to new ones with
510512
;; CHECK-NEXT: exnref

test/lit/help/wasm2js.test

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,8 @@
452452
;; CHECK-NEXT:
453453
;; CHECK-NEXT: --symbolmap (alias for print-function-map)
454454
;; CHECK-NEXT:
455+
;; CHECK-NEXT: --table64-lowering lower 64-bit tables 32-bit ones
456+
;; CHECK-NEXT:
455457
;; CHECK-NEXT: --translate-to-exnref translate old Phase 3 EH
456458
;; CHECK-NEXT: instructions to new ones with
457459
;; CHECK-NEXT: exnref

0 commit comments

Comments
 (0)