Fix coroutine transformation. Make emulated threads work in Wasm

This commit is contained in:
Alexey Andreev 2021-03-22 21:28:50 +03:00
parent eb495182d2
commit 9df897d298
4 changed files with 46 additions and 8 deletions

View File

@ -120,8 +120,8 @@ public class CoroutineTransformation {
processBlock(program.basicBlockAt(i));
}
splitter.fixProgram();
new PhiUpdater().updatePhis(program, methodReference.parameterCount() + 1);
processIrreducibleCfg();
new PhiUpdater().updatePhis(program, methodReference.parameterCount() + 1);
}
private void createSplitPrologue() {
@ -475,7 +475,6 @@ public class CoroutineTransformation {
weights[i] = program.basicBlockAt(i).instructionCount();
}
GraphUtils.splitIrreducibleGraph(graph, weights, splittingBackend);
new PhiUpdater().updatePhis(program, parameterCount + 1);
}
class SplittingBackend implements GraphSplittingBackend {

View File

@ -37,6 +37,7 @@ import org.teavm.backend.lowlevel.analyze.LowLevelInliningFilterFactory;
import org.teavm.backend.lowlevel.dependency.StringsDependencyListener;
import org.teavm.backend.lowlevel.generate.NameProvider;
import org.teavm.backend.lowlevel.generate.NameProviderWithSpecialNames;
import org.teavm.backend.lowlevel.transform.CoroutineTransformation;
import org.teavm.backend.wasm.binary.BinaryWriter;
import org.teavm.backend.wasm.generate.WasmClassGenerator;
import org.teavm.backend.wasm.generate.WasmDependencyListener;
@ -154,6 +155,7 @@ import org.teavm.model.optimization.InliningFilterFactory;
import org.teavm.model.transformation.BoundCheckInsertion;
import org.teavm.model.transformation.ClassPatch;
import org.teavm.model.transformation.NullCheckInsertion;
import org.teavm.model.util.AsyncMethodFinder;
import org.teavm.runtime.Allocator;
import org.teavm.runtime.EventQueue;
import org.teavm.runtime.ExceptionHandling;
@ -194,6 +196,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
private int minHeapSize = 2 * 1024 * 1024;
private int maxHeapSize = 128 * 1024 * 1024;
private boolean obfuscated;
private Set<MethodReference> asyncMethods;
private boolean hasThreads;
@Override
public void setController(TeaVMTargetController controller) {
@ -380,6 +384,16 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
dependencyAnalyzer.addDependencyListener(new StringsDependencyListener());
}
@Override
public void analyzeBeforeOptimizations(ListableClassReaderSource classSource) {
AsyncMethodFinder asyncFinder = new AsyncMethodFinder(controller.getDependencyInfo().getCallGraph(),
controller.getDependencyInfo());
asyncFinder.find(classSource);
asyncMethods = new HashSet<>(asyncFinder.getAsyncMethods());
asyncMethods.addAll(asyncFinder.getAsyncFamilyMethods());
hasThreads = asyncFinder.hasAsyncMethods();
}
@Override
public void beforeOptimizations(Program program, MethodReader method) {
nullCheckInsertion.transformProgram(program, method.getReference());
@ -390,6 +404,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
public void afterOptimizations(Program program, MethodReader method) {
classInitializerEliminator.apply(program);
classInitializerTransformer.transform(program);
new CoroutineTransformation(controller.getUnprocessedClassSource(), asyncMethods, hasThreads)
.apply(program, method.getReference());
checkTransformation.apply(program, method.getResultType());
shadowStackTransformer.apply(program, method);
writeBarrierInsertion.apply(program);
@ -456,7 +472,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
classGenerator, stringPool, obfuscated);
context.addIntrinsic(exceptionHandlingIntrinsic);
WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator, binaryWriter);
WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator, binaryWriter,
asyncMethods::contains);
generateMethods(classes, context, generator, classGenerator, binaryWriter, module);
new WasmInteropFunctionGenerator(classGenerator).generateFunctions(module);

