diff --git a/.prettierignore b/.prettierignore index 7b75e44..1ffc659 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,6 +5,6 @@ /transform/*.mjs /tests/ts/fixture /third_party -/tests/cpp/lit/**/*.json +/tests/cpp/lit/expectInstrument**/*.json /.cache /.github/CODEOWNERS diff --git a/instrumentation/BasicBlockWalker.cpp b/instrumentation/BasicBlockWalker.cpp index 8e90545..2f6fbea 100644 --- a/instrumentation/BasicBlockWalker.cpp +++ b/instrumentation/BasicBlockWalker.cpp @@ -1,5 +1,11 @@ #include "BasicBlockWalker.hpp" +#include #include +#include +#include +#include +#include +#include #include "ir/branch-utils.h" namespace wasmInstrumentation { @@ -27,30 +33,72 @@ void BasicBlockWalker::visitExpression(wasm::Expression *curr) noexcept { } } -void BasicBlockWalker::unlinkEmptyBlock() noexcept { - const auto lambda = [](std::unique_ptr &block) { - if (block->contents.exprs.empty() && block->out.size() == 1) { - const auto outBlock = block->out[0]; - outBlock->in.erase(std::find(outBlock->in.begin(), outBlock->in.end(), block.get())); - for (auto &inBlock : block->in) { - inBlock->out.erase(std::find(inBlock->out.begin(), inBlock->out.end(), block.get())); - inBlock->out.push_back(outBlock); - outBlock->in.push_back(inBlock); +static bool +isBasicBlockContainUnreachable(BasicBlockWalker::BasicBlock &block, + std::set unreachableBlocks) { + return (!block.contents.exprs.empty() && + std::any_of(block.contents.exprs.begin(), block.contents.exprs.end(), + [](wasm::Expression *expr) { + return expr->is(); + })) || + (!block.in.empty() && + std::all_of(block.in.begin(), block.in.end(), + [&unreachableBlocks](BasicBlockWalker::BasicBlock *inBlock) { + return unreachableBlocks.find(inBlock) != unreachableBlocks.end(); + })); +}; + +static void removeDuplicates(std::vector &list) { + std::sort(list.begin(), list.end()); + list.erase(std::unique(list.begin(), list.end()), list.end()); +} + +void BasicBlockWalker::cleanBlock() noexcept { + bool isModified = true; + std::set unreachableBlocks{}; + while (isModified) { + isModified = false; + for (auto &block : basicBlocks) { + if (isBasicBlockContainUnreachable(*block, unreachableBlocks)) { + isModified |= unreachableBlocks.insert(block.get()).second; } - block->in.clear(); - block->out.clear(); - return true; } - return false; - }; - basicBlocks.erase(std::remove_if(basicBlocks.begin(), basicBlocks.end(), lambda), - basicBlocks.end()); + } + std::set emptyBlocks{}; + for (auto &block : basicBlocks) { + if (block->contents.exprs.empty() && block->out.size() == 1) { + emptyBlocks.insert(block.get()); + } + } + + std::set targetCleanBlocks{}; + targetCleanBlocks.insert(unreachableBlocks.begin(), unreachableBlocks.end()); + targetCleanBlocks.insert(emptyBlocks.begin(), emptyBlocks.end()); + + for (auto &block : targetCleanBlocks) { + for (auto &outBlock : block->out) { + outBlock->in.erase(std::find(outBlock->in.begin(), outBlock->in.end(), block)); + outBlock->in.insert(outBlock->in.end(), block->in.begin(), block->in.end()); + removeDuplicates(outBlock->in); + } + for (auto &inBlock : block->in) { + inBlock->out.erase(std::find(inBlock->out.begin(), inBlock->out.end(), block)); + inBlock->out.insert(inBlock->out.end(), block->out.begin(), block->out.end()); + removeDuplicates(inBlock->out); + } + block->in.clear(); + block->out.clear(); + basicBlocks.erase(std::find_if(basicBlocks.begin(), basicBlocks.end(), + [&block](std::unique_ptr const &b) -> bool { + return b.get() == block; + })); + } } void BasicBlockWalker::doWalkFunction(wasm::Function *const func) noexcept { wasm::CFGWalker, BasicBlockInfo>::doWalkFunction(func); - unlinkEmptyBlock(); + cleanBlock(); // LCOV_EXCL_START if (basicBlocks.size() > UINT32_MAX) { std::cerr << "Error: BasicBlocks length exceeds UINT32_MAX\n"; diff --git a/instrumentation/BasicBlockWalker.hpp b/instrumentation/BasicBlockWalker.hpp index d79b122..db03685 100644 --- a/instrumentation/BasicBlockWalker.hpp +++ b/instrumentation/BasicBlockWalker.hpp @@ -167,7 +167,7 @@ class BasicBlockWalker final /// /// @brief remove empty block that do not belong to any branch /// - void unlinkEmptyBlock() noexcept; + void cleanBlock() noexcept; }; } // namespace wasmInstrumentation diff --git a/tests/cpp/lit/covInstrument/unreachable.wast b/tests/cpp/lit/covInstrument/unreachable.wast new file mode 100644 index 0000000..7c03ec4 --- /dev/null +++ b/tests/cpp/lit/covInstrument/unreachable.wast @@ -0,0 +1,49 @@ +(module + (type $0 (func (param i32 i32 i32 i32))) + (type $1 (func (param i32))) + (type $2 (func)) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (global $~lib/memory/__data_end i32 (i32.const 76)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 32844)) + (global $~lib/memory/__heap_base i32 (i32.const 32844)) + (memory $0 1) + (data $0 (i32.const 12) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\"\00\00\00a\00s\00s\00e\00m\00b\00l\00y\00/\00i\00n\00d\00e\00x\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00") + (table $0 1 1 funcref) + (elem $0 (i32.const 1)) + (export "main" (func $assembly/index/main)) + (export "memory" (memory $0)) + (func $assembly/index/f (param $a i32) + ;;@ assembly/index.ts:5:2 + (if + ;;@ + (i32.eqz + ;;@ assembly/index.ts:5:9 + (i32.ge_s + (local.get $a) + ;;@ assembly/index.ts:5:14 + (i32.const 10) + ) + ) + (then + ;;@ + (call $~lib/builtins/abort + (i32.const 0) + (i32.const 32) + (i32.const 5) + (i32.const 3) + ) + ;;@ + (unreachable) + ) + ) + ) + (func $assembly/index/main + ;;@ assembly/index.ts:2:2 + (call $assembly/index/f + ;;@ assembly/index.ts:2:4 + (i32.const 10) + ) + ;;@ assembly/index.ts:2:2 + ) + ;; custom section "sourceMappingURL", size 17 +) diff --git a/tests/cpp/lit/covInstrument/unreachable.wast.debug.json b/tests/cpp/lit/covInstrument/unreachable.wast.debug.json new file mode 100644 index 0000000..657a9a1 --- /dev/null +++ b/tests/cpp/lit/covInstrument/unreachable.wast.debug.json @@ -0,0 +1,27 @@ +{ + "debugFiles": ["assembly/index.ts"], + "debugInfos": { + "assembly/index/f": { + "branchInfo": [], + "index": 0, + "lineInfo": [ + [ + [0, 5, 2], + [0, 5, 9], + [0, 5, 14] + ], + [] + ] + }, + "assembly/index/main": { + "branchInfo": [], + "index": 1, + "lineInfo": [ + [ + [0, 2, 2], + [0, 2, 4] + ] + ] + } + } +} diff --git a/tests/cpp/lit/covInstrument/unreachable.wast.run.log b/tests/cpp/lit/covInstrument/unreachable.wast.run.log new file mode 100644 index 0000000..01f1b5d --- /dev/null +++ b/tests/cpp/lit/covInstrument/unreachable.wast.run.log @@ -0,0 +1,6 @@ +make directly call to function index=1 +make directly call to function index=0 +basic block entry trace to: function=0, basic block=0 +basic block entry trace to: function=0, basic block=1 +basic block entry trace to: function=1, basic block=0 +exit from function call index=0