diff options
author | Alon Zakai <azakai@google.com> | 2021-10-04 12:43:00 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-04 12:43:00 -0700 |
commit | 1183a7ddef3f0597b4547da0af6b6d1a8393f290 (patch) | |
tree | 5222afdf3ef4e52732be2e09a7fca8f38dd4a50a /src | |
parent | 9d6067dfc0adfb45c0e0b650cd74c64d3748d92b (diff) | |
download | binaryen-1183a7ddef3f0597b4547da0af6b6d1a8393f290.tar.gz binaryen-1183a7ddef3f0597b4547da0af6b6d1a8393f290.tar.bz2 binaryen-1183a7ddef3f0597b4547da0af6b6d1a8393f290.zip |
Fix inlining name collision (#4206)
Diffstat (limited to 'src')
-rw-r--r-- | src/ir/branch-utils.h | 22 | ||||
-rw-r--r-- | src/passes/Inlining.cpp | 17 |
2 files changed, 39 insertions, 0 deletions
diff --git a/src/ir/branch-utils.h b/src/ir/branch-utils.h index f2fa43f18..57bad2fb9 100644 --- a/src/ir/branch-utils.h +++ b/src/ir/branch-utils.h @@ -238,6 +238,28 @@ inline NameSet getBranchTargets(Expression* ast) { return scanner.targets; } +// Check if an expression defines a particular name as a branch target anywhere +// inside it. +inline bool hasBranchTarget(Expression* ast, Name target) { + struct Scanner + : public PostWalker<Scanner, UnifiedExpressionVisitor<Scanner>> { + Name target; + bool has = false; + + void visitExpression(Expression* curr) { + operateOnScopeNameDefs(curr, [&](Name& name) { + if (name == target) { + has = true; + } + }); + } + }; + Scanner scanner; + scanner.target = target; + scanner.walk(ast); + return scanner.has; +} + // Get the name of the branch target that is defined in the expression, or an // empty name if there is none. inline Name getDefinedName(Expression* curr) { diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp index 7467d2559..0360974f0 100644 --- a/src/passes/Inlining.cpp +++ b/src/passes/Inlining.cpp @@ -299,6 +299,23 @@ doInlining(Module* module, Function* into, const InliningAction& action) { Builder builder(*module); auto* block = builder.makeBlock(); block->name = Name(std::string("__inlined_func$") + from->name.str); + // In the unlikely event that the function already has a branch target with + // this name, fix that up, as otherwise we can get unexpected capture of our + // branches, that is, we could end up with this: + // + // (block $X ;; a new block we add as the target of returns + // (from's contents + // (block $X ;; a block in from's contents with a colliding name + // (br $X ;; a new br we just added that replaces a return + // + // 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); }); + } if (call->isReturn) { if (retType.isConcrete()) { *action.callSite = builder.makeReturn(block); |