summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/LocalGraph.cpp35
-rw-r--r--test/lit/passes/precompute-gc.wast45
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)
+ )
)