mirror of
https://github.com/konsoletyper/teavm.git
synced 2024-11-27 01:30:35 +08:00
C backend: bugfixes
This commit is contained in:
parent
cbc8d3f638
commit
f828d049c4
@ -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>
|
@ -36,6 +36,7 @@ import java.util.stream.Collectors;
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.ast.decompilation.Decompiler;
|
||||
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.ClassGenerator;
|
||||
import org.teavm.backend.c.generate.CodeGenerationVisitor;
|
||||
@ -148,7 +149,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||
|
||||
@Override
|
||||
public List<DependencyListener> getDependencyListeners() {
|
||||
return Arrays.asList(new CDependencyListener(), exportDependencyListener);
|
||||
return Arrays.asList(new CDependencyListener(), exportDependencyListener, new InteropDependencyListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
@ -37,7 +37,6 @@ import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.TextLocation;
|
||||
import org.teavm.model.TryCatchBlock;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.Variable;
|
||||
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.PhiUpdater;
|
||||
import org.teavm.model.util.ProgramUtils;
|
||||
import org.teavm.model.util.TransitionExtractor;
|
||||
import org.teavm.model.util.TypeInferer;
|
||||
import org.teavm.model.util.UsageExtractor;
|
||||
import org.teavm.model.util.VariableType;
|
||||
import org.teavm.runtime.Fiber;
|
||||
|
||||
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 LivenessAnalyzer livenessAnalysis = new LivenessAnalyzer();
|
||||
private TypeInferer variableTypes = new TypeInferer();
|
||||
@ -89,6 +90,11 @@ public class CoroutineTransformation {
|
||||
return;
|
||||
}
|
||||
|
||||
ClassReader cls = classSource.get(methodReference.getClassName());
|
||||
if (cls != null && cls.getInterfaces().contains(ASYNC_CALL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hasJob = false;
|
||||
for (BasicBlock block : program.getBasicBlocks()) {
|
||||
if (hasSplitInstructions(block)) {
|
||||
@ -176,15 +182,7 @@ public class CoroutineTransformation {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
BitSet live = new BitSet();
|
||||
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()));
|
||||
}
|
||||
BitSet live = livenessAnalysis.liveOut(block.getIndex());
|
||||
|
||||
Map<Instruction, BitSet> result = new LinkedHashMap<>();
|
||||
UsageExtractor use = new UsageExtractor();
|
||||
@ -224,6 +222,9 @@ public class CoroutineTransformation {
|
||||
if (instruction instanceof InvokeInstruction) {
|
||||
InvokeInstruction invoke = (InvokeInstruction) instruction;
|
||||
MethodReference method = findRealMethod(invoke.getMethod());
|
||||
if (method.equals(FIBER_SUSPEND)) {
|
||||
return true;
|
||||
}
|
||||
if (method.getClassName().equals(Fiber.class.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
@ -39,7 +39,6 @@ import org.teavm.model.Instruction;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Phi;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.TryCatchBlock;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.Variable;
|
||||
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.util.DefinitionExtractor;
|
||||
import org.teavm.model.util.LivenessAnalyzer;
|
||||
import org.teavm.model.util.TransitionExtractor;
|
||||
import org.teavm.model.util.UsageExtractor;
|
||||
|
||||
public class EscapeAnalysis {
|
||||
@ -189,17 +187,7 @@ public class EscapeAnalysis {
|
||||
}
|
||||
|
||||
private BitSet getUsedVarsInBlock(LivenessAnalyzer liveness, BasicBlock block) {
|
||||
BitSet usedVars = new BitSet();
|
||||
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;
|
||||
return liveness.liveOut(block.getIndex());
|
||||
}
|
||||
|
||||
private void propagateFields(Program program, List<Set<FieldReference>> fields) {
|
||||
|
@ -72,8 +72,8 @@ public class GCShadowStackContributor {
|
||||
public int contribute(Program program, MethodReader method) {
|
||||
List<Map<Instruction, BitSet>> liveInInformation = findCallSiteLiveIns(program, method);
|
||||
|
||||
Graph interferenceGraph = buildInterferenceGraph(liveInInformation, program);
|
||||
boolean[] spilled = getAffectedVariables(liveInInformation, program);
|
||||
Graph interferenceGraph = buildInterferenceGraph(liveInInformation, program, spilled);
|
||||
int[] colors = new int[interferenceGraph.size()];
|
||||
Arrays.fill(colors, -1);
|
||||
new GraphColorer().colorize(interferenceGraph, colors);
|
||||
@ -81,8 +81,10 @@ public class GCShadowStackContributor {
|
||||
int usedColors = 0;
|
||||
for (int var = 0; var < colors.length; ++var) {
|
||||
if (spilled[var]) {
|
||||
usedColors = Math.max(usedColors, colors[var] + 1);
|
||||
usedColors = Math.max(usedColors, colors[var]);
|
||||
colors[var]--;
|
||||
} else {
|
||||
colors[var] = -1;
|
||||
}
|
||||
}
|
||||
if (usedColors == 0) {
|
||||
@ -182,7 +184,8 @@ public class GCShadowStackContributor {
|
||||
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());
|
||||
for (Map<Instruction, BitSet> blockLiveIn : liveInInformation) {
|
||||
for (BitSet liveVarsSet : blockLiveIn.values()) {
|
||||
@ -193,8 +196,12 @@ public class GCShadowStackContributor {
|
||||
int[] liveVarArray = liveVars.toArray();
|
||||
for (int i = 0; i < liveVarArray.length - 1; ++i) {
|
||||
for (int j = i + 1; j < liveVarArray.length; ++j) {
|
||||
builder.addEdge(liveVarArray[i], liveVarArray[j]);
|
||||
builder.addEdge(liveVarArray[j], liveVarArray[i]);
|
||||
int a = 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];
|
||||
int head = 0;
|
||||
Step start = new Step(0);
|
||||
Arrays.fill(start.slotStates, usedColors);
|
||||
Arrays.fill(start.slotStates, program.variableCount());
|
||||
stack[head++] = start;
|
||||
|
||||
while (head > 0) {
|
||||
|
@ -34,13 +34,7 @@ class InterferenceGraphBuilder {
|
||||
BasicBlock block = program.basicBlockAt(i);
|
||||
block.getLastInstruction().acceptVisitor(succExtractor);
|
||||
|
||||
BitSet liveOut = new BitSet(program.variableCount());
|
||||
for (BasicBlock succ : succExtractor.getTargets()) {
|
||||
liveOut.or(liveness.liveIn(succ.getIndex()));
|
||||
}
|
||||
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
||||
liveOut.or(liveness.liveIn(tryCatch.getHandler().getIndex()));
|
||||
}
|
||||
BitSet liveOut = liveness.liveOut(i);
|
||||
live.clear();
|
||||
for (int j = 0; j < liveOut.length(); ++j) {
|
||||
if (liveOut.get(j)) {
|
||||
|
@ -30,6 +30,7 @@ import org.teavm.model.Variable;
|
||||
|
||||
public class LivenessAnalyzer {
|
||||
private BitSet[] liveVars;
|
||||
private BitSet[] liveOutVars;
|
||||
|
||||
public boolean liveIn(int block, int var) {
|
||||
return liveVars[block].get(var);
|
||||
@ -39,11 +40,17 @@ public class LivenessAnalyzer {
|
||||
return (BitSet) liveVars[block].clone();
|
||||
}
|
||||
|
||||
public BitSet liveOut(int block) {
|
||||
return (BitSet) liveOutVars[block].clone();
|
||||
}
|
||||
|
||||
public void analyze(Program program) {
|
||||
Graph cfg = ProgramUtils.buildControlFlowGraph(program);
|
||||
liveVars = new BitSet[cfg.size()];
|
||||
liveOutVars = new BitSet[cfg.size()];
|
||||
for (int i = 0; i < liveVars.length; ++i) {
|
||||
liveVars[i] = new BitSet(program.basicBlockCount());
|
||||
liveOutVars[i] = new BitSet(program.basicBlockCount());
|
||||
}
|
||||
|
||||
UsageExtractor usageExtractor = new UsageExtractor();
|
||||
@ -99,6 +106,18 @@ public class LivenessAnalyzer {
|
||||
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 {
|
||||
|
@ -44,6 +44,7 @@ public class Fiber {
|
||||
private boolean daemon;
|
||||
|
||||
private static Fiber current;
|
||||
private static PendingCall lastPendingCall;
|
||||
|
||||
private Fiber(FiberRunner runner, boolean daemon) {
|
||||
this.runner = runner;
|
||||
@ -187,26 +188,65 @@ public class Fiber {
|
||||
}
|
||||
return fiber.result;
|
||||
}
|
||||
PendingCall pendingCall = new PendingCall(call, lastPendingCall);
|
||||
if (lastPendingCall != null) {
|
||||
lastPendingCall.next = pendingCall;
|
||||
}
|
||||
lastPendingCall = pendingCall;
|
||||
|
||||
fiber.state = STATE_SUSPENDING;
|
||||
call.run(new AsyncCallback<Object>() {
|
||||
@Override
|
||||
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();
|
||||
}
|
||||
});
|
||||
pendingCall.callback = new AsyncCallbackImpl(pendingCall, javaThread, fiber);
|
||||
call.run(pendingCall.callback);
|
||||
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);
|
||||
|
||||
public static void start(FiberRunner runner, boolean daemon) {
|
||||
@ -241,4 +281,17 @@ public class Fiber {
|
||||
public interface AsyncCall {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user