wasm: implement transformation from TeaVM tree IR to Wasm GC

This commit is contained in:
Alexey Andreev 2024-07-23 20:50:05 +02:00
parent 172de8e737
commit f48c24283c
18 changed files with 768 additions and 95 deletions

View File

@ -127,6 +127,8 @@ import org.teavm.model.transformation.BoundCheckInsertion;
import org.teavm.model.transformation.ClassPatch; import org.teavm.model.transformation.ClassPatch;
import org.teavm.model.transformation.NullCheckInsertion; import org.teavm.model.transformation.NullCheckInsertion;
import org.teavm.model.util.AsyncMethodFinder; import org.teavm.model.util.AsyncMethodFinder;
import org.teavm.model.util.DefaultVariableCategoryProvider;
import org.teavm.model.util.VariableCategoryProvider;
import org.teavm.runtime.Allocator; import org.teavm.runtime.Allocator;
import org.teavm.runtime.CallSite; import org.teavm.runtime.CallSite;
import org.teavm.runtime.CallSiteLocation; import org.teavm.runtime.CallSiteLocation;
@ -259,8 +261,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
} }
@Override @Override
public boolean requiresRegisterAllocation() { public VariableCategoryProvider variableCategoryProvider() {
return true; return new DefaultVariableCategoryProvider();
} }
@Override @Override

View File

@ -98,6 +98,8 @@ import org.teavm.model.instructions.StringConstantInstruction;
import org.teavm.model.transformation.BoundCheckInsertion; import org.teavm.model.transformation.BoundCheckInsertion;
import org.teavm.model.transformation.NullCheckFilter; import org.teavm.model.transformation.NullCheckFilter;
import org.teavm.model.transformation.NullCheckInsertion; import org.teavm.model.transformation.NullCheckInsertion;
import org.teavm.model.util.DefaultVariableCategoryProvider;
import org.teavm.model.util.VariableCategoryProvider;
import org.teavm.vm.BuildTarget; import org.teavm.vm.BuildTarget;
import org.teavm.vm.RenderingException; import org.teavm.vm.RenderingException;
import org.teavm.vm.TeaVMTarget; import org.teavm.vm.TeaVMTarget;
@ -223,8 +225,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
} }
@Override @Override
public boolean requiresRegisterAllocation() { public VariableCategoryProvider variableCategoryProvider() {
return true; return new DefaultVariableCategoryProvider();
} }
public void setStackTraceIncluded(boolean stackTraceIncluded) { public void setStackTraceIncluded(boolean stackTraceIncluded) {

View File

@ -166,6 +166,8 @@ import org.teavm.model.transformation.BoundCheckInsertion;
import org.teavm.model.transformation.ClassPatch; import org.teavm.model.transformation.ClassPatch;
import org.teavm.model.transformation.NullCheckInsertion; import org.teavm.model.transformation.NullCheckInsertion;
import org.teavm.model.util.AsyncMethodFinder; import org.teavm.model.util.AsyncMethodFinder;
import org.teavm.model.util.DefaultVariableCategoryProvider;
import org.teavm.model.util.VariableCategoryProvider;
import org.teavm.runtime.Allocator; import org.teavm.runtime.Allocator;
import org.teavm.runtime.EventQueue; import org.teavm.runtime.EventQueue;
import org.teavm.runtime.ExceptionHandling; import org.teavm.runtime.ExceptionHandling;
@ -236,8 +238,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
} }
@Override @Override
public boolean requiresRegisterAllocation() { public VariableCategoryProvider variableCategoryProvider() {
return true; return new DefaultVariableCategoryProvider();
} }
@Override @Override

View File

