diff --git a/core/src/main/java/org/teavm/ast/optimization/Optimizer.java b/core/src/main/java/org/teavm/ast/optimization/Optimizer.java index 6e05819fd..2a04ae84c 100644 --- a/core/src/main/java/org/teavm/ast/optimization/Optimizer.java +++ b/core/src/main/java/org/teavm/ast/optimization/Optimizer.java @@ -27,7 +27,7 @@ import org.teavm.model.Program; import org.teavm.model.Variable; import org.teavm.model.util.AsyncProgramSplitter; import org.teavm.model.util.DefinitionExtractor; -import org.teavm.model.util.LivenessAnalyzer; +import org.teavm.model.util.NonSsaLivenessAnalyzer; import org.teavm.model.util.ProgramUtils; import org.teavm.model.util.UsageExtractor; @@ -67,7 +67,7 @@ public class Optimizer { } public void optimize(AsyncMethodNode method, AsyncProgramSplitter splitter, boolean friendlyToDebugger) { - LivenessAnalyzer liveness = new LivenessAnalyzer(); + NonSsaLivenessAnalyzer liveness = new NonSsaLivenessAnalyzer(); liveness.analyze(splitter.getOriginalProgram(), method.getReference().getDescriptor()); Graph cfg = ProgramUtils.buildControlFlowGraph(splitter.getOriginalProgram()); @@ -114,7 +114,7 @@ public class Optimizer { } } - private void findEscapingLiveVars(LivenessAnalyzer liveness, Graph cfg, AsyncProgramSplitter splitter, + private void findEscapingLiveVars(NonSsaLivenessAnalyzer liveness, Graph cfg, AsyncProgramSplitter splitter, int partIndex, boolean[] output) { Program originalProgram = splitter.getOriginalProgram(); Program program = splitter.getProgram(partIndex); diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java index ab81d00a2..f5debb032 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java @@ -112,6 +112,11 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { this.minifying = context.isMinifying(); this.naming = context.getNaming(); this.debugEmitter = context.getDebugEmitter(); + if (!minifying) { + usedVariableNames.add("$tmp"); + usedVariableNames.add("$ptr"); + usedVariableNames.add("$thread"); + } } public boolean isLongLibraryUsed() { diff --git a/core/src/main/java/org/teavm/model/util/LivenessAnalyzer.java b/core/src/main/java/org/teavm/model/util/LivenessAnalyzer.java index 1ea186277..ded292794 100644 --- a/core/src/main/java/org/teavm/model/util/LivenessAnalyzer.java +++ b/core/src/main/java/org/teavm/model/util/LivenessAnalyzer.java @@ -15,10 +15,7 @@ */ package org.teavm.model.util; -import com.carrotsearch.hppc.IntHashSet; -import com.carrotsearch.hppc.IntSet; import com.carrotsearch.hppc.IntStack; -import java.util.Arrays; import java.util.BitSet; import org.teavm.common.DominatorTree; import org.teavm.common.Graph; @@ -54,81 +51,85 @@ public class LivenessAnalyzer { public void analyze(Program program, int parameterCount) { Graph cfg = ProgramUtils.buildControlFlowGraph(program); DominatorTree dominatorTree = GraphUtils.buildDominatorTree(cfg); + BitSet[] visited = new BitSet[program.basicBlockCount()]; liveVars = new BitSet[program.basicBlockCount()]; liveOutVars = new BitSet[program.basicBlockCount()]; for (int i = 0; i < liveVars.length; ++i) { - liveVars[i] = new BitSet(program.basicBlockCount()); - liveOutVars[i] = new BitSet(program.basicBlockCount()); + visited[i] = new BitSet(program.variableCount()); + liveVars[i] = new BitSet(program.variableCount()); + liveOutVars[i] = new BitSet(program.variableCount()); } UsageExtractor usageExtractor = new UsageExtractor(); DefinitionExtractor defExtractor = new DefinitionExtractor(); IntStack stack = new IntStack(); - IntSet[] definitionsBuilder = new IntSet[program.variableCount()]; + int[] definitions = new int[program.variableCount()]; for (int i = 0; i < program.basicBlockCount(); ++i) { BasicBlock block = program.basicBlockAt(i); - if (block.getExceptionVariable() != null) { - addDefinition(definitionsBuilder, block.getExceptionVariable().getIndex(), i); - } + BitSet usedVariables = new BitSet(program.variableCount()); - for (Instruction insn : block) { - insn.acceptVisitor(usageExtractor); - for (Variable var : usageExtractor.getUsedVariables()) { - stack.push(i); - stack.push(var.getIndex()); - } + for (Instruction insn = block.getLastInstruction(); insn != null; insn = insn.getPrevious()) { insn.acceptVisitor(defExtractor); for (Variable var : defExtractor.getDefinedVariables()) { - addDefinition(definitionsBuilder, var.getIndex(), i); + definitions[var.getIndex()] = i; + usedVariables.clear(var.getIndex()); + } + insn.acceptVisitor(usageExtractor); + for (Variable var : usageExtractor.getUsedVariables()) { + usedVariables.set(var.getIndex()); } } for (Phi phi : block.getPhis()) { - addDefinition(definitionsBuilder, phi.getReceiver().getIndex(), i); + definitions[phi.getReceiver().getIndex()] = i; + usedVariables.clear(phi.getReceiver().getIndex()); for (Incoming incoming : phi.getIncomings()) { stack.push(incoming.getSource().getIndex()); stack.push(incoming.getValue().getIndex()); } } - } - int[][] definitions = new int[program.variableCount()][]; - for (int i = 0; i < definitions.length; ++i) { - IntSet definitionsByVar = definitionsBuilder[i]; - if (definitionsByVar == null) { - definitions[i] = new int[0]; - } else { - definitions[i] = definitionsByVar.toArray(); - Arrays.sort(definitions[i]); + if (block.getExceptionVariable() != null) { + definitions[block.getExceptionVariable().getIndex()] = i; + usedVariables.clear(block.getExceptionVariable().getIndex()); + } + + if (i == 0) { + for (int v = 0; v < parameterCount; ++v) { + definitions[v] = 0; + usedVariables.clear(v); + } + } + + int[] predecessors = cfg.incomingEdges(i); + for (int v = usedVariables.nextSetBit(0); v >= 0; v = usedVariables.nextSetBit(v + 1)) { + liveVars[i].set(v); + for (int pred : predecessors) { + stack.push(pred); + stack.push(v); + } } } - worklist: while (!stack.isEmpty()) { + while (!stack.isEmpty()) { int variable = stack.pop(); int block = stack.pop(); - BitSet blockLiveVars = liveVars[block]; - if (blockLiveVars.get(variable)) { + BitSet blockVisited = visited[block]; + if (blockVisited.get(variable)) { continue; } + blockVisited.set(variable); - boolean hasDominatingDefinition = variable < parameterCount; - for (int definedAt : definitions[variable]) { - if (definedAt == block) { - continue worklist; - } - if (!hasDominatingDefinition && dominatorTree.dominates(definedAt, block)) { - hasDominatingDefinition = true; - } - } - if (!hasDominatingDefinition) { + if (definitions[variable] == block || !dominatorTree.dominates(definitions[variable], block)) { continue; } - - blockLiveVars.set(variable, true); + liveVars[block].set(variable, true); for (int pred : cfg.incomingEdges(block)) { - stack.push(pred); - stack.push(variable); + if (!visited[pred].get(variable)) { + stack.push(pred); + stack.push(variable); + } } } @@ -144,13 +145,4 @@ public class LivenessAnalyzer { } } } - - private static void addDefinition(IntSet[] definitions, int v, int block) { - IntSet definitionsByVar = definitions[v]; - if (definitionsByVar == null) { - definitionsByVar = new IntHashSet(); - definitions[v] = definitionsByVar; - } - definitionsByVar.add(block); - } } diff --git a/core/src/main/java/org/teavm/model/util/NonSsaLivenessAnalyzer.java b/core/src/main/java/org/teavm/model/util/NonSsaLivenessAnalyzer.java new file mode 100644 index 000000000..da564895f --- /dev/null +++ b/core/src/main/java/org/teavm/model/util/NonSsaLivenessAnalyzer.java @@ -0,0 +1,158 @@ +/* + * Copyright 2019 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.model.util; + +import com.carrotsearch.hppc.IntStack; +import java.util.BitSet; +import org.teavm.common.Graph; +import org.teavm.model.BasicBlock; +import org.teavm.model.Instruction; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.Program; +import org.teavm.model.Variable; + +public class NonSsaLivenessAnalyzer { + private BitSet[] liveVars; + private BitSet[] liveOutVars; + + public boolean liveIn(int block, int var) { + return liveVars[block].get(var); + } + + public BitSet liveIn(int block) { + return (BitSet) liveVars[block].clone(); + } + + public BitSet liveOut(int block) { + return (BitSet) liveOutVars[block].clone(); + } + + public void analyze(Program program, MethodDescriptor descriptor) { + analyze(program, descriptor.parameterCount() + 1); + } + + public void analyze(Program program, int parameterCount) { + Graph cfg = ProgramUtils.buildControlFlowGraph(program); + BitSet[] definitions = new BitSet[program.basicBlockCount()]; + BitSet[] definedVars = new BitSet[program.basicBlockCount()]; + BitSet[] visited = new BitSet[program.basicBlockCount()]; + liveVars = new BitSet[program.basicBlockCount()]; + liveOutVars = new BitSet[program.basicBlockCount()]; + for (int i = 0; i < liveVars.length; ++i) { + definitions[i] = new BitSet(program.variableCount()); + definedVars[i] = new BitSet(program.variableCount()); + visited[i] = new BitSet(program.variableCount()); + liveVars[i] = new BitSet(program.variableCount()); + liveOutVars[i] = new BitSet(program.variableCount()); + } + + UsageExtractor usageExtractor = new UsageExtractor(); + DefinitionExtractor defExtractor = new DefinitionExtractor(); + IntStack stack = new IntStack(); + IntStack defStack = new IntStack(); + for (int i = 0; i < program.basicBlockCount(); ++i) { + BasicBlock block = program.basicBlockAt(i); + + BitSet usedVariables = new BitSet(); + BitSet definedVariables = definitions[i]; + for (Instruction insn = block.getLastInstruction(); insn != null; insn = insn.getPrevious()) { + insn.acceptVisitor(defExtractor); + for (Variable var : defExtractor.getDefinedVariables()) { + definedVariables.set(var.getIndex()); + usedVariables.clear(var.getIndex()); + } + + insn.acceptVisitor(usageExtractor); + for (Variable var : usageExtractor.getUsedVariables()) { + usedVariables.set(var.getIndex()); + } + } + + assert block.getPhis().isEmpty(); + + if (block.getExceptionVariable() != null) { + definedVariables.set(block.getExceptionVariable().getIndex()); + usedVariables.clear(block.getExceptionVariable().getIndex()); + } + + if (i == 0) { + for (int v = 0; v < parameterCount; ++v) { + definedVariables.set(v); + usedVariables.clear(v); + } + } + + for (int v = usedVariables.nextSetBit(0); v >= 0; v = usedVariables.nextSetBit(v + 1)) { + liveVars[i].set(v); + visited[i].set(v); + for (int pred : cfg.incomingEdges(i)) { + stack.push(pred); + stack.push(v); + } + } + + for (int v = definedVariables.nextSetBit(0); v >= 0; v = definedVariables.nextSetBit(v + 1)) { + defStack.push(i); + defStack.push(v); + } + } + + while (!defStack.isEmpty()) { + int variable = defStack.pop(); + int block = defStack.pop(); + BitSet blockDefinedVars = definedVars[block]; + if (blockDefinedVars.get(variable)) { + continue; + } + blockDefinedVars.set(variable); + for (int succ : cfg.outgoingEdges(block)) { + if (!definedVars[succ].get(variable)) { + defStack.push(succ); + defStack.push(variable); + } + } + } + + while (!stack.isEmpty()) { + int variable = stack.pop(); + int block = stack.pop(); + BitSet blockVisited = visited[block]; + if (blockVisited.get(variable)) { + continue; + } + blockVisited.set(variable); + + if (definitions[block].get(variable) || !definedVars[block].get(variable)) { + continue; + } + + liveVars[block].set(variable); + + for (int pred : cfg.incomingEdges(block)) { + if (!visited[pred].get(variable)) { + stack.push(pred); + stack.push(variable); + } + } + } + + for (int i = 0; i < liveVars.length; ++i) { + for (int j : cfg.incomingEdges(i)) { + liveOutVars[j].or(liveVars[i]); + } + } + } +}