diff --git a/src/ir/linear-execution.h b/src/ir/linear-execution.h index 9e69405ff7c..452b7c39a2c 100644 --- a/src/ir/linear-execution.h +++ b/src/ir/linear-execution.h @@ -35,12 +35,15 @@ namespace wasm { template> struct LinearExecutionWalker : public PostWalker { + static constexpr bool IsConst = internal::GetIsConst::value; + template using C = MaybeConst; + LinearExecutionWalker() = default; // subclasses should implement this - void noteNonLinear(Expression* curr) { abort(); } + void noteNonLinear(C* curr) { abort(); } - static void doNoteNonLinear(SubType* self, Expression** currp) { + static void doNoteNonLinear(SubType* self, C** currp) { self->noteNonLinear(*currp); } @@ -77,8 +80,8 @@ struct LinearExecutionWalker : public PostWalker { // only do a simple postorder walk on the IR, no CFG is constructed, etc.). bool connectAdjacentBlocks = false; - static void scan(SubType* self, Expression** currp) { - Expression* curr = *currp; + static void scan(SubType* self, C** currp) { + C* curr = *currp; auto handleCall = [&](bool isReturn, bool refutesThrowEffect) { bool mayThrow = !self->getModule() || @@ -103,10 +106,10 @@ struct LinearExecutionWalker : public PostWalker { WASM_UNREACHABLE("bad id"); case Expression::Id::BlockId: { self->pushTask(SubType::doVisitBlock, currp); - if (curr->cast()->name.is()) { + if (curr->template cast()->name.is()) { self->pushTask(SubType::doNoteNonLinear, currp); } - auto& list = curr->cast()->list; + auto& list = curr->template cast()->list; for (int i = int(list.size()) - 1; i >= 0; i--) { self->pushTask(SubType::scan, &list[i]); } @@ -115,24 +118,24 @@ struct LinearExecutionWalker : public PostWalker { case Expression::Id::IfId: { self->pushTask(SubType::doVisitIf, currp); self->pushTask(SubType::doNoteNonLinear, currp); - self->maybePushTask(SubType::scan, &curr->cast()->ifFalse); + self->maybePushTask(SubType::scan, &curr->template cast()->ifFalse); self->pushTask(SubType::doNoteNonLinear, currp); - self->pushTask(SubType::scan, &curr->cast()->ifTrue); + self->pushTask(SubType::scan, &curr->template cast()->ifTrue); if (!self->connectAdjacentBlocks) { self->pushTask(SubType::doNoteNonLinear, currp); } - self->pushTask(SubType::scan, &curr->cast()->condition); + self->pushTask(SubType::scan, &curr->template cast()->condition); break; } case Expression::Id::LoopId: { self->pushTask(SubType::doVisitLoop, currp); - self->pushTask(SubType::scan, &curr->cast()->body); + self->pushTask(SubType::scan, &curr->template cast()->body); self->pushTask(SubType::doNoteNonLinear, currp); break; } case Expression::Id::BreakId: { self->pushTask(SubType::doVisitBreak, currp); - auto* br = curr->cast(); + auto* br = curr->template cast(); // If there is no condition then we note non-linearity as the code after // us is unreachable anyhow (we do the same for Switch, Return, etc.). // If there is a condition, then we note or do not note depending on @@ -147,18 +150,18 @@ struct LinearExecutionWalker : public PostWalker { case Expression::Id::SwitchId: { self->pushTask(SubType::doVisitSwitch, currp); self->pushTask(SubType::doNoteNonLinear, currp); - self->pushTask(SubType::scan, &curr->cast()->condition); - self->maybePushTask(SubType::scan, &curr->cast()->value); + self->pushTask(SubType::scan, &curr->template cast()->condition); + self->maybePushTask(SubType::scan, &curr->template cast()->value); break; } case Expression::Id::ReturnId: { self->pushTask(SubType::doVisitReturn, currp); self->pushTask(SubType::doNoteNonLinear, currp); - self->maybePushTask(SubType::scan, &curr->cast()->value); + self->maybePushTask(SubType::scan, &curr->template cast()->value); break; } case Expression::Id::CallId: { - auto* call = curr->cast(); + auto* call = curr->template cast(); bool refutesThrowEffect = false; if (self->getModule()) { @@ -174,7 +177,7 @@ struct LinearExecutionWalker : public PostWalker { break; } case Expression::Id::CallRefId: { - auto* callRef = curr->cast(); + auto* callRef = curr->template cast(); bool refutesThrowEffect = [&]() { if (!self->getModule()) { @@ -197,7 +200,7 @@ struct LinearExecutionWalker : public PostWalker { break; } case Expression::Id::CallIndirectId: { - auto* callIndirect = curr->cast(); + auto* callIndirect = curr->template cast(); bool refutesThrowEffect = false; if (self->getModule()) { @@ -213,24 +216,24 @@ struct LinearExecutionWalker : public PostWalker { case Expression::Id::TryId: { self->pushTask(SubType::doVisitTry, currp); self->pushTask(SubType::doNoteNonLinear, currp); - auto& list = curr->cast()->catchBodies; + auto& list = curr->template cast()->catchBodies; for (int i = int(list.size()) - 1; i >= 0; i--) { self->pushTask(SubType::scan, &list[i]); self->pushTask(SubType::doNoteNonLinear, currp); } - self->pushTask(SubType::scan, &curr->cast()->body); + self->pushTask(SubType::scan, &curr->template cast()->body); break; } case Expression::Id::TryTableId: { self->pushTask(SubType::doVisitTryTable, currp); self->pushTask(SubType::doNoteNonLinear, currp); - self->pushTask(SubType::scan, &curr->cast()->body); + self->pushTask(SubType::scan, &curr->template cast()->body); break; } case Expression::Id::ThrowId: { self->pushTask(SubType::doVisitThrow, currp); self->pushTask(SubType::doNoteNonLinear, currp); - auto& list = curr->cast()->operands; + auto& list = curr->template cast()->operands; for (int i = int(list.size()) - 1; i >= 0; i--) { self->pushTask(SubType::scan, &list[i]); } @@ -251,7 +254,7 @@ struct LinearExecutionWalker : public PostWalker { if (!self->connectAdjacentBlocks) { self->pushTask(SubType::doNoteNonLinear, currp); } - self->pushTask(SubType::scan, &curr->cast()->ref); + self->pushTask(SubType::scan, &curr->template cast()->ref); break; } default: { diff --git a/src/ir/module-utils.cpp b/src/ir/module-utils.cpp index b17d6c772b9..7c39abae765 100644 --- a/src/ir/module-utils.cpp +++ b/src/ir/module-utils.cpp @@ -405,88 +405,88 @@ struct TypeInfos { bool contains(HeapType type) { return info.contains(type); } }; -struct CodeScanner : PostWalker { +struct CodeScanner : PostWalker> { TypeInfos& info; - CodeScanner(Module& wasm, TypeInfos& info) : info(info) { setModule(&wasm); } + CodeScanner(const Module& wasm, TypeInfos& info) : info(info) { setModule(&wasm); } - void visitCallIndirect(CallIndirect* curr) { info.note(curr->heapType); } - void visitCallRef(CallRef* curr) { info.note(curr->target->type); } - void visitRefNull(RefNull* curr) { info.note(curr->type); } - void visitSelect(Select* curr) { + void visitCallIndirect(const CallIndirect* curr) { info.note(curr->heapType); } + void visitCallRef(const CallRef* curr) { info.note(curr->target->type); } + void visitRefNull(const RefNull* curr) { info.note(curr->type); } + void visitSelect(const Select* curr) { if (curr->type.isRef()) { // This select will be annotated in the binary, so note it. info.note(curr->type); } } - void visitStructNew(StructNew* curr) { info.note(curr->type); } - void visitArrayNew(ArrayNew* curr) { info.note(curr->type); } - void visitArrayNewData(ArrayNewData* curr) { info.note(curr->type); } - void visitArrayNewElem(ArrayNewElem* curr) { info.note(curr->type); } - void visitArrayNewFixed(ArrayNewFixed* curr) { info.note(curr->type); } - void visitArrayCopy(ArrayCopy* curr) { + void visitStructNew(const StructNew* curr) { info.note(curr->type); } + void visitArrayNew(const ArrayNew* curr) { info.note(curr->type); } + void visitArrayNewData(const ArrayNewData* curr) { info.note(curr->type); } + void visitArrayNewElem(const ArrayNewElem* curr) { info.note(curr->type); } + void visitArrayNewFixed(const ArrayNewFixed* curr) { info.note(curr->type); } + void visitArrayCopy(const ArrayCopy* curr) { info.note(curr->destRef->type); info.note(curr->srcRef->type); } - void visitArrayFill(ArrayFill* curr) { info.note(curr->ref->type); } - void visitArrayInitData(ArrayInitData* curr) { info.note(curr->ref->type); } - void visitArrayInitElem(ArrayInitElem* curr) { info.note(curr->ref->type); } - void visitRefCast(RefCast* curr) { info.note(curr->type); } - void visitRefTest(RefTest* curr) { info.note(curr->castType); } - void visitBrOn(BrOn* curr) { + void visitArrayFill(const ArrayFill* curr) { info.note(curr->ref->type); } + void visitArrayInitData(const ArrayInitData* curr) { info.note(curr->ref->type); } + void visitArrayInitElem(const ArrayInitElem* curr) { info.note(curr->ref->type); } + void visitRefCast(const RefCast* curr) { info.note(curr->type); } + void visitRefTest(const RefTest* curr) { info.note(curr->castType); } + void visitBrOn(const BrOn* curr) { if (curr->op == BrOnCast || curr->op == BrOnCastFail) { info.note(curr->ref->type); info.note(curr->castType); } } - void visitStructGet(StructGet* curr) { info.note(curr->ref->type); } - void visitStructSet(StructSet* curr) { info.note(curr->ref->type); } - void visitStructWait(StructWait* curr) { info.note(curr->ref->type); } - void visitStructNotify(StructNotify* curr) { info.note(curr->ref->type); } - void visitArrayGet(ArrayGet* curr) { info.note(curr->ref->type); } - void visitArraySet(ArraySet* curr) { info.note(curr->ref->type); } - void visitContBind(ContBind* curr) { + void visitStructGet(const StructGet* curr) { info.note(curr->ref->type); } + void visitStructSet(const StructSet* curr) { info.note(curr->ref->type); } + void visitStructWait(const StructWait* curr) { info.note(curr->ref->type); } + void visitStructNotify(const StructNotify* curr) { info.note(curr->ref->type); } + void visitArrayGet(const ArrayGet* curr) { info.note(curr->ref->type); } + void visitArraySet(const ArraySet* curr) { info.note(curr->ref->type); } + void visitContBind(const ContBind* curr) { info.note(curr->cont->type); info.note(curr->type); } - void visitContNew(ContNew* curr) { info.note(curr->type); } - void visitResume(Resume* curr) { + void visitContNew(const ContNew* curr) { info.note(curr->type); } + void visitResume(const Resume* curr) { info.note(curr->cont->type); info.note(curr->type); } - void visitResumeThrow(ResumeThrow* curr) { + void visitResumeThrow(const ResumeThrow* curr) { info.note(curr->cont->type); info.note(curr->type); } - void visitStackSwitch(StackSwitch* curr) { + void visitStackSwitch(const StackSwitch* curr) { info.note(curr->cont->type); info.note(curr->type); } - void visitBlock(Block* curr) { + void visitBlock(const Block* curr) { info.noteControlFlow(Signature(Type::none, curr->type)); } - void visitIf(If* curr) { + void visitIf(const If* curr) { info.noteControlFlow(Signature(Type::none, curr->type)); } - void visitLoop(Loop* curr) { + void visitLoop(const Loop* curr) { info.noteControlFlow(Signature(Type::none, curr->type)); } - void visitTry(Try* curr) { + void visitTry(const Try* curr) { info.noteControlFlow(Signature(Type::none, curr->type)); } - void visitTryTable(TryTable* curr) { + void visitTryTable(const TryTable* curr) { info.noteControlFlow(Signature(Type::none, curr->type)); } }; -void classifyTypeVisibility(Module& wasm, +void classifyTypeVisibility(const Module& wasm, InsertOrderedMap& types, WorldMode worldMode); } // anonymous namespace InsertOrderedMap -collectHeapTypeInfo(Module& wasm, +collectHeapTypeInfo(const Module& wasm, WorldMode worldMode, TypeInclusion inclusion, VisibilityHandling visibility) { @@ -604,7 +604,7 @@ collectHeapTypeInfo(Module& wasm, namespace { -void classifyTypeVisibility(Module& wasm, +void classifyTypeVisibility(const Module& wasm, InsertOrderedMap& types, WorldMode worldMode) { for (auto type : getPublicHeapTypes(wasm, worldMode)) { @@ -685,7 +685,7 @@ void setIndices(IndexedHeapTypes& indexedTypes) { } // anonymous namespace -std::vector collectHeapTypes(Module& wasm) { +std::vector collectHeapTypes(const Module& wasm) { auto info = collectHeapTypeInfo(wasm, WorldMode::Open); std::vector types; types.reserve(info.size()); @@ -695,7 +695,7 @@ std::vector collectHeapTypes(Module& wasm) { return types; } -std::vector getExposedPublicHeapTypes(Module& wasm) { +std::vector getExposedPublicHeapTypes(const Module& wasm) { // Look at the types of imports and exports to get an initial set of public // types. std::vector publicTypes; @@ -764,7 +764,7 @@ std::vector getExposedPublicHeapTypes(Module& wasm) { return publicTypes; } -std::vector getPublicHeapTypes(Module& wasm, WorldMode worldMode) { +std::vector getPublicHeapTypes(const Module& wasm, WorldMode worldMode) { auto directlyExposed = getExposedPublicHeapTypes(wasm); auto transitivelyExposed = getTransitivelyReachable( directlyExposed, /*includeSupertypes=*/true, /*includeRecGroups=*/true); @@ -778,7 +778,7 @@ std::vector getPublicHeapTypes(Module& wasm, WorldMode worldMode) { return publicTypes; } -std::vector getPrivateHeapTypes(Module& wasm, WorldMode worldMode) { +std::vector getPrivateHeapTypes(const Module& wasm, WorldMode worldMode) { auto info = collectHeapTypeInfo(wasm, worldMode, TypeInclusion::UsedIRTypes, @@ -793,7 +793,7 @@ std::vector getPrivateHeapTypes(Module& wasm, WorldMode worldMode) { return types; } -IndexedHeapTypes getOptimizedIndexedHeapTypes(Module& wasm) { +IndexedHeapTypes getOptimizedIndexedHeapTypes(const Module& wasm) { auto counts = collectHeapTypeInfo(wasm, WorldMode::Open, TypeInclusion::BinaryTypes); diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h index 860672beef8..a38f791a80c 100644 --- a/src/ir/module-utils.h +++ b/src/ir/module-utils.h @@ -76,7 +76,7 @@ void renameFunction(Module& wasm, Name oldName, Name newName); // Convenient iteration over imported/non-imported module elements -template inline void iterImportedMemories(Module& wasm, T visitor) { +template inline void iterImportedMemories(const Module& wasm, T visitor) { for (auto& import : wasm.memories) { if (import->imported()) { visitor(import.get()); @@ -84,7 +84,7 @@ template inline void iterImportedMemories(Module& wasm, T visitor) { } } -template inline void iterDefinedMemories(Module& wasm, T visitor) { +template inline void iterDefinedMemories(const Module& wasm, T visitor) { for (auto& import : wasm.memories) { if (!import->imported()) { visitor(import.get()); @@ -93,7 +93,7 @@ template inline void iterDefinedMemories(Module& wasm, T visitor) { } template -inline void iterMemorySegments(Module& wasm, Name memory, T visitor) { +inline void iterMemorySegments(const Module& wasm, Name memory, T visitor) { for (auto& segment : wasm.dataSegments) { if (segment->isActive() && segment->memory == memory) { visitor(segment.get()); @@ -102,7 +102,7 @@ inline void iterMemorySegments(Module& wasm, Name memory, T visitor) { } template -inline void iterActiveDataSegments(Module& wasm, T visitor) { +inline void iterActiveDataSegments(const Module& wasm, T visitor) { for (auto& segment : wasm.dataSegments) { if (segment->isActive()) { visitor(segment.get()); @@ -110,7 +110,7 @@ inline void iterActiveDataSegments(Module& wasm, T visitor) { } } -template inline void iterImportedTables(Module& wasm, T visitor) { +template inline void iterImportedTables(const Module& wasm, T visitor) { for (auto& import : wasm.tables) { if (import->imported()) { visitor(import.get()); @@ -118,7 +118,7 @@ template inline void iterImportedTables(Module& wasm, T visitor) { } } -template inline void iterDefinedTables(Module& wasm, T visitor) { +template inline void iterDefinedTables(const Module& wasm, T visitor) { for (auto& import : wasm.tables) { if (!import->imported()) { visitor(import.get()); @@ -127,7 +127,7 @@ template inline void iterDefinedTables(Module& wasm, T visitor) { } template -inline void iterTableSegments(Module& wasm, Name table, T visitor) { +inline void iterTableSegments(const Module& wasm, Name table, T visitor) { // Just a precaution so that we don't iterate over passive elem segments by // accident assert(table.is() && "Table name must not be null"); @@ -140,7 +140,7 @@ inline void iterTableSegments(Module& wasm, Name table, T visitor) { } template -inline void iterActiveElementSegments(Module& wasm, T visitor) { +inline void iterActiveElementSegments(const Module& wasm, T visitor) { for (auto& segment : wasm.elementSegments) { if (segment->isActive()) { visitor(segment.get()); @@ -148,7 +148,7 @@ inline void iterActiveElementSegments(Module& wasm, T visitor) { } } -template inline void iterImportedGlobals(Module& wasm, T visitor) { +template inline void iterImportedGlobals(const Module& wasm, T visitor) { for (auto& import : wasm.globals) { if (import->imported()) { visitor(import.get()); @@ -156,7 +156,7 @@ template inline void iterImportedGlobals(Module& wasm, T visitor) { } } -template inline void iterDefinedGlobals(Module& wasm, T visitor) { +template inline void iterDefinedGlobals(const Module& wasm, T visitor) { for (auto& import : wasm.globals) { if (!import->imported()) { visitor(import.get()); @@ -165,7 +165,7 @@ template inline void iterDefinedGlobals(Module& wasm, T visitor) { } template -inline void iterImportedFunctions(Module& wasm, T visitor) { +inline void iterImportedFunctions(const Module& wasm, T visitor) { for (auto& import : wasm.functions) { if (import->imported()) { visitor(import.get()); @@ -173,7 +173,7 @@ inline void iterImportedFunctions(Module& wasm, T visitor) { } } -template inline void iterDefinedFunctions(Module& wasm, T visitor) { +template inline void iterDefinedFunctions(const Module& wasm, T visitor) { for (auto& import : wasm.functions) { if (!import->imported()) { visitor(import.get()); @@ -181,7 +181,7 @@ template inline void iterDefinedFunctions(Module& wasm, T visitor) { } } -template inline void iterImportedTags(Module& wasm, T visitor) { +template inline void iterImportedTags(const Module& wasm, T visitor) { for (auto& import : wasm.tags) { if (import->imported()) { visitor(import.get()); @@ -189,7 +189,7 @@ template inline void iterImportedTags(Module& wasm, T visitor) { } } -template inline void iterDefinedTags(Module& wasm, T visitor) { +template inline void iterDefinedTags(const Module& wasm, T visitor) { for (auto& import : wasm.tags) { if (!import->imported()) { visitor(import.get()); @@ -197,7 +197,7 @@ template inline void iterDefinedTags(Module& wasm, T visitor) { } } -template inline void iterImports(Module& wasm, T visitor) { +template inline void iterImports(const Module& wasm, T visitor) { iterImportedMemories(wasm, visitor); iterImportedTables(wasm, visitor); iterImportedGlobals(wasm, visitor); @@ -207,7 +207,7 @@ template inline void iterImports(Module& wasm, T visitor) { // Iterates over all importable module items. The visitor provided should have // signature void(ExternalKind, Importable*). -template inline void iterImportable(Module& wasm, T visitor) { +template inline void iterImportable(const Module& wasm, T visitor) { for (auto& curr : wasm.functions) { if (curr->imported()) { visitor(ExternalKind::Function, curr.get()); @@ -237,7 +237,7 @@ template inline void iterImportable(Module& wasm, T visitor) { // Iterates over all module items. The visitor provided should have signature // void(ModuleItemKind, Named*). -template inline void iterModuleItems(Module& wasm, T visitor) { +template inline void iterModuleItems(const Module& wasm, T visitor) { for (auto& curr : wasm.functions) { visitor(ModuleItemKind::Function, curr.get()); } @@ -273,14 +273,14 @@ template class MapT = DefaultMap> struct ParallelFunctionAnalysis { - Module& wasm; + MaybeConst& wasm; using Map = MapT; Map map; using Func = std::function; - ParallelFunctionAnalysis(Module& wasm, Func work) : wasm(wasm) { + ParallelFunctionAnalysis(MaybeConst& wasm, Func work) : wasm(wasm) { // Fill in the map as we operate on it in parallel (each function to its own // entry). for (auto& func : wasm.functions) { @@ -303,30 +303,32 @@ struct ParallelFunctionAnalysis { } } - struct Mapper : public WalkerPass> { + struct Mapper : public WalkerPass>> { bool isFunctionParallel() override { return true; } - bool modifiesBinaryenIR() override { return Mut; } + bool modifiesBinaryenIR() override { return Mut == Mutable; } - Mapper(Module& module, Map& map, Func work) + Mapper(MaybeConst& module, Map& map, Func work) : module(module), map(map), work(work) {} std::unique_ptr create() override { return std::make_unique(module, map, work); } - void doWalkFunction(Function* curr) { - assert(map.contains(curr)); - work(curr, map[curr]); + void doWalkFunction(MaybeConst* curr) { + auto* mutableCurr = const_cast(curr); + assert(map.contains(mutableCurr)); + work(mutableCurr, map[mutableCurr]); } private: - Module& module; + MaybeConst& module; Map& map; Func work; }; PassRunner runner(&wasm); - Mapper(wasm, map, work).run(&runner, &wasm); + runner.add(std::make_unique(wasm, map, work)); + runner.run(); } }; @@ -381,7 +383,7 @@ template struct CallGraphPropertyAnalysis { Module* module; T& info; Func work; - } mapper(&wasm, info, work); + } mapper(const_cast(&wasm), info, work); mapper.walk(func->body); }); @@ -471,27 +473,27 @@ struct HeapTypeInfo { }; InsertOrderedMap collectHeapTypeInfo( - Module& wasm, + const Module& wasm, WorldMode worldMode, TypeInclusion inclusion = TypeInclusion::AllTypes, VisibilityHandling visibility = VisibilityHandling::NoVisibility); // Helper function for collecting all the non-basic heap types used in the // module, i.e. the types that would appear in the type section. -std::vector collectHeapTypes(Module& wasm); +std::vector collectHeapTypes(const Module& wasm); // Get the types directly made public by imported or exported module items. For // example, the types of imported or exported globals or functions, but not // other types reachable from those types. Includes abstract heap types. -std::vector getExposedPublicHeapTypes(Module& wasm); +std::vector getExposedPublicHeapTypes(const Module& wasm); // Collect all the defined heap types visible on the module boundary that cannot // be changed, e.g. the defined types from getExposedPublicHeapTypes and those // they reach. -std::vector getPublicHeapTypes(Module& wasm, WorldMode worldMode); +std::vector getPublicHeapTypes(const Module& wasm, WorldMode worldMode); // All the defined heap types that are not public. -std::vector getPrivateHeapTypes(Module& wasm, WorldMode worldMode); +std::vector getPrivateHeapTypes(const Module& wasm, WorldMode worldMode); struct IndexedHeapTypes { std::vector types; @@ -501,7 +503,7 @@ struct IndexedHeapTypes { // Similar to `collectHeapTypes`, but provides fast lookup of the index for each // type as well. Also orders the types to be valid and sorts the types by // frequency of use to minimize code size. -IndexedHeapTypes getOptimizedIndexedHeapTypes(Module& wasm); +IndexedHeapTypes getOptimizedIndexedHeapTypes(const Module& wasm); } // namespace wasm::ModuleUtils diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index 766409347e6..5a36a6d687e 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -1616,11 +1616,11 @@ struct TNHInfo { std::unordered_map inferences; }; -class TNHOracle : public ModuleUtils::ParallelFunctionAnalysis { +class TNHOracle : public ModuleUtils::ParallelFunctionAnalysis { const PassOptions& options; public: - using Parent = ModuleUtils::ParallelFunctionAnalysis; + using Parent = ModuleUtils::ParallelFunctionAnalysis; TNHOracle(Module& wasm, const PassOptions& options) : Parent(wasm, [this, &options](Function* func, TNHInfo& info) { diff --git a/src/ir/subtypes.h b/src/ir/subtypes.h index 912d61f878d..ea12a626832 100644 --- a/src/ir/subtypes.h +++ b/src/ir/subtypes.h @@ -34,7 +34,7 @@ struct SubTypes { } } - SubTypes(Module& wasm) : SubTypes(ModuleUtils::collectHeapTypes(wasm)) {} + SubTypes(const Module& wasm) : SubTypes(ModuleUtils::collectHeapTypes(wasm)) {} const std::vector& getImmediateSubTypes(HeapType type) const { // When we return an empty result, use a canonical constant empty vec to diff --git a/src/pass.h b/src/pass.h index 8bbf9612a90..40b91a025fb 100644 --- a/src/pass.h +++ b/src/pass.h @@ -290,10 +290,15 @@ struct PassRunner { MixedArena* allocator; std::vector> passes; PassOptions options; + bool isConstModule = false; PassRunner(Module* wasm) : wasm(wasm), allocator(&wasm->allocator) {} PassRunner(Module* wasm, PassOptions options) : wasm(wasm), allocator(&wasm->allocator), options(options) {} + + PassRunner(const Module* wasm) : wasm(const_cast(wasm)), allocator(&const_cast(wasm)->allocator), isConstModule(true) {} + PassRunner(const Module* wasm, PassOptions options) + : wasm(const_cast(wasm)), allocator(&const_cast(wasm)->allocator), options(options), isConstModule(true) {} // no copying, we control |passes| PassRunner(const PassRunner&) = delete; diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp index 672936f3444..9d01fd3802d 100644 --- a/src/passes/pass.cpp +++ b/src/passes/pass.cpp @@ -1012,6 +1012,9 @@ void PassRunner::clear() { passes.clear(); } void PassRunner::runPass(Pass* pass) { assert(!pass->isFunctionParallel()); + if (isConstModule) { + assert(!pass->modifiesBinaryenIR() && "Cannot run a mutating pass on a const module"); + } if (options.passesToSkip.contains(pass->name)) { return; @@ -1027,6 +1030,9 @@ void PassRunner::runPass(Pass* pass) { void PassRunner::runPassOnFunction(Pass* pass, Function* func) { assert(pass->isFunctionParallel()); + if (isConstModule) { + assert(!pass->modifiesBinaryenIR() && "Cannot run a mutating pass on a const module"); + } if (options.passesToSkip.contains(pass->name)) { return; diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h index 550a9306b0e..0134e6bada9 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -34,38 +34,48 @@ namespace wasm { +namespace internal { +template struct GetIsConst { static constexpr bool value = false; }; +template struct GetIsConst> { static constexpr bool value = T::IsConst; }; +} + +template +using MaybeConst = std::conditional_t; + // A generic visitor, defaulting to doing nothing on each visit -template struct Visitor { +template struct Visitor { // Capture the parameter in something we can access later. using ReturnType = ReturnType_; + static constexpr bool IsConst = IsConstVisitor; + template using C = MaybeConst; // Expression visitors #define DELEGATE(CLASS_TO_VISIT) \ - ReturnType visit##CLASS_TO_VISIT(CLASS_TO_VISIT* curr) { \ + ReturnType visit##CLASS_TO_VISIT(C* curr) { \ return ReturnType(); \ } #include "wasm-delegations.def" // Module-level visitors - ReturnType visitExport(Export* curr) { return ReturnType(); } - ReturnType visitGlobal(Global* curr) { return ReturnType(); } - ReturnType visitFunction(Function* curr) { return ReturnType(); } - ReturnType visitTable(Table* curr) { return ReturnType(); } - ReturnType visitElementSegment(ElementSegment* curr) { return ReturnType(); } - ReturnType visitMemory(Memory* curr) { return ReturnType(); } - ReturnType visitDataSegment(DataSegment* curr) { return ReturnType(); } - ReturnType visitTag(Tag* curr) { return ReturnType(); } - ReturnType visitModule(Module* curr) { return ReturnType(); } - - ReturnType visit(Expression* curr) { + ReturnType visitExport(C* curr) { return ReturnType(); } + ReturnType visitGlobal(C* curr) { return ReturnType(); } + ReturnType visitFunction(C* curr) { return ReturnType(); } + ReturnType visitTable(C* curr) { return ReturnType(); } + ReturnType visitElementSegment(C* curr) { return ReturnType(); } + ReturnType visitMemory(C* curr) { return ReturnType(); } + ReturnType visitDataSegment(C* curr) { return ReturnType(); } + ReturnType visitTag(C* curr) { return ReturnType(); } + ReturnType visitModule(C* curr) { return ReturnType(); } + + ReturnType visit(C* curr) { assert(curr); switch (curr->_id) { #define DELEGATE(CLASS_TO_VISIT) \ case Expression::Id::CLASS_TO_VISIT##Id: \ return static_cast(this)->visit##CLASS_TO_VISIT( \ - static_cast(curr)) + static_cast*>(curr)) #include "wasm-delegations.def" @@ -77,14 +87,15 @@ template struct Visitor { // A visitor which must be overridden for each visitor that is reached. -template -struct OverriddenVisitor : public Visitor { +template +struct OverriddenVisitor : public Visitor { + template using C = MaybeConst; // Expression visitors, which must be overridden #define DELEGATE(CLASS_TO_VISIT) \ - ReturnType visit##CLASS_TO_VISIT(CLASS_TO_VISIT* curr) { \ + ReturnType visit##CLASS_TO_VISIT(C* curr) { \ static_assert( \ &SubType::visit##CLASS_TO_VISIT != \ - &OverriddenVisitor::visit##CLASS_TO_VISIT, \ + &OverriddenVisitor::visit##CLASS_TO_VISIT, \ "Derived class must implement visit" #CLASS_TO_VISIT); \ WASM_UNREACHABLE("Derived class must implement visit" #CLASS_TO_VISIT); \ } @@ -95,14 +106,15 @@ struct OverriddenVisitor : public Visitor { // Visit with a single unified visitor, called on every node, instead of // separate visit* per node -template -struct UnifiedExpressionVisitor : public Visitor { +template +struct UnifiedExpressionVisitor : public Visitor { + template using C = MaybeConst; // called on each node - ReturnType visitExpression(Expression* curr) { return ReturnType(); } + ReturnType visitExpression(C* curr) { return ReturnType(); } // redirects #define DELEGATE(CLASS_TO_VISIT) \ - ReturnType visit##CLASS_TO_VISIT(CLASS_TO_VISIT* curr) { \ + ReturnType visit##CLASS_TO_VISIT(C* curr) { \ return static_cast(this)->visitExpression(curr); \ } @@ -118,12 +130,16 @@ struct UnifiedExpressionVisitor : public Visitor { // template struct Walker : public VisitorType { + static constexpr bool IsConst = internal::GetIsConst::value; + template using C = MaybeConst; + // Useful methods for visitor implementations // Replace the current node. You can call this in your visit*() methods. // Note that the visit*() for the result node is not called for you (i.e., // just one visit*() method is called by the traversal; if you replace a node, // and you want to process the output, you must do that explicitly). + template::type = 0> Expression* replaceCurrent(Expression* expression) { // Copy debug info, if present. if (currFunction) { @@ -133,33 +149,33 @@ struct Walker : public VisitorType { return *replacep = expression; } - Expression* getCurrent() { return *replacep; } + C* getCurrent() { return *replacep; } - Expression** getCurrentPointer() { return replacep; } + C** getCurrentPointer() { return replacep; } // Get the current module - Module* getModule() { return currModule; } + C* getModule() { return currModule; } // Get the current function - Function* getFunction() { return currFunction; } + C* getFunction() { return currFunction; } // Walk starting - void walkGlobal(Global* global) { + void walkGlobal(C* global) { walk(global->init); static_cast(this)->visitGlobal(global); } - void walkFunction(Function* func) { + void walkFunction(C* func) { setFunction(func); static_cast(this)->doWalkFunction(func); static_cast(this)->visitFunction(func); setFunction(nullptr); } - void walkTag(Tag* tag) { static_cast(this)->visitTag(tag); } + void walkTag(C* tag) { static_cast(this)->visitTag(tag); } - void walkFunctionInModule(Function* func, Module* module) { + void walkFunctionInModule(C* func, C* module) { setModule(module); setFunction(func); static_cast(this)->doWalkFunction(func); @@ -169,9 +185,9 @@ struct Walker : public VisitorType { } // override this to provide custom functionality - void doWalkFunction(Function* func) { walk(func->body); } + void doWalkFunction(C* func) { walk(func->body); } - void walkElementSegment(ElementSegment* segment) { + void walkElementSegment(C* segment) { if (segment->isActive()) { walk(segment->offset); } @@ -181,26 +197,26 @@ struct Walker : public VisitorType { static_cast(this)->visitElementSegment(segment); } - void walkTable(Table* table) { + void walkTable(C
* table) { if (table->init) { walk(table->init); } static_cast(this)->visitTable(table); } - void walkDataSegment(DataSegment* segment) { + void walkDataSegment(C* segment) { if (segment->isActive()) { walk(segment->offset); } static_cast(this)->visitDataSegment(segment); } - void walkMemory(Memory* memory) { + void walkMemory(C* memory) { // TODO: This method and walkTable should walk children too, or be renamed. static_cast(this)->visitMemory(memory); } - void walkModule(Module* module) { + void walkModule(C* module) { setModule(module); static_cast(this)->doWalkModule(module); static_cast(this)->visitModule(module); @@ -208,7 +224,7 @@ struct Walker : public VisitorType { } // override this to provide custom functionality - void doWalkModule(Module* module) { + void doWalkModule(C* module) { // Dispatch statically through the SubType. SubType* self = static_cast(this); for (auto& curr : module->exports) { @@ -250,7 +266,7 @@ struct Walker : public VisitorType { } // Walks module-level code, that is, code that is not in functions. - void walkModuleCode(Module* module) { + void walkModuleCode(C* module) { setModule(module); // Dispatch statically through the SubType. SubType* self = static_cast(this); @@ -284,20 +300,20 @@ struct Walker : public VisitorType { // nested. // Tasks receive the this pointer and a pointer to the pointer to operate on - using TaskFunc = void (*)(SubType*, Expression**); + using TaskFunc = void (*)(SubType*, C**); struct Task { TaskFunc func; - Expression** currp; + C** currp; Task() {} - Task(TaskFunc func, Expression** currp) : func(func), currp(currp) {} + Task(TaskFunc func, C** currp) : func(func), currp(currp) {} }; - void pushTask(TaskFunc func, Expression** currp) { + void pushTask(TaskFunc func, C** currp) { assert(*currp); stack.emplace_back(func, currp); } - void maybePushTask(TaskFunc func, Expression** currp) { + void maybePushTask(TaskFunc func, C** currp) { if (*currp) { stack.emplace_back(func, currp); } @@ -308,9 +324,10 @@ struct Walker : public VisitorType { return ret; } - void walk(Expression*& root) { + void walk(Expression* const& root) { assert(stack.size() == 0); - pushTask(SubType::scan, &root); + C** p = (C**)&root; + pushTask(SubType::scan, p); while (stack.size() > 0) { auto task = popTask(); replacep = task.currp; @@ -320,27 +337,27 @@ struct Walker : public VisitorType { } // subclasses implement this to define the proper order of execution - static void scan(SubType* self, Expression** currp) { abort(); } + static void scan(SubType* self, C** currp) { abort(); } // task hooks to call visitors #define DELEGATE(CLASS_TO_VISIT) \ - static void doVisit##CLASS_TO_VISIT(SubType* self, Expression** currp) { \ - self->visit##CLASS_TO_VISIT((*currp)->cast()); \ + static void doVisit##CLASS_TO_VISIT(SubType* self, C** currp) { \ + self->visit##CLASS_TO_VISIT((*currp)->template cast()); \ } #include "wasm-delegations.def" - void setModule(Module* module) { currModule = module; } + void setModule(C* module) { this->currModule = module; } - void setFunction(Function* func) { currFunction = func; } + void setFunction(C* func) { this->currFunction = func; } private: // the address of the current node, used to replace it - Expression** replacep = nullptr; - SmallVector stack; // stack of tasks - Function* currFunction = nullptr; // current function being processed - Module* currModule = nullptr; // current module being processed + C** replacep = nullptr; + SmallVector::Task, 10> stack; // stack of tasks + C* currFunction = nullptr; // current function being processed + C* currModule = nullptr; // current module being processed }; // Define which expression classes are leaves. We can handle them more @@ -369,9 +386,11 @@ template<> struct IsLeaf : std::true_type {}; template> struct PostWalker : public Walker { + static constexpr bool IsConst = internal::GetIsConst::value; + template using C = MaybeConst; - static void scan(SubType* self, Expression** currp) { - Expression* curr = *currp; + static void scan(SubType* self, C** currp) { + C* curr = *currp; #define DELEGATE_ID curr->_id @@ -397,16 +416,16 @@ struct PostWalker : public Walker { #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 11 #define DELEGATE_START(id) \ if (&SubType::visit##id != \ - &Visitor::visit##id || \ + &Visitor::visit##id || \ &SubType::doVisit##id != &Walker::doVisit##id) { \ self->pushTask(SubType::doVisit##id, currp); \ } \ - [[maybe_unused]] auto* cast = curr->cast(); + [[maybe_unused]] auto* cast = curr->template cast(); #else // constexpr #define DELEGATE_START(id) \ if constexpr (&SubType::visit##id != \ &Visitor::visit##id || \ + typename SubType::ReturnType, IsConst>::visit##id || \ &SubType::doVisit##id != \ &Walker::doVisit##id) { \ if constexpr (IsLeaf::value && \ @@ -416,16 +435,16 @@ struct PostWalker : public Walker { } \ self->pushTask(SubType::doVisit##id, currp); \ } \ - [[maybe_unused]] auto* cast = curr->cast(); + [[maybe_unused]] auto* cast = curr->template cast(); #endif // constexpr #define DELEGATE_GET_FIELD(id, field) cast->field #define DELEGATE_FIELD_CHILD(id, field) \ - self->pushTask(SubType::scan, &cast->field); + self->pushTask(SubType::scan, (C**)&cast->field); #define DELEGATE_FIELD_OPTIONAL_CHILD(id, field) \ - self->maybePushTask(SubType::scan, &cast->field); + self->maybePushTask(SubType::scan, (C**)&cast->field); #define DELEGATE_FIELD_INT(id, field) #define DELEGATE_FIELD_LITERAL(id, field) @@ -448,20 +467,23 @@ using ExpressionStack = SmallVector; template> struct ControlFlowWalker : public PostWalker { + static constexpr bool IsConst = internal::GetIsConst::value; + template using C = MaybeConst; + // contains blocks, loops, ifs, trys, and try_tables - ExpressionStack controlFlowStack; + SmallVector*, 10> controlFlowStack; // Uses the control flow stack to find the target of a break to a name - Expression* findBreakTarget(Name name) { + C* findBreakTarget(Name name) { assert(!controlFlowStack.empty()); Index i = controlFlowStack.size() - 1; while (true) { auto* curr = controlFlowStack[i]; - if (Block* block = curr->template dynCast()) { + if (C* block = curr->template dynCast()) { if (name == block->name) { return curr; } - } else if (Loop* loop = curr->template dynCast()) { + } else if (C* loop = curr->template dynCast()) { if (name == loop->name) { return curr; } @@ -477,17 +499,17 @@ struct ControlFlowWalker : public PostWalker { } } - static void doPreVisitControlFlow(SubType* self, Expression** currp) { + static void doPreVisitControlFlow(SubType* self, C** currp) { self->controlFlowStack.push_back(*currp); } - static void doPostVisitControlFlow(SubType* self, Expression** currp) { + static void doPostVisitControlFlow(SubType* self, C** currp) { // note that we might be popping something else, as we may have been // replaced self->controlFlowStack.pop_back(); } - static void scan(SubType* self, Expression** currp) { + static void scan(SubType* self, C** currp) { auto* curr = *currp; switch (curr->_id) { @@ -524,21 +546,24 @@ struct ControlFlowWalker : public PostWalker { template> struct ExpressionStackWalker : public PostWalker { + static constexpr bool IsConst = internal::GetIsConst::value; + template using C = MaybeConst; + ExpressionStackWalker() = default; - ExpressionStack expressionStack; + SmallVector*, 10> expressionStack; // Uses the control flow stack to find the target of a break to a name - Expression* findBreakTarget(Name name) { + C* findBreakTarget(Name name) { assert(!expressionStack.empty()); Index i = expressionStack.size() - 1; while (true) { auto* curr = expressionStack[i]; - if (Block* block = curr->template dynCast()) { + if (C* block = curr->template dynCast()) { if (name == block->name) { return curr; } - } else if (Loop* loop = curr->template dynCast()) { + } else if (C* loop = curr->template dynCast()) { if (name == loop->name) { return curr; } @@ -550,7 +575,7 @@ struct ExpressionStackWalker : public PostWalker { } } - Expression* getParent() { + C* getParent() { if (expressionStack.size() == 1) { return nullptr; } @@ -558,15 +583,15 @@ struct ExpressionStackWalker : public PostWalker { return expressionStack[expressionStack.size() - 2]; } - static void doPreVisit(SubType* self, Expression** currp) { + static void doPreVisit(SubType* self, C** currp) { self->expressionStack.push_back(*currp); } - static void doPostVisit(SubType* self, Expression** currp) { + static void doPostVisit(SubType* self, C** currp) { self->expressionStack.pop_back(); } - static void scan(SubType* self, Expression** currp) { + static void scan(SubType* self, C** currp) { self->pushTask(SubType::doPostVisit, currp); PostWalker::scan(self, currp); @@ -574,6 +599,7 @@ struct ExpressionStackWalker : public PostWalker { self->pushTask(SubType::doPreVisit, currp); } + template::type = 0> Expression* replaceCurrent(Expression* expression) { PostWalker::replaceCurrent(expression); // also update the stack