View File

@ -126,6 +126,14 @@ import org.teavm.runtime.RuntimeClass;
import org.teavm.runtime.ShadowStack;
class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
private static final MethodReference MONITOR_ENTER_SYNC = new MethodReference(Object.class,
"monitorEnterSync", Object.class, void.class);
private static final MethodReference MONITOR_EXIT_SYNC = new MethodReference(Object.class,
"monitorExitSync", Object.class, void.class);
private static final MethodReference MONITOR_ENTER = new MethodReference(Object.class,
"monitorEnter", Object.class, void.class);
private static final MethodReference MONITOR_EXIT = new MethodReference(Object.class,
"monitorExit", Object.class, void.class);
private static final int SWITCH_TABLE_THRESHOLD = 256;
private WasmGenerationContext context;
private WasmClassGenerator classGenerator;
@ -140,10 +148,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
private List<Deque<WasmLocal>> temporaryVariablesByType = new ArrayList<>();
private WasmLocal stackVariable;
private BinaryWriter binaryWriter;
private boolean async;
WasmExpression result;
WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator,
BinaryWriter binaryWriter, WasmFunction function, int firstVariable) {
BinaryWriter binaryWriter, WasmFunction function, int firstVariable, boolean async) {
this.context = context;
this.classGenerator = classGenerator;
this.binaryWriter = binaryWriter;
@ -154,6 +163,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
temporaryVariablesByType.add(new ArrayDeque<>());
}
typeInference = new WasmTypeInference(context);
this.async = async;
}
private void accept(Expr expr) {
@ -1420,12 +1430,20 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
@Override
public void visit(MonitorEnterStatement statement) {
result = emptyStatement(statement.getLocation());
WasmCall call = new WasmCall(context.names.forMethod(async ? MONITOR_ENTER : MONITOR_ENTER_SYNC));
call.setLocation(statement.getLocation());
statement.getObjectRef().acceptVisitor(this);
call.getArguments().add(result);
result = call;
}
@Override
public void visit(MonitorExitStatement statement) {
result = emptyStatement(statement.getLocation());
WasmCall call = new WasmCall(context.names.forMethod(async ? MONITOR_EXIT : MONITOR_EXIT_SYNC));
call.setLocation(statement.getLocation());
statement.getObjectRef().acceptVisitor(this);
call.getArguments().add(result);
result = call;
}
@Override

View File

@ -15,6 +15,7 @@
*/
package org.teavm.backend.wasm.generate;
import java.util.function.Predicate;
import org.teavm.ast.RegularMethodNode;
import org.teavm.ast.VariableNode;
import org.teavm.ast.decompilation.Decompiler;
@ -40,15 +41,18 @@ public class WasmGenerator {
private WasmClassGenerator classGenerator;
private BinaryWriter binaryWriter;
private NameProvider names;
private Predicate<MethodReference> asyncMethods;
public WasmGenerator(Decompiler decompiler, ClassHolderSource classSource,
WasmGenerationContext context, WasmClassGenerator classGenerator, BinaryWriter binaryWriter) {
WasmGenerationContext context, WasmClassGenerator classGenerator, BinaryWriter binaryWriter,
Predicate<MethodReference> asyncMethods) {
this.decompiler = decompiler;
this.classSource = classSource;
this.context = context;
this.classGenerator = classGenerator;
this.binaryWriter = binaryWriter;
names = classGenerator.names;
this.asyncMethods = asyncMethods;
}
public WasmFunction generateDefinition(MethodReference methodReference) {
@ -85,7 +89,7 @@ public class WasmGenerator {
}
WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, classGenerator, binaryWriter, function,
firstVariable);
firstVariable, asyncMethods.test(methodReference));
methodAst.getBody().acceptVisitor(visitor);
if (visitor.result instanceof WasmBlock) {
((WasmBlock) visitor.result).setType(function.getResult());