diff --git a/src/compiler/iroptimizer.js b/src/compiler/iroptimizer.js index ae7d98e495..2693613425 100644 --- a/src/compiler/iroptimizer.js +++ b/src/compiler/iroptimizer.js @@ -559,9 +559,12 @@ class IROptimizer { break; case StackOpcode.CONTROL_WHILE: case StackOpcode.CONTROL_FOR: + modified = this.analyzeInputs(inputs, state) || modified; + modified = this.analyzeLoopedStack(inputs.do, state, stackBlock, true) || modified; + break; case StackOpcode.CONTROL_REPEAT: modified = this.analyzeInputs(inputs, state) || modified; - modified = this.analyzeLoopedStack(inputs.do, state, stackBlock) || modified; + modified = this.analyzeLoopedStack(inputs.do, state, stackBlock, false) || modified; break; case StackOpcode.CONTROL_IF_ELSE: { modified = this.analyzeInputs(inputs, state) || modified; @@ -642,20 +645,24 @@ class IROptimizer { * @param {IntermediateStack} stack * @param {TypeState} state * @param {IntermediateStackBlock} block + * @param {boolean} willReevaluateInputs * @returns {boolean} * @private */ - analyzeLoopedStack (stack, state, block) { + analyzeLoopedStack (stack, state, block, willReevaluateInputs) { + let modified = false; + if (block.yields && !this.ignoreYields) { - let modified = state.clear(); + modified = state.clear(); + if (willReevaluateInputs) { + modified = this.analyzeInputs(block.inputs, state) || modified; + } block.entryState = state.clone(); block.exitState = state.clone(); - modified = this.analyzeInputs(block.inputs, state) || modified; return this.analyzeStack(stack, state) || modified; } let iterations = 0; - let modified = false; let keepLooping; do { // If we are stuck in an apparent infinite loop, give up and assume the worst. @@ -672,7 +679,10 @@ class IROptimizer { const newState = state.clone(); modified = this.analyzeStack(stack, newState) || modified; modified = (keepLooping = state.or(newState)) || modified; - modified = this.analyzeInputs(block.inputs, state) || modified; + + if (willReevaluateInputs) { + modified = this.analyzeInputs(block.inputs, state) || modified; + } } while (keepLooping); block.entryState = state.clone(); return modified; diff --git a/test/fixtures/execute/tw-repeat-analysis-doesnt-reanalyze.sb3 b/test/fixtures/execute/tw-repeat-analysis-doesnt-reanalyze.sb3 new file mode 100644 index 0000000000..12d51fb79b Binary files /dev/null and b/test/fixtures/execute/tw-repeat-analysis-doesnt-reanalyze.sb3 differ diff --git a/test/snapshot/__snapshots__/tw-repeat-analysis-doesnt-reanalyze.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-repeat-analysis-doesnt-reanalyze.sb3.tw-snapshot new file mode 100644 index 0000000000..020097c50f --- /dev/null +++ b/test/snapshot/__snapshots__/tw-repeat-analysis-doesnt-reanalyze.sb3.tw-snapshot @@ -0,0 +1,28 @@ +// TW Snapshot +// Input SHA-256: 38075839898d1ba3d2556f350ff18438a205c5b6b3cb809d07d3f738bd37dad9 + +// Sprite1 script +(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); +const b0 = runtime.getOpcodeFunction("looks_say"); +const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; +return function* genXYZ () { +yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, "d", null); +thread.procedures["Wa"](); +if ((("" + b1.value).toLowerCase() === "bwah".toLowerCase())) { +yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "l", null); +} +yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "k", null); +retire(); return; +}; }) + +// Sprite1 Wa +(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); +const b0 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; +const b1 = stage.variables["6h7bpiz0`;;x^G1B3(%["]; +return function funXYZ_a () { +b0.value = b1.value.length; +for (var a0 = b0.value; a0 > 0; a0--) { +b0.value = "bwah"; +} +return ""; +}; }) diff --git a/test/snapshot/__snapshots__/warp-timer/tw-repeat-analysis-doesnt-reanalyze.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-repeat-analysis-doesnt-reanalyze.sb3.tw-snapshot new file mode 100644 index 0000000000..35a0f59d7b --- /dev/null +++ b/test/snapshot/__snapshots__/warp-timer/tw-repeat-analysis-doesnt-reanalyze.sb3.tw-snapshot @@ -0,0 +1,29 @@ +// TW Snapshot +// Input SHA-256: 38075839898d1ba3d2556f350ff18438a205c5b6b3cb809d07d3f738bd37dad9 + +// Sprite1 script +(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); +const b0 = runtime.getOpcodeFunction("looks_say"); +const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; +return function* genXYZ () { +yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, "d", null); +yield* thread.procedures["Wa"](); +if ((("" + b1.value).toLowerCase() === "bwah".toLowerCase())) { +yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "l", null); +} +yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "k", null); +retire(); return; +}; }) + +// Sprite1 Wa +(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); +const b0 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; +const b1 = stage.variables["6h7bpiz0`;;x^G1B3(%["]; +return function* genXYZ_a () { +b0.value = b1.value.length; +for (var a0 = b0.value; a0 > 0; a0--) { +b0.value = "bwah"; +if (isStuck()) yield; +} +return ""; +}; })