C backend: bugfixes

This commit is contained in:
Alexey Andreev 2019-04-23 19:56:16 +03:00
parent cbc8d3f638
commit f828d049c4
9 changed files with 174 additions and 60 deletions

View File

@ -1,7 +0,0 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="Project Default" />
<option name="USE_PROJECT_PROFILE" value="true" />
<version value="1.0" />
</settings>
</component>

View File

@ -36,6 +36,7 @@ import java.util.stream.Collectors;
import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationExpr;
import org.teavm.ast.decompilation.Decompiler; import org.teavm.ast.decompilation.Decompiler;
import org.teavm.backend.c.analyze.CDependencyListener; import org.teavm.backend.c.analyze.CDependencyListener;
import org.teavm.backend.c.analyze.InteropDependencyListener;
import org.teavm.backend.c.generate.BufferedCodeWriter; import org.teavm.backend.c.generate.BufferedCodeWriter;
import org.teavm.backend.c.generate.ClassGenerator; import org.teavm.backend.c.generate.ClassGenerator;
import org.teavm.backend.c.generate.CodeGenerationVisitor; import org.teavm.backend.c.generate.CodeGenerationVisitor;
@ -148,7 +149,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
@Override @Override
public List<DependencyListener> getDependencyListeners() { public List<DependencyListener> getDependencyListeners() {
return Arrays.asList(new CDependencyListener(), exportDependencyListener); return Arrays.asList(new CDependencyListener(), exportDependencyListener, new InteropDependencyListener());
} }
@Override @Override

View File

