From 54dc7fe5f89c7efbf29219bcb1c44d8bc3163f6f Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 1 Aug 2024 19:57:12 +0200 Subject: [PATCH] wasm gc: fix generation issues, get first version that produces module that passes validation --- .../backend/wasm/WasmGCModuleGenerator.java | 2 + .../org/teavm/backend/wasm/WasmGCTarget.java | 15 +++++ .../backend/wasm/gc/PreciseTypeInference.java | 53 +++++++++------- .../backend/wasm/gc/PreciseValueType.java | 28 +++++++++ .../backend/wasm/gc/WasmGCDependencies.java | 11 ++-- .../gc/WasmGCVariableCategoryProvider.java | 1 + .../gc/classes/WasmGCClassGenerator.java | 46 ++++++++------ .../generate/gc/classes/WasmGCClassInfo.java | 6 ++ .../WasmGCSupertypeFunctionGenerator.java | 30 +++++----- .../gc/methods/WasmGCGenerationVisitor.java | 50 +++++++++++++--- .../gc/methods/WasmGCMethodGenerator.java | 6 +- .../generate/gc/strings/WasmGCStringPool.java | 60 ++++--------------- .../generators/gc/WasmGCCustomGenerators.java | 4 +- .../backend/wasm/model/WasmFunction.java | 9 +++ .../wasm/render/WasmBinaryRenderer.java | 34 ++++++++--- .../backend/wasm/runtime/WasmGCSupport.java | 47 +++++++++++++++ .../model/analysis/BaseTypeInference.java | 29 +++++++-- 17 files changed, 302 insertions(+), 129 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/wasm/gc/PreciseValueType.java diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmGCModuleGenerator.java b/core/src/main/java/org/teavm/backend/wasm/WasmGCModuleGenerator.java index 101df7a8a..bcffa3be8 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmGCModuleGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmGCModuleGenerator.java @@ -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)); diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java index bb0f2e697..5f418fbf6 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java @@ -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, diff --git a/core/src/main/java/org/teavm/backend/wasm/gc/PreciseTypeInference.java b/core/src/main/java/org/teavm/backend/wasm/gc/PreciseTypeInference.java index 0e20eab44..587fde704 100644 --- a/core/src/main/java/org/teavm/backend/wasm/gc/PreciseTypeInference.java +++ b/core/src/main/java/org/teavm/backend/wasm/gc/PreciseTypeInference.java @@ -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 { - public static final ValueType OBJECT_TYPE = ValueType.object("java.lang.Object"); +public class PreciseTypeInference extends BaseTypeInference { + 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 { } @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 { } 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 { } } @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); + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/gc/PreciseValueType.java b/core/src/main/java/org/teavm/backend/wasm/gc/PreciseValueType.java new file mode 100644 index 000000000..d590e534a --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/gc/PreciseValueType.java @@ -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; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java index fa37c67f9..d2fecb611 100644 --- a/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java +++ b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java @@ -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(); } } diff --git a/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCVariableCategoryProvider.java b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCVariableCategoryProvider.java index 9079ff2d6..0b47b5521 100644 --- a/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCVariableCategoryProvider.java +++ b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCVariableCategoryProvider.java @@ -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)); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java index b730338cd..23e4debe7 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java @@ -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 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 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"); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfo.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfo.java index 3ac9d0fbf..4ae7a5936 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfo.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfo.java @@ -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; } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java index 8b4a9e209..0378bcfdc 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java @@ -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 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)); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java index 00838ef94..368f6d7ae 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java @@ -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; } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java index 941b81f2d..afac89c28 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java @@ -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; diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java index a3cd66390..49091e65a 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java @@ -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 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(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerators.java b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerators.java index 1b0f30ac6..917d50cc8 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerators.java +++ b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerators.java @@ -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() ); } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmFunction.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmFunction.java index 113a32e62..935b44584 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmFunction.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmFunction.java @@ -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 localVariables = new ArrayList<>(); private List 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; diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java index a477341c7..39647bf3e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java @@ -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()); diff --git a/core/src/main/java/org/teavm/backend/wasm/runtime/WasmGCSupport.java b/core/src/main/java/org/teavm/backend/wasm/runtime/WasmGCSupport.java index 1d2a6b702..47e601cfe 100644 --- a/core/src/main/java/org/teavm/backend/wasm/runtime/WasmGCSupport.java +++ b/core/src/main/java/org/teavm/backend/wasm/runtime/WasmGCSupport.java @@ -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(); } diff --git a/core/src/main/java/org/teavm/model/analysis/BaseTypeInference.java b/core/src/main/java/org/teavm/model/analysis/BaseTypeInference.java index f0ade0b15..b9f0cabfe 100644 --- a/core/src/main/java/org/teavm/model/analysis/BaseTypeInference.java +++ b/core/src/main/java/org/teavm/model/analysis/BaseTypeInference.java @@ -60,12 +60,18 @@ public abstract class BaseTypeInference { 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 { 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 { } graph = visitor.graphBuilder.build(); arrayGraph = visitor.arrayGraphBuilder.build(); + arrayUnwrapGraph = visitor.arrayUnwrapGraphBuilder.build(); } @SuppressWarnings("unchecked") @@ -126,6 +135,12 @@ public abstract class BaseTypeInference { 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 { 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 { @Override public void visit(UnwrapArrayInstruction insn) { - graphBuilder.addEdge(insn.getArray().getIndex(), insn.getReceiver().getIndex()); + arrayUnwrapGraphBuilder.addEdge(insn.getArray().getIndex(), insn.getReceiver().getIndex()); } @Override