diff options
-rw-r--r-- | src/ir/LocalGraph.cpp | 35 | ||||
-rw-r--r-- | test/lit/passes/precompute-gc.wast | 45 |
2 files changed, 73 insertions, 7 deletions
diff --git a/src/ir/LocalGraph.cpp b/src/ir/LocalGraph.cpp index 9bdb6765f..7865bc404 100644 --- a/src/ir/LocalGraph.cpp +++ b/src/ir/LocalGraph.cpp @@ -106,6 +106,10 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { auto numLocals = func->getNumLocals(); std::vector<FlowBlock*> work; + // Track if we have unreachable code anywhere, as if we do that may inhibit + // certain optimizations below. + bool hasUnreachable = false; + // Convert input blocks (basicBlocks) into more efficient flow blocks to // improve memory access. std::vector<FlowBlock> flowBlocks; @@ -114,9 +118,19 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { // Init mapping between basicblocks and flowBlocks std::unordered_map<BasicBlock*, FlowBlock*> basicToFlowMap; for (Index i = 0; i < basicBlocks.size(); ++i) { - basicToFlowMap[basicBlocks[i].get()] = &flowBlocks[i]; + auto* block = basicBlocks[i].get(); + basicToFlowMap[block] = &flowBlocks[i]; + // Check for unreachable code. Note we ignore the entry block (index 0) as + // that is always reached when we are called. + if (i != 0 && block->in.empty()) { + hasUnreachable = true; + } } + // We note which local indexes have local.sets, as that can help us + // optimize later (if there are none at all). + std::vector<bool> hasSet(numLocals, false); + const size_t NULL_ITERATION = -1; FlowBlock* entryFlowBlock = nullptr; @@ -140,6 +154,7 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { flowBlock.lastSets.reserve(block->contents.lastSets.size()); for (auto set : block->contents.lastSets) { flowBlock.lastSets.emplace_back(set); + hasSet[set.first] = true; } } assert(entryFlowBlock != nullptr); @@ -185,6 +200,24 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { if (gets.empty()) { continue; } + if (!hasUnreachable && !hasSet[index]) { + // This local index has no sets, so we know all gets will end up + // reaching the entry block. Do that here as an optimization to avoid + // flowing through the (potentially very many) blocks in the function. + // + // Note that we must check for unreachable code in this function, as + // if there is any then we would not be precise: in that case, the + // gets may either have the entry value, or no value at all. It would + // be safe to mark the entry value in that case anyhow (as it only + // matters in unreachable code), but to keep the IR consistent and to + // avoid confusion when debugging, simply do not optimize if + // there is anything unreachable (which will not happen normally, as + // DCE should run before passes that use this utility). + for (auto* get : gets) { + getSetses[get].insert(nullptr); + } + continue; + } work.push_back(&block); // Note that we may need to revisit the later parts of this initial // block, if we are in a loop, so don't mark it as seen. diff --git a/test/lit/passes/precompute-gc.wast b/test/lit/passes/precompute-gc.wast index df755dade..b2e9593d5 100644 --- a/test/lit/passes/precompute-gc.wast +++ b/test/lit/passes/precompute-gc.wast @@ -229,7 +229,7 @@ (struct.get $struct 0 (local.get $x)) ) ) - ;; CHECK: (func $ref-comparisons (type $8) (param $x (ref null $struct)) (param $y (ref null $struct)) + ;; CHECK: (func $ref-comparisons (type $9) (param $x (ref null $struct)) (param $y (ref null $struct)) ;; CHECK-NEXT: (local $z (ref null $struct)) ;; CHECK-NEXT: (local $w (ref null $struct)) ;; CHECK-NEXT: (call $log @@ -407,7 +407,7 @@ (local.get $tempresult) ) - ;; CHECK: (func $propagate-different-params (type $9) (param $input1 (ref $empty)) (param $input2 (ref $empty)) (result i32) + ;; CHECK: (func $propagate-different-params (type $10) (param $input1 (ref $empty)) (param $input2 (ref $empty)) (result i32) ;; CHECK-NEXT: (local $tempresult i32) ;; CHECK-NEXT: (local.set $tempresult ;; CHECK-NEXT: (ref.eq @@ -723,7 +723,7 @@ ) ) - ;; CHECK: (func $helper (type $10) (param $0 i32) (result i32) + ;; CHECK: (func $helper (type $11) (param $0 i32) (result i32) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $helper (param i32) (result i32) @@ -801,14 +801,14 @@ ) ) - ;; CHECK: (func $receive-f64 (type $11) (param $0 f64) + ;; CHECK: (func $receive-f64 (type $12) (param $0 f64) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $receive-f64 (param f64) (unreachable) ) - ;; CHECK: (func $odd-cast-and-get-non-null (type $12) (param $temp (ref $func-return-i32)) + ;; CHECK: (func $odd-cast-and-get-non-null (type $13) (param $temp (ref $func-return-i32)) ;; CHECK-NEXT: (local.set $temp ;; CHECK-NEXT: (ref.cast (ref nofunc) ;; CHECK-NEXT: (ref.func $receive-f64) @@ -836,7 +836,7 @@ ) ) - ;; CHECK: (func $new_block_unreachable (type $13) (result anyref) + ;; CHECK: (func $new_block_unreachable (type $8) (result anyref) ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block @@ -1033,4 +1033,37 @@ ) ) ) + + ;; CHECK: (func $get-nonnullable-in-unreachable (type $8) (result anyref) + ;; CHECK-NEXT: (local $x (ref any)) + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + (func $get-nonnullable-in-unreachable (result anyref) + (local $x (ref any)) + ;; We cannot read a non-nullable local without setting it first, but it is ok + ;; to do so here because we are in unreachable code. We should also not error + ;; about this get seeming to read the default value from the function entry + ;; (because it does not, as the entry is not reachable from it). Nothing is + ;; expected to be optimized here. + + ;; This unreachable set is needed for the later get to validate. + (local.set $x + (unreachable) + ) + ;; This if is needed so we have an interesting enough CFG that a possible + ;; assertion can be hit about reading the default value from the entry in a + ;; later block. + (if + (i32.const 1) + (unreachable) + ) + (local.get $x) + ) ) |