@ -146,8 +146,10 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
} }
@Override @Override
protected WasmExpression generateThrowNPE() { protected void generateThrowNPE(TextLocation location, List<WasmExpression> target) {
return new WasmCall(context.functions().forStaticMethod(THROW_NPE_METHOD)); var call = new WasmCall(context.functions().forStaticMethod(THROW_NPE_METHOD));
call.setLocation(location);
target.add(call);
} }
@Override @Override
@ -174,8 +176,17 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
} }
@Override @Override
protected WasmExpression catchException() { protected void catchException(TextLocation location, List<WasmExpression> target, WasmLocal local) {
return new WasmCall(context.functions().forStaticMethod(CATCH_METHOD)); var call = new WasmCall(context.functions().forStaticMethod(CATCH_METHOD));
if (local != null) {
var save = new WasmSetLocal(local, call);
save.setLocation(location);
target.add(save);
} else {
var drop = new WasmDrop(call);
drop.setLocation(location);
target.add(drop);
}
} }
@Override @Override
@ -494,8 +505,10 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
} }
@Override @Override
protected WasmExpression generateThrow(WasmExpression expression) { protected void generateThrow(WasmExpression expression, TextLocation location, List<WasmExpression> target) {
return new WasmCall(context.functions().forStaticMethod(THROW_METHOD), result); var call = new WasmCall(context.functions().forStaticMethod(THROW_METHOD), result);
call.setLocation(location);
target.add(call);
} }
private class CallSiteIdentifierImpl extends CallSiteIdentifier private class CallSiteIdentifierImpl extends CallSiteIdentifier
@ -745,18 +758,24 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
} }
@Override @Override
protected WasmExpression allocateObject(String className, TextLocation location) { protected void allocateObject(String className, TextLocation location, WasmLocal local,
List<WasmExpression> target) {
int tag = classGenerator.getClassPointer(ValueType.object(className)); int tag = classGenerator.getClassPointer(ValueType.object(className));
var allocFunction = context.functions().forStaticMethod(new MethodReference(Allocator.class, "allocate", var allocFunction = context.functions().forStaticMethod(new MethodReference(Allocator.class, "allocate",
RuntimeClass.class, Address.class)); RuntimeClass.class, Address.class));
WasmCall call = new WasmCall(allocFunction); WasmCall call = new WasmCall(allocFunction);
call.getArguments().add(new WasmInt32Constant(tag)); call.getArguments().add(new WasmInt32Constant(tag));
call.setLocation(location); call.setLocation(location);
return call; if (local != null) {
target.add(new WasmSetLocal(local, call));
} else {
target.add(call);
}
} }
@Override @Override
protected WasmExpression allocateArray(ValueType itemType, WasmExpression length, TextLocation location) { protected void allocateArray(ValueType itemType, WasmExpression length, TextLocation location, WasmLocal local,
List<WasmExpression> target) {
int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(itemType)); int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(itemType));
var allocFunction = context.functions().forStaticMethod(new MethodReference(Allocator.class, "allocateArray", var allocFunction = context.functions().forStaticMethod(new MethodReference(Allocator.class, "allocateArray",
RuntimeClass.class, int.class, Address.class)); RuntimeClass.class, int.class, Address.class));
@ -764,7 +783,11 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
call.getArguments().add(new WasmInt32Constant(classPointer)); call.getArguments().add(new WasmInt32Constant(classPointer));
call.getArguments().add(length); call.getArguments().add(length);
call.setLocation(location); call.setLocation(location);
return call; if (local != null) {
target.add(new WasmSetLocal(local, call));
} else {
target.add(call);
}
} }
@Override @Override
@ -822,8 +845,10 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
} }
@Override @Override
protected WasmExpression generateThrowCCE() { protected void generateThrowCCE(TextLocation location, List<WasmExpression> target) {
return new WasmCall(context.functions().forStaticMethod(THROW_CCE_METHOD)); var call = new WasmCall(context.functions().forStaticMethod(THROW_CCE_METHOD));
call.setLocation(location);
target.add(call);
} }
@Override @Override
@ -834,8 +859,8 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
} }
@Override @Override
protected boolean needsClassInitializer(String clasName) { protected boolean needsClassInitializer(String className) {
return classGenerator.hasClinit(clasName); return classGenerator.hasClinit(className);
} }
@Override @Override
@ -928,8 +953,10 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
} }
@Override @Override
protected WasmExpression generateThrowAIOOBE() { protected void generateThrowAIOOBE(TextLocation location, List<WasmExpression> target) {
return new WasmCall(context.functions().forStaticMethod(THROW_AIOOBE_METHOD)); var call = new WasmCall(context.functions().forStaticMethod(THROW_AIOOBE_METHOD));
call.setLocation(location);
target.add(call);
} }
private WasmIntrinsicManager intrinsicManager = new WasmIntrinsicManager() { private WasmIntrinsicManager intrinsicManager = new WasmIntrinsicManager() {

View File

@ -125,7 +125,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
private Map<IdentifiedStatement, WasmBlock> continueTargets = new HashMap<>(); private Map<IdentifiedStatement, WasmBlock> continueTargets = new HashMap<>();
private Set<WasmBlock> usedBlocks = new HashSet<>(); private Set<WasmBlock> usedBlocks = new HashSet<>();
protected final TemporaryVariablePool tempVars; protected final TemporaryVariablePool tempVars;
private ExpressionCache exprCache; protected final ExpressionCache exprCache;
private boolean async; private boolean async;
protected WasmExpression result; protected WasmExpression result;
@ -459,14 +459,14 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
var callSiteId = generateCallSiteId(location); var callSiteId = generateCallSiteId(location);
callSiteId.generateRegister(block.getBody(), location); callSiteId.generateRegister(block.getBody(), location);
block.getBody().add(generateThrowNPE()); generateThrowNPE(location, block.getBody());
callSiteId.generateThrow(block.getBody(), location); callSiteId.generateThrow(block.getBody(), location);
cachedValue.release(); cachedValue.release();
return block; return block;
} }
protected abstract WasmExpression generateThrowNPE(); protected abstract void generateThrowNPE(TextLocation location, List<WasmExpression> target);
protected abstract WasmExpression generateArrayLength(WasmExpression array); protected abstract WasmExpression generateArrayLength(WasmExpression array);
@ -852,8 +852,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
block.setType(resultType); block.setType(resultType);
var tmp = tempVars.acquire(resultType); var tmp = tempVars.acquire(resultType);
block.getBody().add(new WasmSetLocal(tmp, allocateObject(expr.getMethod().getClassName(), allocateObject(expr.getMethod().getClassName(), expr.getLocation(), tmp, block.getBody());
expr.getLocation())));
var function = context.functions().forInstanceMethod(expr.getMethod()); var function = context.functions().forInstanceMethod(expr.getMethod());
var call = new WasmCall(function); var call = new WasmCall(function);
@ -876,7 +875,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
accept(expr.getArguments().get(0)); accept(expr.getArguments().get(0));
var instance = result; var instance = result;
var block = new WasmBlock(false); var block = new WasmBlock(false);
block.setType(WasmGeneratorUtil.mapType(reference.getReturnType())); block.setType(mapType(reference.getReturnType()));
var instanceVar = tempVars.acquire(WasmType.INT32); var instanceVar = tempVars.acquire(WasmType.INT32);
block.getBody().add(new WasmSetLocal(instanceVar, instance)); block.getBody().add(new WasmSetLocal(instanceVar, instance));
@ -961,7 +960,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
block.setType(mapType(ValueType.object(expr.getConstructedClass()))); block.setType(mapType(ValueType.object(expr.getConstructedClass())));
var callSiteId = generateCallSiteId(expr.getLocation()); var callSiteId = generateCallSiteId(expr.getLocation());
callSiteId.generateRegister(block.getBody(), expr.getLocation()); callSiteId.generateRegister(block.getBody(), expr.getLocation());
block.getBody().add(allocateObject(expr.getConstructedClass(), expr.getLocation())); allocateObject(expr.getConstructedClass(), expr.getLocation(), null, block.getBody());
if (block.getBody().size() == 1) { if (block.getBody().size() == 1) {
result = block.getBody().get(0); result = block.getBody().get(0);
} else { } else {
@ -969,7 +968,8 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
} }
} }
protected abstract WasmExpression allocateObject(String className, TextLocation location); protected abstract void allocateObject(String className, TextLocation location, WasmLocal local,
List<WasmExpression> target);
@Override @Override
public void visit(NewArrayExpr expr) { public void visit(NewArrayExpr expr) {
@ -981,8 +981,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
accept(expr.getLength()); accept(expr.getLength());
var length = result; var length = result;
var call = allocateArray(expr.getType(), length, expr.getLocation()); allocateArray(expr.getType(), length, expr.getLocation(), null, block.getBody());
block.getBody().add(call);
if (block.getBody().size() == 1) { if (block.getBody().size() == 1) {
result = block.getBody().get(0); result = block.getBody().get(0);
@ -991,7 +990,8 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
} }
} }
protected abstract WasmExpression allocateArray(ValueType itemType, WasmExpression length, TextLocation location); protected abstract void allocateArray(ValueType itemType, WasmExpression length, TextLocation location,
WasmLocal local, List<WasmExpression> target);
protected abstract WasmExpression allocateMultiArray(List<WasmExpression> target, ValueType itemType, protected abstract WasmExpression allocateMultiArray(List<WasmExpression> target, ValueType itemType,
List<WasmExpression> dimensions, TextLocation location); List<WasmExpression> dimensions, TextLocation location);
@ -1035,9 +1035,8 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
callSiteId.generateRegister(block.getBody(), expr.getLocation()); callSiteId.generateRegister(block.getBody(), expr.getLocation());
var array = tempVars.acquire(wasmArrayType); var array = tempVars.acquire(wasmArrayType);
var allocation = allocateArray(expr.getType(), new WasmInt32Constant(expr.getData().size()), allocateArray(expr.getType(), new WasmInt32Constant(expr.getData().size()), expr.getLocation(), array,
expr.getLocation()); block.getBody());
block.getBody().add(new WasmSetLocal(array, allocation));
for (int i = 0; i < expr.getData().size(); ++i) { for (int i = 0; i < expr.getData().size(); ++i) {
expr.getData().get(i).acceptVisitor(this); expr.getData().get(i).acceptVisitor(this);
@ -1117,13 +1116,12 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
callSiteId.generateRegister(resultConsumer, statement.getLocation()); callSiteId.generateRegister(resultConsumer, statement.getLocation());
accept(statement.getException()); accept(statement.getException());
var call = generateThrow(result); generateThrow(result, statement.getLocation(), resultConsumer);
call.setLocation(statement.getLocation());
resultConsumer.add(call);
callSiteId.generateThrow(resultConsumer, statement.getLocation()); callSiteId.generateThrow(resultConsumer, statement.getLocation());
} }
protected abstract WasmExpression generateThrow(WasmExpression expression); protected abstract void generateThrow(WasmExpression expression, TextLocation location,
List<WasmExpression> target);
@Override @Override
public void visit(CastExpr expr) { public void visit(CastExpr expr) {
@ -1149,7 +1147,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
var callSiteId = generateCallSiteId(expr.getLocation()); var callSiteId = generateCallSiteId(expr.getLocation());
callSiteId.generateRegister(block.getBody(), expr.getLocation()); callSiteId.generateRegister(block.getBody(), expr.getLocation());
block.getBody().add(generateThrowCCE()); generateThrowCCE(expr.getLocation(), block.getBody());
callSiteId.generateThrow(block.getBody(), expr.getLocation()); callSiteId.generateThrow(block.getBody(), expr.getLocation());
valueToCast.release(); valueToCast.release();
@ -1168,7 +1166,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
} }
} }
protected abstract boolean needsClassInitializer(String clasName); protected abstract boolean needsClassInitializer(String className);
protected abstract WasmExpression generateClassInitializer(String className, TextLocation location); protected abstract WasmExpression generateClassInitializer(String className, TextLocation location);
@ -1245,11 +1243,10 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
var catchBlock = catchBlocks.get(i); var catchBlock = catchBlocks.get(i);
catchBlock.getBody().add(currentBlock); catchBlock.getBody().add(currentBlock);
var catchCall = catchException(); var catchLocal = tryCatch.getExceptionVariable() != null
var catchWrapper = tryCatch.getExceptionVariable() != null ? localVar(tryCatch.getExceptionVariable())
? new WasmSetLocal(localVar(tryCatch.getExceptionVariable()), catchCall) : null;
: new WasmDrop(catchCall); catchException(null, catchBlock.getBody(), catchLocal);
catchBlock.getBody().add(catchWrapper);
visitMany(tryCatch.getHandler(), catchBlock.getBody()); visitMany(tryCatch.getHandler(), catchBlock.getBody());
if (!catchBlock.isTerminating() && catchBlock != outerCatchBlock) { if (!catchBlock.isTerminating() && catchBlock != outerCatchBlock) {
catchBlock.getBody().add(new WasmBreak(outerCatchBlock)); catchBlock.getBody().add(new WasmBreak(outerCatchBlock));
@ -1262,7 +1259,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
protected abstract WasmExpression peekException(); protected abstract WasmExpression peekException();
protected abstract WasmExpression catchException(); protected abstract void catchException(TextLocation location, List<WasmExpression> target, WasmLocal local);
private void visitMany(List<Statement> statements, List<WasmExpression> target) { private void visitMany(List<Statement> statements, List<WasmExpression> target) {
var oldTarget = resultConsumer; var oldTarget = resultConsumer;
@ -1346,15 +1343,15 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
var callSiteId = generateCallSiteId(expr.getLocation()); var callSiteId = generateCallSiteId(expr.getLocation());
callSiteId.generateRegister(block.getBody(), expr.getLocation()); callSiteId.generateRegister(block.getBody(), expr.getLocation());
block.getBody().add(generateThrowAIOOBE()); generateThrowAIOOBE(expr.getLocation(), block.getBody());
callSiteId.generateThrow(block.getBody(), expr.getLocation()); callSiteId.generateThrow(block.getBody(), expr.getLocation());
result = block; result = block;
} }
protected abstract WasmExpression generateThrowAIOOBE(); protected abstract void generateThrowAIOOBE(TextLocation location, List<WasmExpression> target);
protected abstract WasmExpression generateThrowCCE(); protected abstract void generateThrowCCE(TextLocation location, List<WasmExpression> target);
private static WasmExpression negate(WasmExpression expr) { private static WasmExpression negate(WasmExpression expr) {
if (expr instanceof WasmIntBinary) { if (expr instanceof WasmIntBinary) {

View File

@ -0,0 +1,130 @@
/*
* Copyright 2024 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.wasm.generate.gc;
import org.teavm.backend.wasm.WasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationContext;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmGlobal;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmTag;
import org.teavm.backend.wasm.model.expression.WasmNullConstant;
import org.teavm.backend.wasm.runtime.WasmGCSupport;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.MethodReference;
import org.teavm.model.classes.VirtualTableProvider;
public class WasmGCGenerationContext implements BaseWasmGenerationContext {
private WasmModule module;
private WasmGCClassInfoProvider classInfoProvider;
private WasmGCStandardClasses standardClasses;
private WasmGCStringProvider strings;
private VirtualTableProvider virtualTables;
private WasmGCTypeMapper typeMapper;
private WasmFunction npeMethod;
private WasmFunction aaiobeMethod;
private WasmFunction cceMethod;
private WasmGlobal exceptionGlobal;
public WasmGCGenerationContext(WasmModule module, WasmGCClassInfoProvider classInfoProvider,
WasmGCStandardClasses standardClasses, WasmGCStringProvider strings, VirtualTableProvider virtualTables,
WasmGCTypeMapper typeMapper) {
this.module = module;
this.classInfoProvider = classInfoProvider;
this.standardClasses = standardClasses;
this.strings = strings;
this.virtualTables = virtualTables;
this.typeMapper = typeMapper;
}
public WasmGCClassInfoProvider classInfoProvider() {
return classInfoProvider;
}
public WasmGCStandardClasses standardClasses() {
return standardClasses;
}
public WasmGCStringProvider strings() {
return strings;
}
public VirtualTableProvider virtualTables() {
return virtualTables;
}
public WasmGCTypeMapper typeMapper() {
return typeMapper;
}
@Override
public WasmFunctionRepository functions() {
return null;
}
@Override
public WasmFunctionTypes functionTypes() {
return null;
}
@Override
public WasmTag getExceptionTag() {
return null;
}
@Override
public ClassReaderSource classSource() {
return null;
}
public WasmFunction npeMethod() {
if (npeMethod == null) {
npeMethod = functions().forStaticMethod(new MethodReference(WasmGCSupport.class, "npe",
NullPointerException.class));
}
return npeMethod;
}
public WasmFunction aaiobeMethod() {
if (aaiobeMethod == null) {
aaiobeMethod = functions().forStaticMethod(new MethodReference(WasmGCSupport.class, "aaiobe",
ArrayIndexOutOfBoundsException.class));
}
return aaiobeMethod;
}
public WasmFunction cceMethod() {
if (cceMethod == null) {
cceMethod = functions().forStaticMethod(new MethodReference(WasmGCSupport.class, "cce",
ClassCastException.class));
}
return cceMethod;
}
public WasmGlobal exceptionGlobal() {
if (exceptionGlobal == null) {
var type = classInfoProvider.getClassInfo("java.lang.Throwable").getType();
exceptionGlobal = new WasmGlobal("teavm_thrown_exception", type, new WasmNullConstant(type));
module.globals.add(exceptionGlobal);
}
return exceptionGlobal;
}
}

View File

@ -54,6 +54,7 @@ import org.teavm.model.FieldReference;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.analysis.ClassInitializerInfo;
import org.teavm.model.analysis.ClassMetadataRequirements; import org.teavm.model.analysis.ClassMetadataRequirements;
import org.teavm.model.classes.TagRegistry; import org.teavm.model.classes.TagRegistry;
import org.teavm.model.classes.VirtualTable; import org.teavm.model.classes.VirtualTable;
@ -61,7 +62,7 @@ import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.model.util.ReflectionUtil; import org.teavm.model.util.ReflectionUtil;
public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInitializerContributor { public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInitializerContributor {
public static final int CLASS_FIELD_OFFSET = 0; private static final MethodDescriptor CLINIT_METHOD_DESC = new MethodDescriptor("<clinit>", ValueType.VOID);
private final WasmModule module; private final WasmModule module;
private ClassReaderSource classSource; private ClassReaderSource classSource;
@ -73,6 +74,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private Map<ValueType, WasmGCClassInfo> classInfoMap = new LinkedHashMap<>(); private Map<ValueType, WasmGCClassInfo> classInfoMap = new LinkedHashMap<>();
private ObjectIntMap<FieldReference> fieldIndexes = new ObjectIntHashMap<>(); private ObjectIntMap<FieldReference> fieldIndexes = new ObjectIntHashMap<>();
private ObjectIntMap<MethodReference> methodIndexes = new ObjectIntHashMap<>(); private ObjectIntMap<MethodReference> methodIndexes = new ObjectIntHashMap<>();
private ClassInitializerInfo classInitializerInfo;
public final WasmGCStringPool strings; public final WasmGCStringPool strings;
public final WasmGCStandardClasses standardClasses; public final WasmGCStandardClasses standardClasses;
@ -95,7 +97,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource, public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource,
WasmFunctionTypes functionTypes, TagRegistry tagRegistry, WasmFunctionTypes functionTypes, TagRegistry tagRegistry,
ClassMetadataRequirements metadataRequirements, VirtualTableProvider virtualTables, ClassMetadataRequirements metadataRequirements, VirtualTableProvider virtualTables,
WasmGCFunctionProvider functionProvider, NameProvider names) { WasmGCFunctionProvider functionProvider, NameProvider names,
ClassInitializerInfo classInitializerInfo) {
this.module = module; this.module = module;
this.classSource = classSource; this.classSource = classSource;
this.functionTypes = functionTypes; this.functionTypes = functionTypes;
@ -104,6 +107,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
this.virtualTables = virtualTables; this.virtualTables = virtualTables;
this.functionProvider = functionProvider; this.functionProvider = functionProvider;
this.names = names; this.names = names;
this.classInitializerInfo = classInitializerInfo;
standardClasses = new WasmGCStandardClasses(this); standardClasses = new WasmGCStandardClasses(this);
strings = new WasmGCStringPool(standardClasses, module, functionProvider); strings = new WasmGCStringPool(standardClasses, module, functionProvider);
supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes); supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes);
@ -128,6 +132,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
new WasmFunctionReference(supertypeFunction))); new WasmFunctionReference(supertypeFunction)));
function.getBody().add(setClassField(classInfo, CLASS_FIELD_OFFSET, function.getBody().add(setClassField(classInfo, CLASS_FIELD_OFFSET,
new WasmGetGlobal(classClass.pointer))); new WasmGetGlobal(classClass.pointer)));
if (classInfo.initializerPointer != null) {
var className = ((ValueType.Object) classInfo.getValueType()).getClassName();
var initFunction = functionProvider.getStaticFunction(new MethodReference(className,
CLINIT_METHOD_DESC));
function.getBody().add(new WasmSetGlobal(classInfo.initializerPointer,
new WasmFunctionReference(initFunction)));
}
} }
} }
@ -224,6 +235,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
} }
private void initRegularClass(WasmGCClassInfo classInfo, WasmStructure classStructure, String name) { private void initRegularClass(WasmGCClassInfo classInfo, WasmStructure classStructure, String name) {
var cls = classSource.get(name);
if (classInitializerInfo.isDynamicInitializer(name)) {
if (cls != null && cls.getMethod(CLINIT_METHOD_DESC) != null) {
var clinitType = functionTypes.of(null);
classInfo.initializerPointer = new WasmGlobal(null, clinitType.getReference(),
new WasmNullConstant(clinitType.getReference()));
}
}
classInfo.initializer = target -> { classInfo.initializer = target -> {
var ranges = tagRegistry.getRanges(name); var ranges = tagRegistry.getRanges(name);
int tag = ranges.stream().mapToInt(range -> range.lower).min().orElse(0); int tag = ranges.stream().mapToInt(range -> range.lower).min().orElse(0);
@ -233,7 +252,6 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
var namePtr = strings.getStringConstant(name).global; var namePtr = strings.getStringConstant(name).global;
target.add(setClassField(classInfo, classNameOffset, new WasmGetGlobal(namePtr))); target.add(setClassField(classInfo, classNameOffset, new WasmGetGlobal(namePtr)));
} }
var cls = classSource.get(name);
if (cls != null) { if (cls != null) {
if (metadataReg.simpleName() && cls.getSimpleName() != null) { if (metadataReg.simpleName() && cls.getSimpleName() != null) {
var namePtr = strings.getStringConstant(cls.getSimpleName()).global; var namePtr = strings.getStringConstant(cls.getSimpleName()).global;
@ -334,6 +352,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
); );
} }
@Override
public int getFieldIndex(FieldReference fieldRef) { public int getFieldIndex(FieldReference fieldRef) {
var result = fieldIndexes.getOrDefault(fieldRef, -1); var result = fieldIndexes.getOrDefault(fieldRef, -1);
if (result < 0) { if (result < 0) {
@ -342,6 +361,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
return result; return result;
} }
@Override
public int getVirtualMethodIndex(MethodReference methodRef) { public int getVirtualMethodIndex(MethodReference methodRef) {
var result = methodIndexes.getOrDefault(methodRef, -1); var result = methodIndexes.getOrDefault(methodRef, -1);
if (result < 0) { if (result < 0) {

View File

@ -28,6 +28,7 @@ public class WasmGCClassInfo {
WasmStructure structure; WasmStructure structure;
WasmStructure virtualTableStructure; WasmStructure virtualTableStructure;
WasmGlobal pointer; WasmGlobal pointer;
WasmGlobal initializerPointer;
Consumer<List<WasmExpression>> initializer; Consumer<List<WasmExpression>> initializer;
WasmGCClassInfo(ValueType valueType) { WasmGCClassInfo(ValueType valueType) {
@ -53,4 +54,8 @@ public class WasmGCClassInfo {
public WasmGlobal getPointer() { public WasmGlobal getPointer() {
return pointer; return pointer;
} }
public WasmGlobal getInitializerPointer() {
return initializerPointer;
}
} }

View File

@ -15,11 +15,21 @@
*/ */
package org.teavm.backend.wasm.generate.gc.classes; package org.teavm.backend.wasm.generate.gc.classes;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
public interface WasmGCClassInfoProvider { public interface WasmGCClassInfoProvider {
int CLASS_FIELD_OFFSET = 0;
int MONITOR_FIELD_OFFSET = 1;
int ARRAY_DATA_FIELD_OFFSET = 2;
WasmGCClassInfo getClassInfo(ValueType type); WasmGCClassInfo getClassInfo(ValueType type);
int getFieldIndex(FieldReference fieldRef);
int getVirtualMethodIndex(MethodReference methodRef);
default WasmGCClassInfo getClassInfo(String name) { default WasmGCClassInfo getClassInfo(String name) {
return getClassInfo(ValueType.object(name)); return getClassInfo(ValueType.object(name));
} }

View File

@ -0,0 +1,381 @@
/*
* Copyright 2024 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.wasm.generate.gc.methods;
import java.util.List;
import org.teavm.ast.ArrayType;
import org.teavm.ast.Expr;
import org.teavm.ast.InvocationExpr;
import org.teavm.ast.QualificationExpr;
import org.teavm.ast.SubscriptExpr;
import org.teavm.ast.UnwrapArrayExpr;
import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationVisitor;
import org.teavm.backend.wasm.generate.gc.WasmGCGenerationContext;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.model.WasmArray;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmFunctionType;
import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmStructure;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmArrayGet;
import org.teavm.backend.wasm.model.expression.WasmArrayLength;
import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault;
import org.teavm.backend.wasm.model.expression.WasmArraySet;
import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmCast;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
import org.teavm.backend.wasm.model.expression.WasmNullConstant;
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmStructGet;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.backend.wasm.model.expression.WasmThrow;
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference;
import org.teavm.model.TextLocation;
import org.teavm.model.ValueType;
public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
private WasmGCGenerationContext context;
public WasmGCGenerationVisitor(WasmGCGenerationContext context, WasmFunction function,
int firstVariable, boolean async) {
super(context, function, firstVariable, async);
this.context = context;
}
@Override
protected boolean isManaged() {
return false;
}
@Override
protected boolean isManagedCall(MethodReference method) {
return false;
}
@Override
protected void generateThrowNPE(TextLocation location, List<WasmExpression> target) {
generateThrow(new WasmCall(context.npeMethod()), location, target);
}
@Override
protected void generateThrowAIOOBE(TextLocation location, List<WasmExpression> target) {
generateThrow(new WasmCall(context.aaiobeMethod()), location, target);
}
@Override
protected void generateThrowCCE(TextLocation location, List<WasmExpression> target) {
generateThrow(new WasmCall(context.cceMethod()), location, target);
}
@Override
protected void generateThrow(WasmExpression expression, TextLocation location, List<WasmExpression> target) {
var setThrowable = new WasmSetGlobal(context.exceptionGlobal(), expression);
setThrowable.setLocation(location);
target.add(setThrowable);
var result = new WasmThrow(context.getExceptionTag());
result.setLocation(location);
target.add(result);
}
@Override
public void visit(UnwrapArrayExpr expr) {
accept(expr.getArray());
result = unwrapArray(result);
result.setLocation(expr.getLocation());
}
private WasmExpression unwrapArray(WasmExpression array) {
array.acceptVisitor(typeInference);
var arrayType = (WasmType.CompositeReference) typeInference.getResult();
var arrayStruct = (WasmStructure) arrayType.composite;
return new WasmStructGet(arrayStruct, array, WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET);
}
@Override
protected WasmExpression generateArrayLength(WasmExpression array) {
return new WasmArrayLength(array);
}
@Override
protected WasmExpression storeArrayItem(WasmExpression array, WasmExpression index, WasmExpression value,
ArrayType type) {
array.acceptVisitor(typeInference);
var arrayRefType = (WasmType.CompositeReference) typeInference.getResult();
var arrayType = (WasmArray) arrayRefType.composite;
return new WasmArraySet(arrayType, array, index, value);
}
@Override
protected void storeField(Expr qualified, FieldReference field, Expr value, TextLocation location) {
accept(qualified);
var target = result;
accept(value);
var wasmValue = result;
target.acceptVisitor(typeInference);
var type = (WasmType.CompositeReference) typeInference.getResult();
var struct = (WasmStructure) type.composite;
var fieldIndex = context.classInfoProvider().getFieldIndex(field);
var expr = new WasmStructSet(struct, target, fieldIndex, wasmValue);
expr.setLocation(location);
resultConsumer.add(expr);
}
@Override
protected WasmExpression stringLiteral(String s) {
var stringConstant = context.strings().getStringConstant(s);
return new WasmGetGlobal(stringConstant.global);
}
@Override
protected WasmExpression classLiteral(ValueType type) {
var classConstant = context.classInfoProvider().getClassInfo(type);
return new WasmGetGlobal(classConstant.getPointer());
}
@Override
protected CallSiteIdentifier generateCallSiteId(TextLocation location) {
return new SimpleCallSite();
}
@Override
protected WasmExpression generateVirtualCall(WasmLocal instance, MethodReference method,
List<WasmExpression> arguments) {
var vtable = context.virtualTables().lookup(method.getClassName());
if (vtable != null) {
vtable = vtable.findMethodContainer(method.getDescriptor());
}
if (vtable == null) {
return new WasmUnreachable();
}
method = new MethodReference(vtable.getClassName(), method.getDescriptor());
arguments.get(0).acceptVisitor(typeInference);
var instanceType = (WasmType.CompositeReference) typeInference.getResult();
var instanceStruct = (WasmStructure) instanceType.composite;
WasmExpression classRef = new WasmStructGet(instanceStruct, new WasmGetLocal(instance),
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET);
var index = context.classInfoProvider().getVirtualMethodIndex(method);
var vtableType = (WasmType.CompositeReference) instanceStruct.getFields()
.get(WasmGCClassInfoProvider.CLASS_FIELD_OFFSET).asUnpackedType();
var vtableStruct = (WasmStructure) vtableType.composite;
var expectedVtableStruct = context.classInfoProvider().getClassInfo(vtable.getClassName())
.getVirtualTableStructure();
if (expectedVtableStruct != vtableStruct) {
classRef = new WasmCast(classRef, expectedVtableStruct.getReference());
}
var functionRef = new WasmStructGet(expectedVtableStruct, classRef, index);
var functionTypeRef = (WasmType.CompositeReference) expectedVtableStruct.getFields()
.get(index).asUnpackedType();
var invoke = new WasmCallReference(functionRef, (WasmFunctionType) functionTypeRef.composite);
invoke.getArguments().addAll(arguments);
return invoke;
}
@Override
protected void allocateObject(String className, TextLocation location, WasmLocal local,
List<WasmExpression> target) {
var classInfo = context.classInfoProvider().getClassInfo(className);
var block = new WasmBlock(false);
block.setType(classInfo.getType());
var targetVar = local;
if (targetVar == null) {
targetVar = tempVars.acquire(classInfo.getType());
}
var structNew = new WasmSetLocal(targetVar, new WasmStructNewDefault(classInfo.getStructure()));
structNew.setLocation(location);
target.add(structNew);
var initClassField = new WasmStructSet(classInfo.getStructure(), new WasmGetLocal(targetVar),
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET, new WasmGetGlobal(classInfo.getPointer()));
initClassField.setLocation(location);
target.add(initClassField);
if (local == null) {
var getLocal = new WasmGetLocal(targetVar);
getLocal.setLocation(location);
target.add(getLocal);
tempVars.release(targetVar);
}
}
@Override
protected void allocateArray(ValueType itemType, WasmExpression length, TextLocation location, WasmLocal local,
List<WasmExpression> target) {
var classInfo = context.classInfoProvider().getClassInfo(ValueType.arrayOf(itemType));
var block = new WasmBlock(false);
block.setType(classInfo.getType());
var targetVar = local;
if (targetVar == null) {
targetVar = tempVars.acquire(classInfo.getType());
}
var structNew = new WasmSetLocal(targetVar, new WasmStructNewDefault(classInfo.getStructure()));
structNew.setLocation(location);
target.add(structNew);
var initClassField = new WasmStructSet(classInfo.getStructure(), new WasmGetLocal(targetVar),
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET, new WasmGetGlobal(classInfo.getPointer()));
initClassField.setLocation(location);
target.add(initClassField);
var wasmArrayType = (WasmType.CompositeReference) classInfo.getStructure().getFields()
.get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET)
.asUnpackedType();
var wasmArray = (WasmArray) wasmArrayType.composite;
var initArrayField = new WasmStructSet(
classInfo.getStructure(),
new WasmGetLocal(targetVar),
WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET,
new WasmArrayNewDefault(wasmArray, length)
);
initArrayField.setLocation(location);
target.add(initArrayField);
if (local == null) {
var getLocal = new WasmGetLocal(targetVar);
getLocal.setLocation(location);
target.add(getLocal);
tempVars.release(targetVar);
}
}
@Override
protected WasmExpression allocateMultiArray(List<WasmExpression> target, ValueType itemType,
List<WasmExpression> dimensions, TextLocation location) {
return null;
}
@Override
protected WasmExpression generateInstanceOf(WasmExpression expression, ValueType type) {
context.classInfoProvider().getClassInfo(type);
var supertypeCall = new WasmCall(context.functions().forSupertype(type));
var classRef = new WasmStructGet(
context.standardClasses().objectClass().getStructure(),
expression,
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET
);
supertypeCall.getArguments().add(classRef);
return supertypeCall;
}
@Override
protected WasmExpression generateCast(WasmExpression value, WasmType targetType) {
return new WasmCast(value, (WasmType.Reference) targetType);
}
@Override
protected boolean needsClassInitializer(String className) {
return context.classInfoProvider().getClassInfo(className).getInitializerPointer() != null;
}
@Override
protected WasmExpression generateClassInitializer(String className, TextLocation location) {
var pointer = context.classInfoProvider().getClassInfo(className).getInitializerPointer();
var result = new WasmCallReference(new WasmGetGlobal(pointer),
context.functionTypes().of(null));
result.setLocation(location);
return result;
}
@Override
protected WasmExpression peekException() {
return new WasmGetGlobal(context.exceptionGlobal());
}
@Override
protected void catchException(TextLocation location, List<WasmExpression> target, WasmLocal local) {
var type = context.classInfoProvider().getClassInfo("java.lang.Throwable").getType();
if (local != null) {
var save = new WasmSetLocal(local, new WasmGetGlobal(context.exceptionGlobal()));
save.setLocation(location);
target.add(save);
}
var erase = new WasmSetGlobal(context.exceptionGlobal(), new WasmNullConstant(type));
erase.setLocation(location);
target.add(erase);
}
@Override
protected WasmType mapType(ValueType type) {
return context.typeMapper().mapType(type).asUnpackedType();
}
@Override
public void visit(SubscriptExpr expr) {
accept(expr.getArray());
var arrayData = unwrapArray(result);
arrayData.acceptVisitor(typeInference);
var arrayTypeRef = (WasmType.CompositeReference) typeInference.getResult();
var arrayType = (WasmArray) arrayTypeRef.composite;
accept(expr.getIndex());
var index = result;
result = new WasmArrayGet(arrayType, arrayData, index);
result.setLocation(expr.getLocation());
}
@Override
public void visit(InvocationExpr expr) {
result = invocation(expr, null, false);
}
@Override
public void visit(QualificationExpr expr) {
accept(expr.getQualified());
var target = result;
target.acceptVisitor(typeInference);
var type = (WasmType.CompositeReference) typeInference.getResult();
var struct = (WasmStructure) type.composite;
var fieldIndex = context.classInfoProvider().getFieldIndex(expr.getField());
result = new WasmStructGet(struct, target, fieldIndex);
result.setLocation(expr.getLocation());
}
private class SimpleCallSite extends CallSiteIdentifier {
@Override
public void generateRegister(List<WasmExpression> consumer, TextLocation location) {
}
@Override
public void checkHandlerId(List<WasmExpression> target, TextLocation location) {
}
@Override
public void generateThrow(List<WasmExpression> target, TextLocation location) {
}
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2024 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.wasm.runtime;
public class WasmGCSupport {
private WasmGCSupport() {
}
public static NullPointerException npe() {
return new NullPointerException();
}
public static ArrayIndexOutOfBoundsException aiiobe() {
return new ArrayIndexOutOfBoundsException();
}
public static ClassCastException cce() {
return new ClassCastException();
}
}

View File

@ -82,6 +82,15 @@ public abstract class BaseTypeInference<T> {
visitor.graphBuilder.addEdge(incoming.getValue().getIndex(), phi.getReceiver().getIndex()); visitor.graphBuilder.addEdge(incoming.getValue().getIndex(), phi.getReceiver().getIndex());
} }
} }
for (var tryCatch : block.getTryCatchBlocks()) {
var exceptionVar = tryCatch.getHandler().getExceptionVariable();
if (exceptionVar != null) {
var exceptionType = tryCatch.getExceptionType() != null
? ValueType.object(tryCatch.getExceptionType())
: ValueType.object("java.lang.Throwable");
visitor.type(exceptionVar, exceptionType);
}
}
} }
graph = visitor.graphBuilder.build(); graph = visitor.graphBuilder.build();
arrayGraph = visitor.arrayGraphBuilder.build(); arrayGraph = visitor.arrayGraphBuilder.build();

View File

@ -0,0 +1,52 @@
/*
* Copyright 2024 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 org.teavm.model.MethodReference;
import org.teavm.model.ProgramReader;
public class DefaultVariableCategoryProvider implements VariableCategoryProvider {
@Override
public Object[] getCategories(ProgramReader program, MethodReference method) {
TypeInferer inferer = new TypeInferer();
inferer.inferTypes(program, method);
var categories = new Object[program.variableCount()];
for (int i = 0; i < program.variableCount(); ++i) {
categories[i] = getCategory(inferer.typeOf(i));
}
return categories;
}
private int getCategory(VariableType type) {
if (type == null) {
return 255;
}
switch (type) {
case INT:
return 0;
case LONG:
return 1;
case FLOAT:
return 2;
case DOUBLE:
return 3;
case OBJECT:
return 4;
default:
return 5;
}
}
}

View File

@ -16,18 +16,20 @@
package org.teavm.model.util; package org.teavm.model.util;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet; import java.util.BitSet;
import java.util.List; import java.util.List;
import org.teavm.common.Graph; import org.teavm.common.Graph;
import org.teavm.common.IntegerArray;
public class GraphColorer { public class GraphColorer {
public void colorize(Graph graph, int[] colors) { public void colorize(Graph graph, int[] colors) {
colorize(graph, colors, new int[graph.size()], new String[graph.size()]); var categories = new Object[graph.size()];
Arrays.fill(categories, new Object());
colorize(graph, colors, categories, new String[graph.size()]);
} }
public void colorize(Graph graph, int[] colors, int[] categories, String[] names) { public void colorize(Graph graph, int[] colors, Object[] categories, String[] names) {
IntegerArray colorCategories = new IntegerArray(graph.size()); var colorCategories = new ArrayList<>(graph.size());
List<String> colorNames = new ArrayList<>(); List<String> colorNames = new ArrayList<>();
for (int i = 0; i < colors.length; ++i) { for (int i = 0; i < colors.length; ++i) {
int color = colors[i]; int color = colors[i];
@ -59,12 +61,12 @@ public class GraphColorer {
while (true) { while (true) {
color = usedColors.nextClearBit(color); color = usedColors.nextClearBit(color);
while (colorCategories.size() <= color) { while (colorCategories.size() <= color) {
colorCategories.add(-1); colorCategories.add(null);
colorNames.add(null); colorNames.add(null);
} }
int category = colorCategories.get(color); var category = colorCategories.get(color);
String name = colorNames.get(color); String name = colorNames.get(color);
if ((category < 0 || category == categories[v]) if ((category == null || category.equals(categories[v]))
&& (name == null || names[v] == null || name.equals(names[v]))) { && (name == null || names[v] == null || name.equals(names[v]))) {
colors[v] = color; colors[v] = color;
colorCategories.set(color, categories[v]); colorCategories.set(color, categories[v]);

View File

@ -38,6 +38,12 @@ import org.teavm.model.instructions.AssignInstruction;
import org.teavm.model.instructions.JumpInstruction; import org.teavm.model.instructions.JumpInstruction;
public class RegisterAllocator { public class RegisterAllocator {
private VariableCategoryProvider variableCategoryProvider;
public RegisterAllocator(VariableCategoryProvider variableCategoryProvider) {
this.variableCategoryProvider = variableCategoryProvider;
}
public void allocateRegisters(MethodReference method, Program program, boolean debuggerFriendly) { public void allocateRegisters(MethodReference method, Program program, boolean debuggerFriendly) {
insertPhiArgumentsCopies(program); insertPhiArgumentsCopies(program);
InterferenceGraphBuilder interferenceBuilder = new InterferenceGraphBuilder(); InterferenceGraphBuilder interferenceBuilder = new InterferenceGraphBuilder();
@ -62,7 +68,7 @@ public class RegisterAllocator {
for (int cls : classArray) { for (int cls : classArray) {
maxClass = Math.max(maxClass, cls + 1); maxClass = Math.max(maxClass, cls + 1);
} }
int[] categories = getVariableCategories(program, method); var categories = variableCategoryProvider.getCategories(program, method);
String[] names = getVariableNames(program, debuggerFriendly); String[] names = getVariableNames(program, debuggerFriendly);
colorer.colorize(MutableGraphNode.toGraph(interferenceGraph), colors, categories, names); colorer.colorize(MutableGraphNode.toGraph(interferenceGraph), colors, categories, names);
@ -91,36 +97,6 @@ public class RegisterAllocator {
} }
} }
private int[] getVariableCategories(ProgramReader program, MethodReference method) {
TypeInferer inferer = new TypeInferer();
inferer.inferTypes(program, method);
int[] categories = new int[program.variableCount()];
for (int i = 0; i < program.variableCount(); ++i) {
categories[i] = getCategory(inferer.typeOf(i));
}
return categories;
}
private int getCategory(VariableType type) {
if (type == null) {
return 255;
}
switch (type) {
case INT:
return 0;
case LONG:
return 1;
case FLOAT:
return 2;
case DOUBLE:
return 3;
case OBJECT:
return 4;
default:
return 5;
}
}
private String[] getVariableNames(ProgramReader program, boolean debuggerFriendly) { private String[] getVariableNames(ProgramReader program, boolean debuggerFriendly) {
String[] names = new String[program.variableCount()]; String[] names = new String[program.variableCount()];
for (int i = 0; i < names.length; ++i) { for (int i = 0; i < names.length; ++i) {

View File

@ -0,0 +1,23 @@
/*
* Copyright 2024 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 org.teavm.model.MethodReference;
import org.teavm.model.ProgramReader;
public interface VariableCategoryProvider {
Object[] getCategories(ProgramReader program, MethodReference method);
}

View File

@ -762,8 +762,9 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
} while (changed); } while (changed);
target.afterOptimizations(optimizedProgram, method); target.afterOptimizations(optimizedProgram, method);
if (target.requiresRegisterAllocation()) { var categoryProvider = target.variableCategoryProvider();
RegisterAllocator allocator = new RegisterAllocator(); if (categoryProvider != null) {
var allocator = new RegisterAllocator(categoryProvider);
allocator.allocateRegisters(method.getReference(), optimizedProgram, allocator.allocateRegisters(method.getReference(), optimizedProgram,
optimizationLevel == TeaVMOptimizationLevel.SIMPLE); optimizationLevel == TeaVMOptimizationLevel.SIMPLE);
} }

View File

@ -27,6 +27,7 @@ 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.optimization.InliningFilterFactory; import org.teavm.model.optimization.InliningFilterFactory;
import org.teavm.model.util.VariableCategoryProvider;
import org.teavm.vm.spi.TeaVMHostExtension; import org.teavm.vm.spi.TeaVMHostExtension;
public interface TeaVMTarget { public interface TeaVMTarget {
@ -38,7 +39,7 @@ public interface TeaVMTarget {
List<TeaVMHostExtension> getHostExtensions(); List<TeaVMHostExtension> getHostExtensions();
boolean requiresRegisterAllocation(); VariableCategoryProvider variableCategoryProvider();
void contributeDependencies(DependencyAnalyzer dependencyAnalyzer); void contributeDependencies(DependencyAnalyzer dependencyAnalyzer);