diff options
-rw-r--r-- | src/ir/branch-utils.h | 6 | ||||
-rw-r--r-- | src/passes/Inlining.cpp | 24 | ||||
-rw-r--r-- | test/lit/passes/inlining_optimize-level=3.wast | 44 |
3 files changed, 69 insertions, 5 deletions
diff --git a/src/ir/branch-utils.h b/src/ir/branch-utils.h index d433a421e..3527f1b36 100644 --- a/src/ir/branch-utils.h +++ b/src/ir/branch-utils.h @@ -327,6 +327,12 @@ struct BranchAccumulator auto selfBranches = getUniqueTargets(curr); branches.insert(selfBranches.begin(), selfBranches.end()); } + + static NameSet get(Expression* tree) { + BranchAccumulator accumulator; + accumulator.walk(tree); + return accumulator.branches; + } }; // A helper structure for the common case of post-walking some IR while querying diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp index c542d1018..41083567c 100644 --- a/src/passes/Inlining.cpp +++ b/src/passes/Inlining.cpp @@ -338,11 +338,25 @@ static Expression* doInlining(Module* module, // // Here the br wants to go to the very outermost block, to represent a // return from the inlined function's code, but it ends up captured by an - // internal block. - if (BranchUtils::hasBranchTarget(from->body, block->name)) { - auto existingNames = BranchUtils::getBranchTargets(from->body); - block->name = Names::getValidName( - block->name, [&](Name test) { return !existingNames.count(test); }); + // internal block. We also need to be careful of the call's children: + // + // (block $X ;; a new block we add as the target of returns + // (local.set $param + // (call's first parameter + // (br $X) ;; nested br in call's first parameter + // ) + // ) + // + // (In this case we could use a second block and define the named block $X + // after the call's parameters, but that adds work for an extremely rare + // situation.) + if (BranchUtils::hasBranchTarget(from->body, block->name) || + BranchUtils::BranchSeeker::has(call, block->name)) { + auto fromNames = BranchUtils::getBranchTargets(from->body); + auto callNames = BranchUtils::BranchAccumulator::get(call); + block->name = Names::getValidName(block->name, [&](Name test) { + return !fromNames.count(test) && !callNames.count(test); + }); } if (call->isReturn) { if (retType.isConcrete()) { diff --git a/test/lit/passes/inlining_optimize-level=3.wast b/test/lit/passes/inlining_optimize-level=3.wast index 416cf7c90..cba2f46fc 100644 --- a/test/lit/passes/inlining_optimize-level=3.wast +++ b/test/lit/passes/inlining_optimize-level=3.wast @@ -451,3 +451,47 @@ (call $bar) ) ) + +;; Similar to the above, but now the name collision happens due to a break in +;; one of the call's params. We must emit a different, non-colliding name. +(module + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (func $1 + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (block $__inlined_func$0_0 + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (block $__inlined_func$0_0_0 (result i32) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (br_if $__inlined_func$0_0 + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $1 + (block $__inlined_func$0_0 + (drop + (call $0_0 + (block (result i32) + (br_if $__inlined_func$0_0 + (i32.const 10) + ) + (i32.const 0) + ) + ) + ) + ) + ) + (func $0_0 (param $0 i32) (result i32) + (unreachable) + ) +) |