mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
wasm gc: fix generation issues, get first version that produces module that passes validation
This commit is contained in:
parent
a8d97ad387
commit
54dc7fe5f8
|
@ -57,6 +57,7 @@ public class WasmGCModuleGenerator {
|
|||
var mainFunction = declarationsGenerator.functions().forStaticMethod(new MethodReference(entryPoint,
|
||||
"main", ValueType.parse(String[].class), ValueType.VOID));
|
||||
var mainFunctionCaller = new WasmFunction(declarationsGenerator.functionTypes.of(null));
|
||||
declarationsGenerator.module.functions.add(mainFunctionCaller);
|
||||
mainFunctionCaller.getBody().add(callInitializer());
|
||||
|
||||
var tempVars = new TemporaryVariablePool(mainFunctionCaller);
|
||||
|
@ -80,6 +81,7 @@ public class WasmGCModuleGenerator {
|
|||
}
|
||||
var functionType = declarationsGenerator.functionTypes.of(null);
|
||||
initializer = new WasmFunction(functionType);
|
||||
initializer.setReferenced(true);
|
||||
declarationsGenerator.module.functions.add(initializer);
|
||||
initializerRef = new WasmGlobal("teavm_initializer", functionType.getReference(),
|
||||
new WasmFunctionReference(initializer));
|
||||
|
|
|
@ -123,10 +123,25 @@ public class WasmGCTarget implements TeaVMTarget {
|
|||
mainFunction.setExportName(controller.getEntryPointName());
|
||||
mainFunction.setName(controller.getEntryPointName());
|
||||
moduleGenerator.generate();
|
||||
adjustModuleMemory(module);
|
||||
|
||||
emitWasmFile(module, buildTarget, outputName);
|
||||
}
|
||||
|
||||
private void adjustModuleMemory(WasmModule module) {
|
||||
var memorySize = 0;
|
||||
for (var segment : module.getSegments()) {
|
||||
memorySize = Math.max(memorySize, segment.getOffset() + segment.getLength());
|
||||
}
|
||||
if (memorySize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var pages = (memorySize - 1) / WasmHeap.PAGE_SIZE + 1;
|
||||
module.setMinMemorySize(pages);
|
||||
module.setMaxMemorySize(pages);
|
||||
}
|
||||
|
||||
private void emitWasmFile(WasmModule module, BuildTarget buildTarget, String outputName) throws IOException {
|
||||
var binaryWriter = new WasmBinaryWriter();
|
||||
var binaryRenderer = new WasmBinaryRenderer(binaryWriter, WasmBinaryVersion.V_0x1, obfuscated,
|
||||
|
|
|
@ -22,8 +22,9 @@ import org.teavm.model.ValueType;
|
|||
import org.teavm.model.analysis.BaseTypeInference;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
|
||||
public class PreciseTypeInference extends BaseTypeInference<ValueType> {
|
||||
public static final ValueType OBJECT_TYPE = ValueType.object("java.lang.Object");
|
||||
public class PreciseTypeInference extends BaseTypeInference<PreciseValueType> {
|
||||
public static final PreciseValueType OBJECT_TYPE = new PreciseValueType(ValueType.object("java.lang.Object"),
|
||||
false);
|
||||
private ClassHierarchy hierarchy;
|
||||
private WasmGCMethodReturnTypes returnTypes;
|
||||
|
||||
|
@ -35,42 +36,42 @@ public class PreciseTypeInference extends BaseTypeInference<ValueType> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ValueType mapType(ValueType type) {
|
||||
return type;
|
||||
protected PreciseValueType mapType(ValueType type) {
|
||||
return new PreciseValueType(type, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ValueType nullType() {
|
||||
protected PreciseValueType nullType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ValueType merge(ValueType a, ValueType b) {
|
||||
protected PreciseValueType merge(PreciseValueType a, PreciseValueType b) {
|
||||
if (a == null) {
|
||||
return b;
|
||||
} else if (b == null) {
|
||||
return a;
|
||||
} else {
|
||||
if (a instanceof ValueType.Primitive && b instanceof ValueType.Primitive) {
|
||||
if (a != b) {
|
||||
if (a.valueType instanceof ValueType.Primitive && b.valueType instanceof ValueType.Primitive) {
|
||||
if (a.valueType != b.valueType) {
|
||||
return OBJECT_TYPE;
|
||||
} else {
|
||||
return a;
|
||||
}
|
||||
} else if (a instanceof ValueType.Array) {
|
||||
if (b instanceof ValueType.Array) {
|
||||
var p = ((ValueType.Array) a).getItemType();
|
||||
var q = ((ValueType.Array) b).getItemType();
|
||||
return ValueType.arrayOf(merge(p, q));
|
||||
} else if (a.valueType instanceof ValueType.Array) {
|
||||
if (b.valueType instanceof ValueType.Array) {
|
||||
var p = new PreciseValueType(((ValueType.Array) a.valueType).getItemType(), false);
|
||||
var q = new PreciseValueType(((ValueType.Array) b.valueType).getItemType(), false);
|
||||
return new PreciseValueType(ValueType.arrayOf(merge(p, q).valueType), a.isArrayUnwrap);
|
||||
} else {
|
||||
return OBJECT_TYPE;
|
||||
}
|
||||
} else if (b instanceof ValueType.Array) {
|
||||
} else if (b.valueType instanceof ValueType.Array) {
|
||||
return OBJECT_TYPE;
|
||||
} else if (a instanceof ValueType.Object) {
|
||||
if (b instanceof ValueType.Object) {
|
||||
var p = ((ValueType.Object) a).getClassName();
|
||||
var q = ((ValueType.Object) b).getClassName();
|
||||
} else if (a.valueType instanceof ValueType.Object) {
|
||||
if (b.valueType instanceof ValueType.Object) {
|
||||
var p = ((ValueType.Object) a.valueType).getClassName();
|
||||
var q = ((ValueType.Object) b.valueType).getClassName();
|
||||
if (p.equals(q)) {
|
||||
return a;
|
||||
}
|
||||
|
@ -87,7 +88,8 @@ public class PreciseTypeInference extends BaseTypeInference<ValueType> {
|
|||
} else if (hierarchy.isSuperType(q, p, false)) {
|
||||
return b;
|
||||
}
|
||||
return ValueType.object(WasmGCUtil.findCommonSuperclass(hierarchy, first, second));
|
||||
var result = ValueType.object(WasmGCUtil.findCommonSuperclass(hierarchy, first, second));
|
||||
return new PreciseValueType(result, false);
|
||||
} else {
|
||||
return OBJECT_TYPE;
|
||||
}
|
||||
|
@ -97,15 +99,20 @@ public class PreciseTypeInference extends BaseTypeInference<ValueType> {
|
|||
}
|
||||
}
|
||||
@Override
|
||||
protected ValueType elementType(ValueType valueType) {
|
||||
return ((ValueType.Array) valueType).getItemType();
|
||||
protected PreciseValueType elementType(PreciseValueType valueType) {
|
||||
return new PreciseValueType(((ValueType.Array) valueType.valueType).getItemType(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ValueType methodReturnType(InvocationType invocationType, MethodReference methodRef) {
|
||||
protected PreciseValueType methodReturnType(InvocationType invocationType, MethodReference methodRef) {
|
||||
if (invocationType == InvocationType.SPECIAL) {
|
||||
return returnTypes.returnTypeOf(methodRef);
|
||||
return new PreciseValueType(returnTypes.returnTypeOf(methodRef), false);
|
||||
}
|
||||
return super.methodReturnType(invocationType, methodRef);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PreciseValueType arrayUnwrapType(PreciseValueType type) {
|
||||
return new PreciseValueType(type.valueType, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.gc;
|
||||
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
public class PreciseValueType {
|
||||
public final ValueType valueType;
|
||||
public final boolean isArrayUnwrap;
|
||||
|
||||
public PreciseValueType(ValueType valueType, boolean isArrayUnwrap) {
|
||||
this.valueType = valueType;
|
||||
this.isArrayUnwrap = isArrayUnwrap;
|
||||
}
|
||||
}
|
|
@ -17,7 +17,6 @@ package org.teavm.backend.wasm.gc;
|
|||
|
||||
import java.util.Arrays;
|
||||
import org.teavm.backend.wasm.WasmRuntime;
|
||||
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool;
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.dependency.DependencyAnalyzer;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
@ -52,12 +51,14 @@ public class WasmGCDependencies {
|
|||
}
|
||||
|
||||
private void contributeExceptionUtils() {
|
||||
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "npe", NullPointerException.class));
|
||||
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "aiiobe", ArrayIndexOutOfBoundsException.class));
|
||||
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "cce", ClassCastException.class));
|
||||
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "npe", NullPointerException.class))
|
||||
.use();
|
||||
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "aiiobe", ArrayIndexOutOfBoundsException.class))
|
||||
.use();
|
||||
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "cce", ClassCastException.class)).use();
|
||||
}
|
||||
|
||||
private void contributeInitializerUtils() {
|
||||
analyzer.linkMethod(new MethodReference(WasmGCStringPool.class, "nextCharArray", char[].class));
|
||||
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "nextCharArray", char[].class)).use();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ public class WasmGCVariableCategoryProvider implements VariableCategoryProvider
|
|||
@Override
|
||||
public Object[] getCategories(Program program, MethodReference method) {
|
||||
inference = new PreciseTypeInference(program, method, hierarchy, returnTypes);
|
||||
inference.setPhisSkipped(true);
|
||||
var result = new Object[program.variableCount()];
|
||||
for (int i = 0; i < program.variableCount(); ++i) {
|
||||
var type = inference.typeOf(program.variableAt(i));
|
||||
|
|
|
@ -95,7 +95,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
|
||||
private int classTagOffset;
|
||||
private int classFlagsOffset;
|
||||
private int classNameOffset;
|
||||
private int classNameOffset = -1;
|
||||
private int classParentOffset;
|
||||
private int classArrayOffset;
|
||||
private int classArrayItemOffset;
|
||||
|
@ -141,7 +141,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
@Override
|
||||
public void contributeToInitializerDefinitions(WasmFunction function) {
|
||||
for (var classInfo : classInfoMap.values()) {
|
||||
var newStruct = new WasmStructNewDefault(standardClasses.classClass().getStructure());
|
||||
var classInstanceType = classInfo.virtualTableStructure != null
|
||||
? classInfo.virtualTableStructure
|
||||
: standardClasses.classClass().getStructure();
|
||||
var newStruct = new WasmStructNewDefault(classInstanceType);
|
||||
function.getBody().add(new WasmSetGlobal(classInfo.pointer, newStruct));
|
||||
}
|
||||
}
|
||||
|
@ -153,6 +156,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
initializerFunctionStatements.clear();
|
||||
for (var classInfo : classInfoMap.values()) {
|
||||
var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType());
|
||||
supertypeFunction.setReferenced(true);
|
||||
function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset,
|
||||
new WasmFunctionReference(supertypeFunction)));
|
||||
function.getBody().add(setClassField(classInfo, CLASS_FIELD_OFFSET,
|
||||
|
@ -161,6 +165,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
var className = ((ValueType.Object) classInfo.getValueType()).getClassName();
|
||||
var initFunction = functionProvider.forStaticMethod(new MethodReference(className,
|
||||
CLINIT_METHOD_DESC));
|
||||
initFunction.setReferenced(true);
|
||||
function.getBody().add(new WasmSetGlobal(classInfo.initializerPointer,
|
||||
new WasmFunctionReference(initFunction)));
|
||||
}
|
||||
|
@ -191,7 +196,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
classInfo.structure.setSupertype(standardClasses.objectClass().structure);
|
||||
}
|
||||
module.types.add(classInfo.structure);
|
||||
fillFields(classInfo.structure.getFields(), type);
|
||||
fillFields(classInfo, type);
|
||||
}
|
||||
var pointerName = names.forClassInstance(type);
|
||||
var classStructure = type instanceof ValueType.Object
|
||||
|
@ -331,6 +336,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
}
|
||||
wrapperFunction.getLocalVariables().addAll(List.of(params));
|
||||
}
|
||||
function.setReferenced(true);
|
||||
var ref = new WasmFunctionReference(function);
|
||||
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), index, ref));
|
||||
}
|
||||
|
@ -439,13 +445,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
return result;
|
||||
}
|
||||
|
||||
private void fillFields(List<WasmStorageType> fields, ValueType type) {
|
||||
private void fillFields(WasmGCClassInfo classInfo, ValueType type) {
|
||||
var fields = classInfo.structure.getFields();
|
||||
fields.add(standardClasses.classClass().getType().asStorage());
|
||||
fields.add(WasmType.Reference.ANY.asStorage());
|
||||
if (type instanceof ValueType.Object) {
|
||||
fillClassFields(fields, ((ValueType.Object) type).getClassName());
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
fillArrayField(fields, ((ValueType.Array) type).getItemType());
|
||||
fillArrayField(classInfo, ((ValueType.Array) type).getItemType());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -487,14 +494,15 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
classSupertypeFunctionOffset = fields.size();
|
||||
fields.add(supertypeGenerator.getFunctionType().getReference().asStorage());
|
||||
virtualTableFieldOffset = fields.size();
|
||||
classNameOffset = fieldIndexes.get(new FieldReference(className, "name"));
|
||||
classNameOffset = fieldIndexes.getOrDefault(new FieldReference(className, "name"), -1);
|
||||
}
|
||||
}
|
||||
|
||||
private void fillArrayField(List<WasmStorageType> fields, ValueType elementType) {
|
||||
private void fillArrayField(WasmGCClassInfo classInfo, ValueType elementType) {
|
||||
var wasmArray = new WasmArray(null, () -> typeMapper.mapStorageType(elementType));
|
||||
module.types.add(wasmArray);
|
||||
fields.add(wasmArray.getReference().asStorage());
|
||||
classInfo.structure.getFields().add(wasmArray.getReference().asStorage());
|
||||
classInfo.array = wasmArray;
|
||||
}
|
||||
|
||||
private WasmFunction getCreatePrimitiveClassFunction() {
|
||||
|
@ -512,11 +520,11 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
WasmType.INT32
|
||||
);
|
||||
var function = new WasmFunction(functionType);
|
||||
function.setName("_teavm_fill_primitive_class_");
|
||||
function.setName("teavm_fill_primitive_class");
|
||||
module.functions.add(function);
|
||||
|
||||
var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target");
|
||||
var nameVar = new WasmLocal(standardClasses.objectClass().getType(), "name");
|
||||
var nameVar = new WasmLocal(standardClasses.stringClass().getType(), "name");
|
||||
var kindVar = new WasmLocal(WasmType.INT32, "kind");
|
||||
function.add(targetVar);
|
||||
function.add(nameVar);
|
||||
|
@ -540,12 +548,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
classFlagsOffset,
|
||||
flagsExpr
|
||||
));
|
||||
function.getBody().add(new WasmStructSet(
|
||||
standardClasses.classClass().getStructure(),
|
||||
new WasmGetLocal(targetVar),
|
||||
classNameOffset,
|
||||
new WasmGetLocal(nameVar)
|
||||
));
|
||||
if (classNameOffset >= 0) {
|
||||
function.getBody().add(new WasmStructSet(
|
||||
standardClasses.classClass().getStructure(),
|
||||
new WasmGetLocal(targetVar),
|
||||
classNameOffset,
|
||||
new WasmGetLocal(nameVar)
|
||||
));
|
||||
}
|
||||
function.getBody().add(new WasmStructSet(
|
||||
standardClasses.classClass().getStructure(),
|
||||
new WasmGetLocal(targetVar),
|
||||
|
@ -559,7 +569,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
if (createArrayClassFunction == null) {
|
||||
createArrayClassFunction = createCreateArrayClassFunction();
|
||||
}
|
||||
return createCreateArrayClassFunction();
|
||||
return createArrayClassFunction;
|
||||
}
|
||||
|
||||
private WasmFunction createCreateArrayClassFunction() {
|
||||
|
@ -570,7 +580,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
);
|
||||
var function = new WasmFunction(functionType);
|
||||
module.functions.add(function);
|
||||
function.setName("_teavm_fill_array_class_");
|
||||
function.setName("teavm_fill_array_class");
|
||||
|
||||
var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target");
|
||||
var itemVar = new WasmLocal(standardClasses.classClass().getType(), "item");
|
||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.backend.wasm.generate.gc.classes;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import org.teavm.backend.wasm.model.WasmArray;
|
||||
import org.teavm.backend.wasm.model.WasmGlobal;
|
||||
import org.teavm.backend.wasm.model.WasmStructure;
|
||||
import org.teavm.backend.wasm.model.WasmType;
|
||||
|
@ -26,6 +27,7 @@ import org.teavm.model.ValueType;
|
|||
public class WasmGCClassInfo {
|
||||
private ValueType valueType;
|
||||
WasmStructure structure;
|
||||
WasmArray array;
|
||||
WasmStructure virtualTableStructure;
|
||||
WasmGlobal pointer;
|
||||
WasmGlobal initializerPointer;
|
||||
|
@ -43,6 +45,10 @@ public class WasmGCClassInfo {
|
|||
return structure;
|
||||
}
|
||||
|
||||
public WasmArray getArray() {
|
||||
return array;
|
||||
}
|
||||
|
||||
public WasmStructure getVirtualTableStructure() {
|
||||
return virtualTableStructure;
|
||||
}
|
||||
|
|
|
@ -35,8 +35,8 @@ import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
|||
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntUnary;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation;
|
||||
import org.teavm.backend.wasm.model.expression.WasmNullConstant;
|
||||
import org.teavm.backend.wasm.model.expression.WasmReferencesEqual;
|
||||
import org.teavm.backend.wasm.model.expression.WasmReturn;
|
||||
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStructGet;
|
||||
|
@ -79,27 +79,27 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction
|
|||
private WasmFunction generateIsSupertypeFunction(ValueType type) {
|
||||
var function = new WasmFunction(getFunctionType());
|
||||
function.setName(nameProvider.forSupertypeFunction(type));
|
||||
var subtypeVar = new WasmLocal(WasmType.INT32, "subtype");
|
||||
var subtypeVar = new WasmLocal(classGenerator.standardClasses.classClass().getType(), "subtype");
|
||||
function.add(subtypeVar);
|
||||
module.functions.add(function);
|
||||
|
||||
if (type instanceof ValueType.Object) {
|
||||
var className = ((ValueType.Object) type).getClassName();
|
||||
generateIsClass(subtypeVar, className, function.getBody());
|
||||
generateIsClass(subtypeVar, className, function);
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
ValueType itemType = ((ValueType.Array) type).getItemType();
|
||||
generateIsArray(subtypeVar, itemType, function.getBody());
|
||||
} else {
|
||||
var expected = classGenerator.getClassInfo(type).pointer;
|
||||
var condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ,
|
||||
new WasmGetLocal(subtypeVar), new WasmGetGlobal(expected));
|
||||
var condition = new WasmReferencesEqual(new WasmGetLocal(subtypeVar), new WasmGetGlobal(expected));
|
||||
function.getBody().add(new WasmReturn(condition));
|
||||
}
|
||||
|
||||
return function;
|
||||
}
|
||||
|
||||
private void generateIsClass(WasmLocal subtypeVar, String className, List<WasmExpression> body) {
|
||||
private void generateIsClass(WasmLocal subtypeVar, String className, WasmFunction function) {
|
||||
var body = function.getBody();
|
||||
var ranges = tagRegistry.getRanges(className);
|
||||
if (ranges.isEmpty()) {
|
||||
body.add(new WasmReturn(new WasmInt32Constant(0)));
|
||||
|
@ -108,8 +108,10 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction
|
|||
|
||||
int tagOffset = classGenerator.getClassTagOffset();
|
||||
|
||||
var tagVar = new WasmLocal(WasmType.INT32, "tag");
|
||||
function.add(tagVar);
|
||||
var tagExpression = getClassField(new WasmGetLocal(subtypeVar), tagOffset);
|
||||
body.add(new WasmSetLocal(subtypeVar, tagExpression));
|
||||
body.add(new WasmSetLocal(tagVar, tagExpression));
|
||||
|
||||
ranges.sort(Comparator.comparingInt(range -> range.lower));
|
||||
|
||||
|
@ -117,13 +119,13 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction
|
|||
int upper = ranges.get(ranges.size() - 1).upper;
|
||||
|
||||
var lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED,
|
||||
new WasmGetLocal(subtypeVar), new WasmInt32Constant(lower));
|
||||
new WasmGetLocal(tagVar), new WasmInt32Constant(lower));
|
||||
var testLower = new WasmConditional(lowerCondition);
|
||||
testLower.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0)));
|
||||
body.add(testLower);
|
||||
|
||||
var upperCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GE_SIGNED,
|
||||
new WasmGetLocal(subtypeVar), new WasmInt32Constant(upper));
|
||||
new WasmGetLocal(tagVar), new WasmInt32Constant(upper));
|
||||
var testUpper = new WasmConditional(upperCondition);
|
||||
testUpper.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0)));
|
||||
body.add(testUpper);
|
||||
|
@ -133,12 +135,12 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction
|
|||
int upperHole = ranges.get(i).lower;
|
||||
|
||||
lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GE_SIGNED,
|
||||
new WasmGetLocal(subtypeVar), new WasmInt32Constant(lowerHole));
|
||||
new WasmGetLocal(tagVar), new WasmInt32Constant(lowerHole));
|
||||
testLower = new WasmConditional(lowerCondition);
|
||||
body.add(testLower);
|
||||
|
||||
upperCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED,
|
||||
new WasmGetLocal(subtypeVar), new WasmInt32Constant(upperHole));
|
||||
new WasmGetLocal(tagVar), new WasmInt32Constant(upperHole));
|
||||
testUpper = new WasmConditional(upperCondition);
|
||||
testUpper.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0)));
|
||||
|
||||
|
@ -154,8 +156,8 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction
|
|||
var itemExpression = getClassField(new WasmGetLocal(subtypeVar), itemOffset);
|
||||
body.add(new WasmSetLocal(subtypeVar, itemExpression));
|
||||
|
||||
var itemTest = new WasmConditional(new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ,
|
||||
new WasmGetLocal(subtypeVar)));
|
||||
var itemTest = new WasmConditional(new WasmReferencesEqual(new WasmGetLocal(subtypeVar),
|
||||
new WasmNullConstant(WasmType.Reference.STRUCT)));
|
||||
itemTest.setType(WasmType.INT32);
|
||||
itemTest.getThenBlock().getBody().add(new WasmInt32Constant(0));
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ import org.teavm.backend.wasm.model.expression.WasmNullConstant;
|
|||
import org.teavm.backend.wasm.model.expression.WasmReferencesEqual;
|
||||
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmSignedType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStructGet;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStructSet;
|
||||
|
@ -340,7 +341,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
|||
@Override
|
||||
public void visit(SubscriptExpr expr) {
|
||||
accept(expr.getArray());
|
||||
var arrayData = unwrapArray(result);
|
||||
var arrayData = result;
|
||||
arrayData.acceptVisitor(typeInference);
|
||||
var arrayTypeRef = (WasmType.CompositeReference) typeInference.getResult();
|
||||
var arrayType = (WasmArray) arrayTypeRef.composite;
|
||||
|
@ -348,8 +349,22 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
|||
accept(expr.getIndex());
|
||||
var index = result;
|
||||
|
||||
result = new WasmArrayGet(arrayType, arrayData, index);
|
||||
result.setLocation(expr.getLocation());
|
||||
var arrayGet = new WasmArrayGet(arrayType, arrayData, index);
|
||||
switch (expr.getType()) {
|
||||
case BYTE:
|
||||
arrayGet.setSignedType(WasmSignedType.SIGNED);
|
||||
break;
|
||||
case SHORT:
|
||||
arrayGet.setSignedType(WasmSignedType.SIGNED);
|
||||
break;
|
||||
case CHAR:
|
||||
arrayGet.setSignedType(WasmSignedType.UNSIGNED);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
arrayGet.setLocation(expr.getLocation());
|
||||
result = arrayGet;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -395,11 +410,32 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
|||
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());
|
||||
var structGet = new WasmStructGet(struct, target, fieldIndex);
|
||||
var cls = context.classes().get(expr.getField().getClassName());
|
||||
if (cls != null) {
|
||||
var field = cls.getField(expr.getField().getFieldName());
|
||||
if (field != null) {
|
||||
var fieldType = field.getType();
|
||||
if (fieldType instanceof ValueType.Primitive) {
|
||||
switch (((ValueType.Primitive) fieldType).getKind()) {
|
||||
case BYTE:
|
||||
structGet.setSignedType(WasmSignedType.SIGNED);
|
||||
break;
|
||||
case SHORT:
|
||||
structGet.setSignedType(WasmSignedType.SIGNED);
|
||||
break;
|
||||
case CHARACTER:
|
||||
structGet.setSignedType(WasmSignedType.UNSIGNED);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
structGet.setLocation(expr.getLocation());
|
||||
result = structGet;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -240,7 +240,10 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
|||
for (var i = firstVar; i < ast.getVariables().size(); ++i) {
|
||||
var localVar = ast.getVariables().get(i);
|
||||
var representative = method.getProgram().variableAt(variableRepresentatives[i]);
|
||||
var type = typeMapper.mapType(typeInference.typeOf(representative));
|
||||
var inferredType = typeInference.typeOf(representative);
|
||||
var type = !inferredType.isArrayUnwrap
|
||||
? typeMapper.mapType(inferredType.valueType)
|
||||
: classInfoProvider.getClassInfo(inferredType.valueType).getArray().getReference();
|
||||
var wasmLocal = new WasmLocal(type, localVar.getName());
|
||||
function.add(wasmLocal);
|
||||
}
|
||||
|
@ -304,6 +307,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
|||
dummyInitializer = new WasmFunction(functionTypes.of(null));
|
||||
dummyInitializer.getBody().add(new WasmReturn());
|
||||
dummyInitializer.setName("teavm_dummy_initializer");
|
||||
dummyInitializer.setReferenced(true);
|
||||
module.functions.add(dummyInitializer);
|
||||
}
|
||||
return dummyInitializer;
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
|
|||
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStructSet;
|
||||
import org.teavm.backend.wasm.render.WasmBinaryWriter;
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializerContributor {
|
||||
|
@ -41,6 +42,7 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
|
|||
private WasmBinaryWriter binaryWriter = new WasmBinaryWriter();
|
||||
private Map<String, WasmGCStringConstant> stringMap = new LinkedHashMap<>();
|
||||
private BaseWasmFunctionRepository functionProvider;
|
||||
private WasmFunction nextCharArrayFunction;
|
||||
|
||||
public WasmGCStringPool(WasmGCStandardClasses standardClasses, WasmModule module,
|
||||
BaseWasmFunctionRepository functionProvider) {
|
||||
|
@ -62,8 +64,9 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
|
|||
|
||||
@Override
|
||||
public void contributeToInitializer(WasmFunction function) {
|
||||
var nextCharArrayFunction = functionProvider.forStaticMethod(new MethodReference(WasmGCStringPool.class,
|
||||
"nextCharArray", char[].class));
|
||||
if (nextCharArrayFunction == null) {
|
||||
return;
|
||||
}
|
||||
var stringStruct = standardClasses.stringClass().getStructure();
|
||||
for (var str : stringMap.values()) {
|
||||
var value = new WasmCall(nextCharArrayFunction);
|
||||
|
@ -75,9 +78,12 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
|
|||
@Override
|
||||
public WasmGCStringConstant getStringConstant(String string) {
|
||||
return stringMap.computeIfAbsent(string, s -> {
|
||||
if (nextCharArrayFunction == null) {
|
||||
initNextCharArrayFunction();
|
||||
}
|
||||
binaryWriter.writeInt32(string.length());
|
||||
binaryWriter.writeBytes(string.getBytes(StandardCharsets.UTF_8));
|
||||
var globalName = "_teavm_java_string_" + stringMap.size();
|
||||
var globalName = "teavm_java_string_" + stringMap.size();
|
||||
var globalType = standardClasses.stringClass().getType();
|
||||
var global = new WasmGlobal(globalName, globalType, WasmExpression.defaultValueOfType(globalType));
|
||||
module.globals.add(global);
|
||||
|
@ -85,50 +91,8 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
|
|||
});
|
||||
}
|
||||
|
||||
static char[] nextCharArray() {
|
||||
var length = nextLEB();
|
||||
var result = new char[length];
|
||||
var pos = 0;
|
||||
while (pos < length) {
|
||||
var b = nextByte();
|
||||
if ((b & 0x80) == 0) {
|
||||
result[pos++] = (char) b;
|
||||
} else if ((b & 0xE0) == 0xC0) {
|
||||
var b2 = nextByte();
|
||||
result[pos++] = (char) (((b & 0x1F) << 6) | (b2 & 0x3F));
|
||||
} else if ((b & 0xF0) == 0xE0) {
|
||||
var b2 = nextByte();
|
||||
var b3 = nextByte();
|
||||
var c = (char) (((b & 0x0F) << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x3F));
|
||||
result[pos++] = c;
|
||||
} else if ((b & 0xF8) == 0xF0) {
|
||||
var b2 = nextByte();
|
||||
var b3 = nextByte();
|
||||
var b4 = nextByte();
|
||||
var code = ((b & 0x07) << 18) | ((b2 & 0x3f) << 12) | ((b3 & 0x3F) << 6) | (b4 & 0x3F);
|
||||
result[pos++] = Character.highSurrogate(code);
|
||||
result[pos++] = Character.lowSurrogate(code);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
private void initNextCharArrayFunction() {
|
||||
nextCharArrayFunction = functionProvider.forStaticMethod(new MethodReference(WasmGCSupport.class,
|
||||
"nextCharArray", char[].class));
|
||||
}
|
||||
|
||||
private static int nextLEB() {
|
||||
var shift = 0;
|
||||
var result = 0;
|
||||
while (true) {
|
||||
var b = nextByte();
|
||||
var digit = b & 0x7F;
|
||||
result |= digit << shift;
|
||||
if ((b & 0x80) == 0) {
|
||||
break;
|
||||
}
|
||||
shift += 7;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static native byte nextByte();
|
||||
|
||||
private static native void error();
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ package org.teavm.backend.wasm.generators.gc;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.teavm.backend.wasm.generate.gc.methods.WasmGCCustomGeneratorProvider;
|
||||
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool;
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
|
||||
|
@ -30,7 +30,7 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
|
|||
|
||||
private void fillStringPool() {
|
||||
generators.put(
|
||||
new MethodReference(WasmGCStringPool.class, "nextByte", byte.class),
|
||||
new MethodReference(WasmGCSupport.class, "nextByte", byte.class),
|
||||
new WasmGCStringPoolGenerator()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ public class WasmFunction extends WasmEntity {
|
|||
private String exportName;
|
||||
private String importName;
|
||||
private String importModule;
|
||||
private boolean referenced;
|
||||
private WasmFunctionType type;
|
||||
private List<WasmLocal> localVariables = new ArrayList<>();
|
||||
private List<WasmLocal> readonlyLocalVariables = Collections.unmodifiableList(localVariables);
|
||||
|
@ -70,6 +71,14 @@ public class WasmFunction extends WasmEntity {
|
|||
this.importModule = importModule;
|
||||
}
|
||||
|
||||
public boolean isReferenced() {
|
||||
return referenced;
|
||||
}
|
||||
|
||||
public void setReferenced(boolean referenced) {
|
||||
this.referenced = referenced;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isImported() {
|
||||
return importName != null;
|
||||
|
|
|
@ -265,19 +265,39 @@ public class WasmBinaryRenderer {
|
|||
}
|
||||
|
||||
private void renderElement(WasmModule module) {
|
||||
if (module.getFunctionTable().isEmpty()) {
|
||||
var count = 0;
|
||||
if (!module.getFunctionTable().isEmpty()) {
|
||||
++count;
|
||||
}
|
||||
if (module.functions.stream().anyMatch(WasmFunction::isReferenced)) {
|
||||
++count;
|
||||
}
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
WasmBinaryWriter section = new WasmBinaryWriter();
|
||||
section.writeLEB(1);
|
||||
section.writeLEB(0);
|
||||
section.writeLEB(count);
|
||||
|
||||
renderInitializer(section, 0);
|
||||
if (!module.getFunctionTable().isEmpty()) {
|
||||
section.writeLEB(0);
|
||||
renderInitializer(section, 0);
|
||||
section.writeLEB(module.getFunctionTable().size());
|
||||
for (var function : module.getFunctionTable()) {
|
||||
section.writeLEB(module.functions.indexOf(function));
|
||||
}
|
||||
}
|
||||
|
||||
section.writeLEB(module.getFunctionTable().size());
|
||||
for (var function : module.getFunctionTable()) {
|
||||
section.writeLEB(module.functions.indexOf(function));
|
||||
var referencedFunctions = module.functions.stream()
|
||||
.filter(WasmFunction::isReferenced)
|
||||
.collect(Collectors.toList());
|
||||
if (!referencedFunctions.isEmpty()) {
|
||||
section.writeLEB(3);
|
||||
section.writeByte(0);
|
||||
section.writeLEB(referencedFunctions.size());
|
||||
for (var function : referencedFunctions) {
|
||||
section.writeLEB(module.functions.indexOf(function));
|
||||
}
|
||||
}
|
||||
|
||||
writeSection(SECTION_ELEMENT, "element", section.getData());
|
||||
|
|
|
@ -38,4 +38,51 @@ public class WasmGCSupport {
|
|||
|
||||
@Import(name = "putcharStderr")
|
||||
public static native void putCharStderr(char c);
|
||||
|
||||
public static char[] nextCharArray() {
|
||||
var length = nextLEB();
|
||||
var result = new char[length];
|
||||
var pos = 0;
|
||||
while (pos < length) {
|
||||
var b = nextByte();
|
||||
if ((b & 0x80) == 0) {
|
||||
result[pos++] = (char) b;
|
||||
} else if ((b & 0xE0) == 0xC0) {
|
||||
var b2 = nextByte();
|
||||
result[pos++] = (char) (((b & 0x1F) << 6) | (b2 & 0x3F));
|
||||
} else if ((b & 0xF0) == 0xE0) {
|
||||
var b2 = nextByte();
|
||||
var b3 = nextByte();
|
||||
var c = (char) (((b & 0x0F) << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x3F));
|
||||
result[pos++] = c;
|
||||
} else if ((b & 0xF8) == 0xF0) {
|
||||
var b2 = nextByte();
|
||||
var b3 = nextByte();
|
||||
var b4 = nextByte();
|
||||
var code = ((b & 0x07) << 18) | ((b2 & 0x3f) << 12) | ((b3 & 0x3F) << 6) | (b4 & 0x3F);
|
||||
result[pos++] = Character.highSurrogate(code);
|
||||
result[pos++] = Character.lowSurrogate(code);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int nextLEB() {
|
||||
var shift = 0;
|
||||
var result = 0;
|
||||
while (true) {
|
||||
var b = nextByte();
|
||||
var digit = b & 0x7F;
|
||||
result |= digit << shift;
|
||||
if ((b & 0x80) == 0) {
|
||||
break;
|
||||
}
|
||||
shift += 7;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static native byte nextByte();
|
||||
|
||||
private static native void error();
|
||||
}
|
||||
|
|
|
@ -60,12 +60,18 @@ public abstract class BaseTypeInference<T> {
|
|||
private Object[] types;
|
||||
private Graph graph;
|
||||
private Graph arrayGraph;
|
||||
private Graph arrayUnwrapGraph;
|
||||
private boolean phisSkipped;
|
||||
|
||||
public BaseTypeInference(Program program, MethodReference reference) {
|
||||
this.program = program;
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
public void setPhisSkipped(boolean phisSkipped) {
|
||||
this.phisSkipped = phisSkipped;
|
||||
}
|
||||
|
||||
private void prepare() {
|
||||
types = new Object[program.variableCount()];
|
||||
var visitor = new InitialTypeVisitor(program.variableCount());
|
||||
|
@ -78,9 +84,11 @@ public abstract class BaseTypeInference<T> {
|
|||
for (var insn : block) {
|
||||
insn.acceptVisitor(visitor);
|
||||
}
|
||||
for (var phi : block.getPhis()) {
|
||||
for (var incoming : phi.getIncomings()) {
|
||||
visitor.graphBuilder.addEdge(incoming.getValue().getIndex(), phi.getReceiver().getIndex());
|
||||
if (!phisSkipped) {
|
||||
for (var phi : block.getPhis()) {
|
||||
for (var incoming : phi.getIncomings()) {
|
||||
visitor.graphBuilder.addEdge(incoming.getValue().getIndex(), phi.getReceiver().getIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var tryCatch : block.getTryCatchBlocks()) {
|
||||
|
@ -95,6 +103,7 @@ public abstract class BaseTypeInference<T> {
|
|||
}
|
||||
graph = visitor.graphBuilder.build();
|
||||
arrayGraph = visitor.arrayGraphBuilder.build();
|
||||
arrayUnwrapGraph = visitor.arrayUnwrapGraphBuilder.build();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -126,6 +135,12 @@ public abstract class BaseTypeInference<T> {
|
|||
typeStack.push(type);
|
||||
}
|
||||
}
|
||||
for (var succ : arrayUnwrapGraph.outgoingEdges(variable)) {
|
||||
if (!Objects.equals(types[succ], type)) {
|
||||
stack.push(succ);
|
||||
typeStack.push(arrayUnwrapType(type));
|
||||
}
|
||||
}
|
||||
if (arrayGraph.outgoingEdgesCount(variable) > 0) {
|
||||
var elementType = elementType(type);
|
||||
for (var succ : arrayGraph.outgoingEdges(variable)) {
|
||||
|
@ -174,13 +189,19 @@ public abstract class BaseTypeInference<T> {
|
|||
return mapType(methodRef.getReturnType());
|
||||
}
|
||||
|
||||
protected T arrayUnwrapType(T type) {
|
||||
return type;
|
||||
}
|
||||
|
||||
private class InitialTypeVisitor extends AbstractInstructionVisitor {
|
||||
private GraphBuilder graphBuilder;
|
||||
private GraphBuilder arrayGraphBuilder;
|
||||
private GraphBuilder arrayUnwrapGraphBuilder;
|
||||
|
||||
InitialTypeVisitor(int size) {
|
||||
graphBuilder = new GraphBuilder(size);
|
||||
arrayGraphBuilder = new GraphBuilder(size);
|
||||
arrayUnwrapGraphBuilder = new GraphBuilder(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -309,7 +330,7 @@ public abstract class BaseTypeInference<T> {
|
|||
|
||||
@Override
|
||||
public void visit(UnwrapArrayInstruction insn) {
|
||||
graphBuilder.addEdge(insn.getArray().getIndex(), insn.getReceiver().getIndex());
|
||||
arrayUnwrapGraphBuilder.addEdge(insn.getArray().getIndex(), insn.getReceiver().getIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in New Issue
Block a user