wasm gc: fix generation issues, get first version that produces module that passes validation

This commit is contained in:
Alexey Andreev 2024-08-01 19:57:12 +02:00
parent a8d97ad387
commit 54dc7fe5f8
17 changed files with 302 additions and 129 deletions

View File

@ -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));

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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));

View File

@ -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");

View File

@ -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;
}

View File

@ -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));

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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()
);
}

View File

@ -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;

View File

@ -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());

View File

@ -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();
}

View File

@ -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