diff --git a/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java b/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java index c57a126f7..9ffdd31de 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java @@ -15,11 +15,15 @@ */ package org.teavm.javascript; +import java.util.*; +import org.teavm.common.Graph; import org.teavm.javascript.ast.AsyncMethodNode; import org.teavm.javascript.ast.AsyncMethodPart; import org.teavm.javascript.ast.RegularMethodNode; +import org.teavm.model.Instruction; import org.teavm.model.Program; -import org.teavm.model.util.AsyncProgramSplitter; +import org.teavm.model.Variable; +import org.teavm.model.util.*; /** * @@ -52,6 +56,9 @@ public class Optimizer { } public void optimize(AsyncMethodNode method, AsyncProgramSplitter splitter) { + LivenessAnalyzer liveness = new LivenessAnalyzer(); + liveness.analyze(splitter.getOriginalProgram()); + boolean[] preservedVars = new boolean[method.getVariables().size()]; int[][] readFrequencies = new int[splitter.size()][]; for (int i = 0; i < splitter.size(); ++i) { @@ -59,16 +66,19 @@ public class Optimizer { stats.analyze(splitter.getProgram(i)); readFrequencies[i] = stats.reads; for (int j = 0; j < stats.writes.length; ++j) { - if (stats.readUninitialized[j] || stats.writes[j] != 1 && stats.reads[j] > 0) { + if (stats.writes[j] != 1 && stats.reads[j] > 0) { preservedVars[j] = true; } } } + for (int i = 0; i < splitter.size(); ++i) { + boolean[] partPreservedVars = preservedVars.clone(); AsyncMethodPart part = method.getBody().get(i); BreakEliminator breakEliminator = new BreakEliminator(); breakEliminator.eliminate(part.getStatement()); - OptimizingVisitor optimizer = new OptimizingVisitor(preservedVars, readFrequencies[i]); + findEscapingLiveVars(liveness, splitter, i, partPreservedVars); + OptimizingVisitor optimizer = new OptimizingVisitor(partPreservedVars, readFrequencies[i]); part.getStatement().acceptVisitor(optimizer); part.setStatement(optimizer.resultStmt); } @@ -86,4 +96,45 @@ public class Optimizer { method.getVariables().set(i, i); } } + + private void findEscapingLiveVars(LivenessAnalyzer liveness, AsyncProgramSplitter splitter, int partIndex, + boolean[] output) { + Program originalProgram = splitter.getOriginalProgram(); + Graph cfg = ProgramUtils.buildControlFlowGraph(originalProgram); + int[] successors = splitter.getBlockSuccessors(partIndex); + int[] splitPoints = splitter.getSplitPoints(partIndex); + + for (int i = 0; i < originalProgram.basicBlockCount(); ++i) { + if (successors[i] < 0) { + continue; + } + + // Determine live-out vars + BitSet liveVars = new BitSet(); + for (int succ : cfg.outgoingEdges(i)) { + liveVars.or(liveness.liveIn(succ)); + } + + // Remove from live set all variables that are defined in these blocks + DefinitionExtractor defExtractor = new DefinitionExtractor(); + UsageExtractor useExtractor = new UsageExtractor(); + List instructions = originalProgram.basicBlockAt(i).getInstructions(); + int splitPoint = splitPoints[i]; + for (int j = instructions.size() - 1; j >= splitPoint; --j) { + instructions.get(j).acceptVisitor(useExtractor); + instructions.get(j).acceptVisitor(defExtractor); + for (Variable var : useExtractor.getUsedVariables()) { + liveVars.set(var.getIndex()); + } + for (Variable var : defExtractor.getDefinedVariables()) { + liveVars.clear(var.getIndex()); + } + } + + // Add live variables to output + for (int j = liveVars.nextSetBit(0); j >= 0; j = liveVars.nextSetBit(j + 1)) { + output[j] = true; + } + } + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ReadWriteStatsBuilder.java b/teavm-core/src/main/java/org/teavm/javascript/ReadWriteStatsBuilder.java index 9b02c1e99..17d7666fb 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ReadWriteStatsBuilder.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ReadWriteStatsBuilder.java @@ -31,7 +31,6 @@ import org.teavm.model.util.UsageExtractor; class ReadWriteStatsBuilder { public int[] reads; public int[] writes; - public boolean[] readUninitialized; private ReadWriteStatsBuilder() { } @@ -39,7 +38,6 @@ class ReadWriteStatsBuilder { public ReadWriteStatsBuilder(int variableCount) { reads = new int[variableCount]; writes = new int[variableCount]; - readUninitialized = new boolean[variableCount]; } public ReadWriteStatsBuilder copy() { @@ -67,9 +65,6 @@ class ReadWriteStatsBuilder { } for (Variable var : useExtractor.getUsedVariables()) { reads[var.getIndex()]++; - if (writes[var.getIndex()] == 0) { - readUninitialized[var.getIndex()] = true; - } } } for (Phi phi : block.getPhis()) { diff --git a/teavm-core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java b/teavm-core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java index e3b3ccde0..2c49e180e 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java +++ b/teavm-core/src/main/java/org/teavm/model/util/AsyncProgramSplitter.java @@ -31,6 +31,7 @@ public class AsyncProgramSplitter { private Map partMap = new HashMap<>(); private ClassReaderSource classSource; private Set asyncMethods = new HashSet<>(); + private Program program; public AsyncProgramSplitter(ClassReaderSource classSource, Set asyncMethods) { this.classSource = classSource; @@ -38,12 +39,15 @@ public class AsyncProgramSplitter { } public void split(Program program) { + this.program = program; parts.clear(); Program initialProgram = createStubCopy(program); Part initialPart = new Part(); initialPart.program = initialProgram; initialPart.blockSuccessors = new int[program.basicBlockCount()]; Arrays.fill(initialPart.blockSuccessors, -1); + initialPart.splitPoints = new int[program.basicBlockCount()]; + Arrays.fill(initialPart.splitPoints, -1); parts.add(initialPart); partMap.put(0L, 0); Step initialStep = new Step(); @@ -87,6 +91,8 @@ public class AsyncProgramSplitter { } last = i; + step.targetPart.splitPoints[step.source] = i; + // If this instruction already separates program, end with current block and refer to the // existing part long key = ((long)step.source << 32) | i; @@ -103,6 +109,8 @@ public class AsyncProgramSplitter { parts.add(part); part.blockSuccessors = new int[program.basicBlockCount() + 1]; Arrays.fill(part.blockSuccessors, -1); + part.splitPoints = new int[program.basicBlockCount() + 1]; + Arrays.fill(part.splitPoints, -1); // Mark current instruction as a separator and remember which part is in charge. partMap.put(key, partId); @@ -192,6 +200,10 @@ public class AsyncProgramSplitter { return parts.size(); } + public Program getOriginalProgram() { + return program; + } + public Program getProgram(int index) { return parts.get(index).program; } @@ -201,9 +213,18 @@ public class AsyncProgramSplitter { return Arrays.copyOf(result, result.length); } + public int getBlockSuccessor(int index, int blockIndex) { + return parts.get(index).blockSuccessors[blockIndex]; + } + + public int[] getSplitPoints(int index) { + return parts.get(index).splitPoints.clone(); + } + private static class Part { Program program; int[] blockSuccessors; + int[] splitPoints; } private static class Step { diff --git a/teavm-core/src/main/java/org/teavm/model/util/DefinitionExtractor.java b/teavm-core/src/main/java/org/teavm/model/util/DefinitionExtractor.java index 7cca20ed0..fec90ad2e 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/DefinitionExtractor.java +++ b/teavm-core/src/main/java/org/teavm/model/util/DefinitionExtractor.java @@ -205,11 +205,11 @@ public class DefinitionExtractor implements InstructionVisitor { @Override public void visit(MonitorEnterInstruction insn) { - + definedVariables = new Variable[0]; } @Override public void visit(MonitorExitInstruction insn) { - + definedVariables = new Variable[0]; } } diff --git a/teavm-samples/teavm-samples-async/pom.xml b/teavm-samples/teavm-samples-async/pom.xml index 99a38de06..5805851ee 100644 --- a/teavm-samples/teavm-samples-async/pom.xml +++ b/teavm-samples/teavm-samples-async/pom.xml @@ -65,7 +65,7 @@ ${project.build.directory}/generated/js/teavm org.teavm.samples.async.AsyncProgram SEPARATE - true + false true true true