From 746b3b7c3e429761fcd4afbab07e83352c4865bd Mon Sep 17 00:00:00 2001 From: dcode Date: Tue, 22 Nov 2022 16:08:12 +0100 Subject: [PATCH 1/2] Use proper flows when compiling switch statements --- src/compiler.ts | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 0ee4365ecb..2140ed090e 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -2825,15 +2825,16 @@ export class Compiler extends DiagnosticEmitter { // nest blocks in order let currentBlock = module.block(`case0|${context}`, breaks, TypeRef.None); - let commonCategorical = FlowFlags.AnyCategorical; - let commonConditional = 0; + let fallThroughFlow: Flow | null = null; + let commonBreakingFlow: Flow | null = null; for (let i = 0; i < numCases; ++i) { let case_ = cases[i]; let statements = case_.statements; let numStatements = statements.length; - // Each switch case initiates a new branch + // Can get here by matching the case or by fall-through let innerFlow = outerFlow.fork(); + if (fallThroughFlow) innerFlow.inheritBranch(fallThroughFlow); this.currentFlow = innerFlow; let breakLabel = `break|${context}`; innerFlow.breakLabel = breakLabel; @@ -2843,38 +2844,38 @@ export class Compiler extends DiagnosticEmitter { let stmts = new Array(1 + numStatements); stmts[0] = currentBlock; let count = 1; - let terminates = false; + let possiblyFallsThrough = true; for (let j = 0; j < numStatements; ++j) { let stmt = this.compileStatement(statements[j]); if (getExpressionId(stmt) != ExpressionId.Nop) { stmts[count++] = stmt; } if (innerFlow.isAny(FlowFlags.Terminates | FlowFlags.Breaks)) { - if (innerFlow.is(FlowFlags.Terminates)) terminates = true; + possiblyFallsThrough = false; break; } } stmts.length = count; - if (terminates || isLast || innerFlow.isAny(FlowFlags.Breaks | FlowFlags.ConditionallyBreaks)) { - commonCategorical &= innerFlow.flags; + fallThroughFlow = possiblyFallsThrough ? innerFlow : null; + let possiblyBreaks = innerFlow.isAny(FlowFlags.Breaks | FlowFlags.ConditionallyBreaks); + innerFlow.unset(FlowFlags.Breaks | FlowFlags.ConditionallyBreaks); // clear + if (possiblyBreaks || (isLast && possiblyFallsThrough)) { + if (commonBreakingFlow) commonBreakingFlow.inheritBranch(innerFlow); + else commonBreakingFlow = innerFlow; } - - commonConditional |= innerFlow.deriveConditionalFlags(); - - // Switch back to the parent flow - innerFlow.unset( - FlowFlags.Breaks | - FlowFlags.ConditionallyBreaks - ); this.currentFlow = outerFlow; currentBlock = module.block(nextLabel, stmts, TypeRef.None); // must be a labeled block } outerFlow.popBreakLabel(); - // If the switch has a default (guaranteed to handle any value), propagate common flags - if (defaultIndex >= 0) outerFlow.flags |= commonCategorical & ~FlowFlags.Breaks; - outerFlow.flags |= commonConditional & ~FlowFlags.ConditionallyBreaks; - // TODO: what about local states? + // If the switch has a default, we only get past through a breaking flow + if (defaultIndex >= 0) { + if (commonBreakingFlow) outerFlow.inherit(commonBreakingFlow); + else outerFlow.set(FlowFlags.Terminates); + // Otherwise either skipping or any breaking flow can get past + } else if (commonBreakingFlow) { + outerFlow.inheritBranch(commonBreakingFlow); + } return currentBlock; } From 66ff86224150efa2d2801b65c694d561c8136472 Mon Sep 17 00:00:00 2001 From: dcode Date: Tue, 22 Nov 2022 16:27:53 +0100 Subject: [PATCH 2/2] breaking flows are mutual --- src/compiler.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 2140ed090e..20aae02762 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -2826,7 +2826,7 @@ export class Compiler extends DiagnosticEmitter { // nest blocks in order let currentBlock = module.block(`case0|${context}`, breaks, TypeRef.None); let fallThroughFlow: Flow | null = null; - let commonBreakingFlow: Flow | null = null; + let mutualBreakingFlow: Flow | null = null; for (let i = 0; i < numCases; ++i) { let case_ = cases[i]; let statements = case_.statements; @@ -2860,21 +2860,21 @@ export class Compiler extends DiagnosticEmitter { let possiblyBreaks = innerFlow.isAny(FlowFlags.Breaks | FlowFlags.ConditionallyBreaks); innerFlow.unset(FlowFlags.Breaks | FlowFlags.ConditionallyBreaks); // clear if (possiblyBreaks || (isLast && possiblyFallsThrough)) { - if (commonBreakingFlow) commonBreakingFlow.inheritBranch(innerFlow); - else commonBreakingFlow = innerFlow; + if (mutualBreakingFlow) mutualBreakingFlow.inheritMutual(mutualBreakingFlow, innerFlow); + else mutualBreakingFlow = innerFlow; } this.currentFlow = outerFlow; currentBlock = module.block(nextLabel, stmts, TypeRef.None); // must be a labeled block } outerFlow.popBreakLabel(); - // If the switch has a default, we only get past through a breaking flow + // If the switch has a default, we only get past through any breaking flow if (defaultIndex >= 0) { - if (commonBreakingFlow) outerFlow.inherit(commonBreakingFlow); + if (mutualBreakingFlow) outerFlow.inherit(mutualBreakingFlow); else outerFlow.set(FlowFlags.Terminates); // Otherwise either skipping or any breaking flow can get past - } else if (commonBreakingFlow) { - outerFlow.inheritBranch(commonBreakingFlow); + } else if (mutualBreakingFlow) { + outerFlow.inheritBranch(mutualBreakingFlow); } return currentBlock; }