@ -0,0 +1,58 @@
/*
* 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.backend.c.analyze;
import org.teavm.dependency.AbstractDependencyListener;
import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.MethodDependency;
import org.teavm.interop.Import;
import org.teavm.interop.Structure;
import org.teavm.model.AnnotationReader;
import org.teavm.model.ClassReader;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReader;
public class InteropDependencyListener extends AbstractDependencyListener {
@Override
public void classReached(DependencyAgent agent, String className) {
if (agent.getClassHierarchy().isSuperType(Structure.class.getName(), className, false)) {
ClassReader cls = agent.getClassSource().get(className);
if (cls != null) {
for (FieldReader field : cls.getFields()) {
if (!field.hasModifier(ElementModifier.STATIC)) {
agent.linkField(field.getReference());
}
}
}
}
}
@Override
public void methodReached(DependencyAgent agent, MethodDependency method) {
if (method.isMissing() || !method.getMethod().hasModifier(ElementModifier.NATIVE)) {
return;
}
AnnotationReader importAnnot = method.getMethod().getAnnotations().get(Import.class.getName());
if (importAnnot == null) {
return;
}
if (method.getReference().getReturnType().isObject("java.lang.String")) {
method.getResult().propagate(agent.getType("java.lang.String"));
}
}
}

View File

@ -37,7 +37,6 @@ import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Program; import org.teavm.model.Program;
import org.teavm.model.TextLocation; import org.teavm.model.TextLocation;
import org.teavm.model.TryCatchBlock;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.Variable; import org.teavm.model.Variable;
import org.teavm.model.instructions.BranchingCondition; import org.teavm.model.instructions.BranchingCondition;
@ -61,13 +60,15 @@ import org.teavm.model.util.DefinitionExtractor;
import org.teavm.model.util.LivenessAnalyzer; import org.teavm.model.util.LivenessAnalyzer;
import org.teavm.model.util.PhiUpdater; import org.teavm.model.util.PhiUpdater;
import org.teavm.model.util.ProgramUtils; import org.teavm.model.util.ProgramUtils;
import org.teavm.model.util.TransitionExtractor;
import org.teavm.model.util.TypeInferer; import org.teavm.model.util.TypeInferer;
import org.teavm.model.util.UsageExtractor; import org.teavm.model.util.UsageExtractor;
import org.teavm.model.util.VariableType; import org.teavm.model.util.VariableType;
import org.teavm.runtime.Fiber; import org.teavm.runtime.Fiber;
public class CoroutineTransformation { public class CoroutineTransformation {
private static final MethodReference FIBER_SUSPEND = new MethodReference(Fiber.class, "suspend",
Fiber.AsyncCall.class, Object.class);
private static final String ASYNC_CALL = Fiber.class.getName() + "$AsyncCall";
private ClassReaderSource classSource; private ClassReaderSource classSource;
private LivenessAnalyzer livenessAnalysis = new LivenessAnalyzer(); private LivenessAnalyzer livenessAnalysis = new LivenessAnalyzer();
private TypeInferer variableTypes = new TypeInferer(); private TypeInferer variableTypes = new TypeInferer();
@ -89,6 +90,11 @@ public class CoroutineTransformation {
return; return;
} }
ClassReader cls = classSource.get(methodReference.getClassName());
if (cls != null && cls.getInterfaces().contains(ASYNC_CALL)) {
return;
}
boolean hasJob = false; boolean hasJob = false;
for (BasicBlock block : program.getBasicBlocks()) { for (BasicBlock block : program.getBasicBlocks()) {
if (hasSplitInstructions(block)) { if (hasSplitInstructions(block)) {
@ -176,15 +182,7 @@ public class CoroutineTransformation {
return Collections.emptyMap(); return Collections.emptyMap();
} }
BitSet live = new BitSet(); BitSet live = livenessAnalysis.liveOut(block.getIndex());
TransitionExtractor transitionExtractor = new TransitionExtractor();
block.getLastInstruction().acceptVisitor(transitionExtractor);
for (BasicBlock successor : transitionExtractor.getTargets()) {
live.or(livenessAnalysis.liveIn(successor.getIndex()));
}
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
live.or(livenessAnalysis.liveIn(tryCatch.getHandler().getIndex()));
}
Map<Instruction, BitSet> result = new LinkedHashMap<>(); Map<Instruction, BitSet> result = new LinkedHashMap<>();
UsageExtractor use = new UsageExtractor(); UsageExtractor use = new UsageExtractor();
@ -224,6 +222,9 @@ public class CoroutineTransformation {
if (instruction instanceof InvokeInstruction) { if (instruction instanceof InvokeInstruction) {
InvokeInstruction invoke = (InvokeInstruction) instruction; InvokeInstruction invoke = (InvokeInstruction) instruction;
MethodReference method = findRealMethod(invoke.getMethod()); MethodReference method = findRealMethod(invoke.getMethod());
if (method.equals(FIBER_SUSPEND)) {
return true;
}
if (method.getClassName().equals(Fiber.class.getName())) { if (method.getClassName().equals(Fiber.class.getName())) {
return false; return false;
} }

View File

@ -39,7 +39,6 @@ import org.teavm.model.Instruction;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Phi; import org.teavm.model.Phi;
import org.teavm.model.Program; import org.teavm.model.Program;
import org.teavm.model.TryCatchBlock;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.Variable; import org.teavm.model.Variable;
import org.teavm.model.instructions.AbstractInstructionVisitor; import org.teavm.model.instructions.AbstractInstructionVisitor;
@ -65,7 +64,6 @@ import org.teavm.model.instructions.StringConstantInstruction;
import org.teavm.model.instructions.UnwrapArrayInstruction; import org.teavm.model.instructions.UnwrapArrayInstruction;
import org.teavm.model.util.DefinitionExtractor; import org.teavm.model.util.DefinitionExtractor;
import org.teavm.model.util.LivenessAnalyzer; import org.teavm.model.util.LivenessAnalyzer;
import org.teavm.model.util.TransitionExtractor;
import org.teavm.model.util.UsageExtractor; import org.teavm.model.util.UsageExtractor;
public class EscapeAnalysis { public class EscapeAnalysis {
@ -189,17 +187,7 @@ public class EscapeAnalysis {
} }
private BitSet getUsedVarsInBlock(LivenessAnalyzer liveness, BasicBlock block) { private BitSet getUsedVarsInBlock(LivenessAnalyzer liveness, BasicBlock block) {
BitSet usedVars = new BitSet(); return liveness.liveOut(block.getIndex());
TransitionExtractor transitionExtractor = new TransitionExtractor();
block.getLastInstruction().acceptVisitor(transitionExtractor);
for (BasicBlock successor : transitionExtractor.getTargets()) {
usedVars.or(liveness.liveIn(successor.getIndex()));
}
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
usedVars.or(liveness.liveIn(tryCatch.getHandler().getIndex()));
}
return usedVars;
} }
private void propagateFields(Program program, List<Set<FieldReference>> fields) { private void propagateFields(Program program, List<Set<FieldReference>> fields) {

View File

@ -72,8 +72,8 @@ public class GCShadowStackContributor {
public int contribute(Program program, MethodReader method) { public int contribute(Program program, MethodReader method) {
List<Map<Instruction, BitSet>> liveInInformation = findCallSiteLiveIns(program, method); List<Map<Instruction, BitSet>> liveInInformation = findCallSiteLiveIns(program, method);
Graph interferenceGraph = buildInterferenceGraph(liveInInformation, program);
boolean[] spilled = getAffectedVariables(liveInInformation, program); boolean[] spilled = getAffectedVariables(liveInInformation, program);
Graph interferenceGraph = buildInterferenceGraph(liveInInformation, program, spilled);
int[] colors = new int[interferenceGraph.size()]; int[] colors = new int[interferenceGraph.size()];
Arrays.fill(colors, -1); Arrays.fill(colors, -1);
new GraphColorer().colorize(interferenceGraph, colors); new GraphColorer().colorize(interferenceGraph, colors);
@ -81,8 +81,10 @@ public class GCShadowStackContributor {
int usedColors = 0; int usedColors = 0;
for (int var = 0; var < colors.length; ++var) { for (int var = 0; var < colors.length; ++var) {
if (spilled[var]) { if (spilled[var]) {
usedColors = Math.max(usedColors, colors[var] + 1); usedColors = Math.max(usedColors, colors[var]);
colors[var]--; colors[var]--;
} else {
colors[var] = -1;
} }
} }
if (usedColors == 0) { if (usedColors == 0) {
@ -182,7 +184,8 @@ public class GCShadowStackContributor {
return liveInInformation; return liveInInformation;
} }
private Graph buildInterferenceGraph(List<Map<Instruction, BitSet>> liveInInformation, Program program) { private Graph buildInterferenceGraph(List<Map<Instruction, BitSet>> liveInInformation, Program program,
boolean[] spilled) {
GraphBuilder builder = new GraphBuilder(program.variableCount()); GraphBuilder builder = new GraphBuilder(program.variableCount());
for (Map<Instruction, BitSet> blockLiveIn : liveInInformation) { for (Map<Instruction, BitSet> blockLiveIn : liveInInformation) {
for (BitSet liveVarsSet : blockLiveIn.values()) { for (BitSet liveVarsSet : blockLiveIn.values()) {
@ -193,8 +196,12 @@ public class GCShadowStackContributor {
int[] liveVarArray = liveVars.toArray(); int[] liveVarArray = liveVars.toArray();
for (int i = 0; i < liveVarArray.length - 1; ++i) { for (int i = 0; i < liveVarArray.length - 1; ++i) {
for (int j = i + 1; j < liveVarArray.length; ++j) { for (int j = i + 1; j < liveVarArray.length; ++j) {
builder.addEdge(liveVarArray[i], liveVarArray[j]); int a = liveVarArray[i];
builder.addEdge(liveVarArray[j], liveVarArray[i]); int b = liveVarArray[j];
if (spilled[a] && spilled[b]) {
builder.addEdge(a, b);
builder.addEdge(b, a);
}
} }
} }
} }
@ -270,7 +277,7 @@ public class GCShadowStackContributor {
Step[] stack = new Step[program.basicBlockCount() * 2]; Step[] stack = new Step[program.basicBlockCount() * 2];
int head = 0; int head = 0;
Step start = new Step(0); Step start = new Step(0);
Arrays.fill(start.slotStates, usedColors); Arrays.fill(start.slotStates, program.variableCount());
stack[head++] = start; stack[head++] = start;
while (head > 0) { while (head > 0) {

View File

@ -34,13 +34,7 @@ class InterferenceGraphBuilder {
BasicBlock block = program.basicBlockAt(i); BasicBlock block = program.basicBlockAt(i);
block.getLastInstruction().acceptVisitor(succExtractor); block.getLastInstruction().acceptVisitor(succExtractor);
BitSet liveOut = new BitSet(program.variableCount()); BitSet liveOut = liveness.liveOut(i);
for (BasicBlock succ : succExtractor.getTargets()) {
liveOut.or(liveness.liveIn(succ.getIndex()));
}
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
liveOut.or(liveness.liveIn(tryCatch.getHandler().getIndex()));
}
live.clear(); live.clear();
for (int j = 0; j < liveOut.length(); ++j) { for (int j = 0; j < liveOut.length(); ++j) {
if (liveOut.get(j)) { if (liveOut.get(j)) {

View File

@ -30,6 +30,7 @@ import org.teavm.model.Variable;
public class LivenessAnalyzer { public class LivenessAnalyzer {
private BitSet[] liveVars; private BitSet[] liveVars;
private BitSet[] liveOutVars;
public boolean liveIn(int block, int var) { public boolean liveIn(int block, int var) {
return liveVars[block].get(var); return liveVars[block].get(var);
@ -39,11 +40,17 @@ public class LivenessAnalyzer {
return (BitSet) liveVars[block].clone(); return (BitSet) liveVars[block].clone();
} }
public BitSet liveOut(int block) {
return (BitSet) liveOutVars[block].clone();
}
public void analyze(Program program) { public void analyze(Program program) {
Graph cfg = ProgramUtils.buildControlFlowGraph(program); Graph cfg = ProgramUtils.buildControlFlowGraph(program);
liveVars = new BitSet[cfg.size()]; liveVars = new BitSet[cfg.size()];
liveOutVars = new BitSet[cfg.size()];
for (int i = 0; i < liveVars.length; ++i) { for (int i = 0; i < liveVars.length; ++i) {
liveVars[i] = new BitSet(program.basicBlockCount()); liveVars[i] = new BitSet(program.basicBlockCount());
liveOutVars[i] = new BitSet(program.basicBlockCount());
} }
UsageExtractor usageExtractor = new UsageExtractor(); UsageExtractor usageExtractor = new UsageExtractor();
@ -99,6 +106,18 @@ public class LivenessAnalyzer {
stack.push(nextTask); stack.push(nextTask);
} }
} }
for (int i = 0; i < liveVars.length; ++i) {
for (int j : cfg.incomingEdges(i)) {
liveOutVars[j].or(liveVars[i]);
}
BasicBlock block = program.basicBlockAt(i);
for (Phi phi : block.getPhis()) {
for (Incoming incoming : phi.getIncomings()) {
liveOutVars[incoming.getSource().getIndex()].set(incoming.getValue().getIndex());
}
}
}
} }
private static class Task { private static class Task {

View File

@ -44,6 +44,7 @@ public class Fiber {
private boolean daemon; private boolean daemon;
private static Fiber current; private static Fiber current;
private static PendingCall lastPendingCall;
private Fiber(FiberRunner runner, boolean daemon) { private Fiber(FiberRunner runner, boolean daemon) {
this.runner = runner; this.runner = runner;
@ -187,26 +188,65 @@ public class Fiber {
} }
return fiber.result; return fiber.result;
} }
PendingCall pendingCall = new PendingCall(call, lastPendingCall);
if (lastPendingCall != null) {
lastPendingCall.next = pendingCall;
}
lastPendingCall = pendingCall;
fiber.state = STATE_SUSPENDING; fiber.state = STATE_SUSPENDING;
call.run(new AsyncCallback<Object>() { pendingCall.callback = new AsyncCallbackImpl(pendingCall, javaThread, fiber);
@Override call.run(pendingCall.callback);
public void complete(Object result) {
setCurrentThread(javaThread);
fiber.result = result;
fiber.resume();
}
@Override
public void error(Throwable e) {
setCurrentThread(javaThread);
fiber.exception = e;
fiber.resume();
}
});
return null; return null;
} }
static class AsyncCallbackImpl implements AsyncCallback<Object> {
PendingCall pendingCall;
Thread javaThread;
Fiber fiber;
AsyncCallbackImpl(PendingCall pendingCall, Thread javaThread, Fiber fiber) {
this.pendingCall = pendingCall;
this.javaThread = javaThread;
this.fiber = fiber;
}
@Override
public void complete(Object result) {
setCurrentThread(javaThread);
javaThread = null;
Fiber fiber = this.fiber;
this.fiber = null;
fiber.result = result;
removePendingCall();
fiber.resume();
}
@Override
public void error(Throwable e) {
setCurrentThread(javaThread);
javaThread = null;
Fiber fiber = this.fiber;
this.fiber = null;
fiber.exception = e;
removePendingCall();
fiber.resume();
}
private void removePendingCall() {
if (pendingCall.previous != null) {
pendingCall.previous.next = pendingCall.next;
}
if (pendingCall.next != null) {
pendingCall.next.previous = pendingCall.previous;
}
if (pendingCall == lastPendingCall) {
lastPendingCall = pendingCall.previous;
}
pendingCall = null;
}
}
static native void setCurrentThread(Thread thread); static native void setCurrentThread(Thread thread);
public static void start(FiberRunner runner, boolean daemon) { public static void start(FiberRunner runner, boolean daemon) {
@ -241,4 +281,17 @@ public class Fiber {
public interface AsyncCall { public interface AsyncCall {
void run(AsyncCallback<?> callback); void run(AsyncCallback<?> callback);
} }
static class PendingCall {
AsyncCall value;
PendingCall next;
PendingCall previous;
AsyncCallbackImpl callback;
PendingCall(AsyncCall value, PendingCall previous) {
this.value = value;
this.next = null;
this.previous = previous;
}
}
} }