| /* |
| * Copyright 2024 WebAssembly Community Group participants |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // |
| // Lowers a module with a 64-bit table to one with a 32-bit table. |
| // |
| // This pass can be deleted once table64 is implemented in Wasm engines: |
| // https://github.com/WebAssembly/memory64/issues/51 |
| // |
| |
| #include "ir/bits.h" |
| #include "ir/import-utils.h" |
| #include "pass.h" |
| #include "wasm-builder.h" |
| #include "wasm.h" |
| |
| namespace wasm { |
| |
| static Name TABLE_BASE("__table_base"); |
| static Name TABLE_BASE32("__table_base32"); |
| |
| struct Table64Lowering : public WalkerPass<PostWalker<Table64Lowering>> { |
| |
| void wrapAddress64(Expression*& ptr, Name tableName) { |
| if (ptr->type == Type::unreachable) { |
| return; |
| } |
| auto& module = *getModule(); |
| auto* table = module.getTable(tableName); |
| if (table->is64()) { |
| assert(ptr->type == Type::i64); |
| ptr = Builder(module).makeUnary(UnaryOp::WrapInt64, ptr); |
| } |
| } |
| |
| void extendAddress64(Expression*& ptr, Name tableName) { |
| if (ptr->type == Type::unreachable) { |
| return; |
| } |
| auto& module = *getModule(); |
| auto* table = module.getTable(tableName); |
| if (table->is64()) { |
| assert(ptr->type == Type::i64); |
| ptr->type = Type::i32; |
| ptr = Builder(module).makeUnary(UnaryOp::ExtendUInt32, ptr); |
| } |
| } |
| |
| void visitTableSize(TableSize* curr) { |
| auto& module = *getModule(); |
| auto* table = module.getTable(curr->table); |
| if (table->is64()) { |
| auto* size = static_cast<Expression*>(curr); |
| extendAddress64(size, curr->table); |
| replaceCurrent(size); |
| } |
| } |
| |
| void visitTableGrow(TableGrow* curr) { |
| auto& module = *getModule(); |
| auto* table = module.getTable(curr->table); |
| if (table->is64()) { |
| wrapAddress64(curr->delta, curr->table); |
| auto* size = static_cast<Expression*>(curr); |
| extendAddress64(size, curr->table); |
| replaceCurrent(size); |
| } |
| } |
| |
| void visitTableFill(TableFill* curr) { |
| wrapAddress64(curr->dest, curr->table); |
| wrapAddress64(curr->size, curr->table); |
| } |
| |
| void visitTableCopy(TableCopy* curr) { |
| wrapAddress64(curr->dest, curr->destTable); |
| wrapAddress64(curr->source, curr->sourceTable); |
| wrapAddress64(curr->size, curr->destTable); |
| } |
| |
| void visitTableInit(TableInit* curr) { |
| wrapAddress64(curr->dest, curr->table); |
| } |
| |
| void visitCallIndirect(CallIndirect* curr) { |
| wrapAddress64(curr->target, curr->table); |
| } |
| |
| void visitElementSegment(ElementSegment* segment) { |
| auto& module = *getModule(); |
| |
| // Passive segments don't have any offset to update. |
| if (segment->table.isNull() || !module.getTable(segment->table)->is64()) { |
| return; |
| } |
| |
| if (auto* c = segment->offset->dynCast<Const>()) { |
| c->value = Literal(static_cast<uint32_t>(c->value.geti64())); |
| c->type = Type::i32; |
| } else if (auto* get = segment->offset->dynCast<GlobalGet>()) { |
| auto* g = module.getGlobal(get->name); |
| if (g->imported() && g->base == TABLE_BASE) { |
| ImportInfo info(module); |
| auto* memoryBase32 = info.getImportedGlobal(g->module, TABLE_BASE32); |
| if (!memoryBase32) { |
| Builder builder(module); |
| memoryBase32 = builder |
| .makeGlobal(TABLE_BASE32, |
| Type::i32, |
| builder.makeConst(int32_t(0)), |
| Builder::Immutable) |
| .release(); |
| memoryBase32->module = g->module; |
| memoryBase32->base = TABLE_BASE32; |
| module.addGlobal(memoryBase32); |
| } |
| // Use this alternative import when initializing the segment. |
| assert(memoryBase32); |
| get->type = Type::i32; |
| get->name = memoryBase32->name; |
| } |
| } else { |
| WASM_UNREACHABLE("unexpected elem offset"); |
| } |
| } |
| |
| void run(Module* module) override { |
| Super::run(module); |
| // Don't modify the tables themselves until after the traversal since we |
| // that would require tables to be the last thing that get visited, and |
| // we don't want to depend on that specific ordering. |
| for (auto& table : module->tables) { |
| if (table->is64()) { |
| table->indexType = Type::i32; |
| } |
| } |
| } |
| }; |
| |
| Pass* createTable64LoweringPass() { return new Table64Lowering(); } |
| |
| } // namespace wasm |