summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/branch-utils.h6
-rw-r--r--src/passes/Inlining.cpp24
-rw-r--r--test/lit/passes/inlining_optimize-level=3.wast44
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)
+ )
+)