diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 3b312839b..000000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - \ No newline at end of file diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index 9d8dcf0a0..e3cd75426 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -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 getDependencyListeners() { - return Arrays.asList(new CDependencyListener(), exportDependencyListener); + return Arrays.asList(new CDependencyListener(), exportDependencyListener, new InteropDependencyListener()); } @Override diff --git a/core/src/main/java/org/teavm/backend/c/analyze/InteropDependencyListener.java b/core/src/main/java/org/teavm/backend/c/analyze/InteropDependencyListener.java new file mode 100644 index 000000000..d24dee91f --- /dev/null +++ b/core/src/main/java/org/teavm/backend/c/analyze/InteropDependencyListener.java @@ -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")); + } + } +} diff --git a/core/src/main/java/org/teavm/backend/lowlevel/transform/CoroutineTransformation.java b/core/src/main/java/org/teavm/backend/lowlevel/transform/CoroutineTransformation.java index 84255f83d..540fc00b8 100644 --- a/core/src/main/java/org/teavm/backend/lowlevel/transform/CoroutineTransformation.java +++ b/core/src/main/java/org/teavm/backend/lowlevel/transform/CoroutineTransformation.java @@ -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 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; } diff --git a/core/src/main/java/org/teavm/model/analysis/EscapeAnalysis.java b/core/src/main/java/org/teavm/model/analysis/EscapeAnalysis.java index d64c5bb48..b5471993c 100644 --- a/core/src/main/java/org/teavm/model/analysis/EscapeAnalysis.java +++ b/core/src/main/java/org/teavm/model/analysis/EscapeAnalysis.java @@ -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> fields) { diff --git a/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java b/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java index b548f67fe..e1f434fba 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java +++ b/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java @@ -72,8 +72,8 @@ public class GCShadowStackContributor { public int contribute(Program program, MethodReader method) { List> 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> liveInInformation, Program program) { + private Graph buildInterferenceGraph(List> liveInInformation, Program program, + boolean[] spilled) { GraphBuilder builder = new GraphBuilder(program.variableCount()); for (Map 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) { diff --git a/core/src/main/java/org/teavm/model/util/InterferenceGraphBuilder.java b/core/src/main/java/org/teavm/model/util/InterferenceGraphBuilder.java index b63374aaf..97528b25d 100644 --- a/core/src/main/java/org/teavm/model/util/InterferenceGraphBuilder.java +++ b/core/src/main/java/org/teavm/model/util/InterferenceGraphBuilder.java @@ -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)) { 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 ae429b8f8..541c149e6 100644 --- a/core/src/main/java/org/teavm/model/util/LivenessAnalyzer.java +++ b/core/src/main/java/org/teavm/model/util/LivenessAnalyzer.java @@ -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 { diff --git a/core/src/main/java/org/teavm/runtime/Fiber.java b/core/src/main/java/org/teavm/runtime/Fiber.java index 65db5b75a..0a06c388c 100644 --- a/core/src/main/java/org/teavm/runtime/Fiber.java +++ b/core/src/main/java/org/teavm/runtime/Fiber.java @@ -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() { - @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 { + 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; + } + } }