| /* | |
| * Copyright 2023 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 <unordered_map> | |
| #include "analysis/cfg.h" | |
| #include "cfg/cfg-traversal.h" | |
| #include "wasm-stack.h" | |
| namespace wasm::analysis { | |
| CFG CFG::fromFunction(Function* func, Module* wasm) { | |
| struct CFGBuilder : CFGWalker<CFGBuilder, | |
| UnifiedExpressionVisitor<CFGBuilder>, | |
| std::vector<Expression*>> { | |
| void visitExpression(Expression* curr) { | |
| if (currBasicBlock) { | |
| currBasicBlock->contents.push_back(curr); | |
| } | |
| } | |
| }; | |
| CFGBuilder builder; | |
| if (wasm) { | |
| builder.walkFunctionInModule(func, wasm); | |
| } else { | |
| builder.walkFunction(func); | |
| } | |
| size_t numBlocks = builder.basicBlocks.size(); | |
| CFG cfg; | |
| cfg.blocks = std::vector<BasicBlock>(numBlocks); | |
| // From here the addresses of the new basic blocks are stable. | |
| std::unordered_map<CFGBuilder::BasicBlock*, BasicBlock*> oldToNewBlocks; | |
| for (size_t i = 0; i < numBlocks; ++i) { | |
| oldToNewBlocks[builder.basicBlocks[i].get()] = &cfg.blocks[i]; | |
| } | |
| for (size_t i = 0; i < numBlocks; ++i) { | |
| auto& oldBlock = *builder.basicBlocks[i]; | |
| auto& newBlock = cfg.blocks[i]; | |
| newBlock.index = i; | |
| newBlock.insts = std::move(oldBlock.contents); | |
| newBlock.predecessors.reserve(oldBlock.in.size()); | |
| for (auto* oldPred : oldBlock.in) { | |
| newBlock.predecessors.push_back(oldToNewBlocks.at(oldPred)); | |
| } | |
| newBlock.successors.reserve(oldBlock.out.size()); | |
| for (auto* oldSucc : oldBlock.out) { | |
| newBlock.successors.push_back(oldToNewBlocks.at(oldSucc)); | |
| } | |
| } | |
| assert(!cfg.blocks.empty()); | |
| cfg.blocks[0].entry = true; | |
| if (builder.exit) { | |
| oldToNewBlocks.at(builder.exit)->exit = true; | |
| } | |
| // Move-construct a new CFG to get mandatory copy elision, preserving basic | |
| // block addresses through the return. | |
| return CFG(std::move(cfg)); | |
| } | |
| void CFG::print(std::ostream& os, Module* wasm) const { | |
| size_t start = 0; | |
| for (auto& block : *this) { | |
| if (&block != &*begin()) { | |
| os << '\n'; | |
| } | |
| block.print(os, wasm, start); | |
| start += block.size(); | |
| } | |
| } | |
| void BasicBlock::print(std::ostream& os, Module* wasm, size_t start) const { | |
| os << ";; preds: ["; | |
| for (const auto* pred : preds()) { | |
| if (pred != *preds().begin()) { | |
| os << ", "; | |
| } | |
| os << pred->index; | |
| } | |
| os << "], succs: ["; | |
| for (const auto* succ : succs()) { | |
| if (succ != *succs().begin()) { | |
| os << ", "; | |
| } | |
| os << succ->index; | |
| } | |
| os << "]\n"; | |
| if (isEntry()) { | |
| os << ";; entry\n"; | |
| } | |
| if (isExit()) { | |
| os << ";; exit\n"; | |
| } | |
| os << index << ":\n"; | |
| size_t instIndex = start; | |
| for (auto* inst : *this) { | |
| os << " " << instIndex++ << ": " << ShallowExpression{inst, wasm} << '\n'; | |
| } | |
| } | |
| CFGBlockIndexes::CFGBlockIndexes(const CFG& cfg) { | |
| for (auto& block : cfg) { | |
| for (auto* expr : block) { | |
| map[expr] = block.getIndex(); | |
| } | |
| } | |
| } | |
| } // namespace wasm::analysis |