/* * Copyright 2022 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. */ #include "module-utils.h" #include "ir/intrinsics.h" #include "ir/manipulation.h" #include "ir/properties.h" #include "support/insert_ordered.h" #include "support/topological_sort.h" namespace wasm::ModuleUtils { // Copies a function into a module. If newName is provided it is used as the // name of the function (otherwise the original name is copied). Function* copyFunction(Function* func, Module& out, Name newName) { auto ret = std::make_unique(); ret->name = newName.is() ? newName : func->name; ret->type = func->type; ret->vars = func->vars; ret->localNames = func->localNames; ret->localIndices = func->localIndices; ret->debugLocations = func->debugLocations; ret->body = ExpressionManipulator::copy(func->body, out); ret->module = func->module; ret->base = func->base; // TODO: copy Stack IR assert(!func->stackIR); return out.addFunction(std::move(ret)); } Global* copyGlobal(Global* global, Module& out) { auto* ret = new Global(); ret->name = global->name; ret->type = global->type; ret->mutable_ = global->mutable_; ret->module = global->module; ret->base = global->base; if (global->imported()) { ret->init = nullptr; } else { ret->init = ExpressionManipulator::copy(global->init, out); } out.addGlobal(ret); return ret; } Tag* copyTag(Tag* tag, Module& out) { auto* ret = new Tag(); ret->name = tag->name; ret->sig = tag->sig; ret->module = tag->module; ret->base = tag->base; out.addTag(ret); return ret; } ElementSegment* copyElementSegment(const ElementSegment* segment, Module& out) { auto copy = [&](std::unique_ptr&& ret) { ret->name = segment->name; ret->hasExplicitName = segment->hasExplicitName; ret->type = segment->type; ret->data.reserve(segment->data.size()); for (auto* item : segment->data) { ret->data.push_back(ExpressionManipulator::copy(item, out)); } return out.addElementSegment(std::move(ret)); }; if (segment->table.isNull()) { return copy(std::make_unique()); } else { auto offset = ExpressionManipulator::copy(segment->offset, out); return copy(std::make_unique(segment->table, offset)); } } Table* copyTable(const Table* table, Module& out) { auto ret = std::make_unique(); ret->name = table->name; ret->hasExplicitName = table->hasExplicitName; ret->type = table->type; ret->module = table->module; ret->base = table->base; ret->initial = table->initial; ret->max = table->max; return out.addTable(std::move(ret)); } Memory* copyMemory(const Memory* memory, Module& out) { auto ret = Builder::makeMemory(memory->name); ret->hasExplicitName = memory->hasExplicitName; ret->initial = memory->initial; ret->max = memory->max; ret->shared = memory->shared; ret->indexType = memory->indexType; ret->module = memory->module; ret->base = memory->base; return out.addMemory(std::move(ret)); } DataSegment* copyDataSegment(const DataSegment* segment, Module& out) { auto ret = Builder::makeDataSegment(); ret->name = segment->name; ret->hasExplicitName = segment->hasExplicitName; ret->memory = segment->memory; ret->isPassive = segment->isPassive; if (!segment->isPassive) { auto offset = ExpressionManipulator::copy(segment->offset, out); ret->offset = offset; } ret->data = segment->data; return out.addDataSegment(std::move(ret)); } // Copies named toplevel module items (things of kind ModuleItemKind). See // copyModule() for something that also copies exports, the start function, etc. void copyModuleItems(const Module& in, Module& out) { for (auto& curr : in.functions) { copyFunction(curr.get(), out); } for (auto& curr : in.globals) { copyGlobal(curr.get(), out); } for (auto& curr : in.tags) { copyTag(curr.get(), out); } for (auto& curr : in.elementSegments) { copyElementSegment(curr.get(), out); } for (auto& curr : in.tables) { copyTable(curr.get(), out); } for (auto& curr : in.memories) { copyMemory(curr.get(), out); } for (auto& curr : in.dataSegments) { copyDataSegment(curr.get(), out); } } void copyModule(const Module& in, Module& out) { // we use names throughout, not raw pointers, so simple copying is fine // for everything *but* expressions for (auto& curr : in.exports) { out.addExport(std::make_unique(*curr)); } copyModuleItems(in, out); out.start = in.start; out.customSections = in.customSections; out.debugInfoFileNames = in.debugInfoFileNames; out.features = in.features; out.typeNames = in.typeNames; } void clearModule(Module& wasm) { wasm.~Module(); new (&wasm) Module; } // Renaming // Rename functions along with all their uses. // Note that for this to work the functions themselves don't necessarily need // to exist. For example, it is possible to remove a given function and then // call this to redirect all of its uses. template void renameFunctions(Module& wasm, T& map) { // Update the function itself. for (auto& [oldName, newName] : map) { if (Function* func = wasm.getFunctionOrNull(oldName)) { assert(!wasm.getFunctionOrNull(newName) || func->name == newName); func->name = newName; } } wasm.updateMaps(); // Update all references to it. struct Updater : public WalkerPass> { bool isFunctionParallel() override { return true; } T& map; void maybeUpdate(Name& name) { if (auto iter = map.find(name); iter != map.end()) { name = iter->second; } } Updater(T& map) : map(map) {} std::unique_ptr create() override { return std::make_unique(map); } void visitCall(Call* curr) { maybeUpdate(curr->target); } void visitRefFunc(RefFunc* curr) { maybeUpdate(curr->func); } }; Updater updater(map); updater.maybeUpdate(wasm.start); PassRunner runner(&wasm); updater.run(&runner, &wasm); updater.runOnModuleCode(&runner, &wasm); } void renameFunction(Module& wasm, Name oldName, Name newName) { std::map map; map[oldName] = newName; renameFunctions(wasm, map); } namespace { // Helper for collecting HeapTypes and their frequencies. struct Counts : public InsertOrderedMap { void note(HeapType type) { if (!type.isBasic()) { (*this)[type]++; } } void note(Type type) { for (HeapType ht : type.getHeapTypeChildren()) { note(ht); } } // Ensure a type is included without increasing its count. void include(HeapType type) { if (!type.isBasic()) { (*this)[type]; } } void include(Type type) { for (HeapType ht : type.getHeapTypeChildren()) { include(ht); } } }; struct CodeScanner : PostWalker> { Counts& counts; CodeScanner(Module& wasm, Counts& counts) : counts(counts) { setModule(&wasm); } void visitExpression(Expression* curr) { if (auto* call = curr->dynCast()) { counts.note(call->heapType); } else if (auto* call = curr->dynCast()) { counts.note(call->target->type); } else if (curr->is()) { counts.note(curr->type); } else if (curr->is