diff --git a/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderWasmSupport.java b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderWasmSupport.java index 0ee7bc70f..4d1682d1e 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderWasmSupport.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderWasmSupport.java @@ -70,7 +70,7 @@ public class ServiceLoaderWasmSupport implements WasmIntrinsicFactory { var table = createServiceData(manager); var tableArg = new WasmInt32Constant(table); var serviceClassAddress = manager.generate(invocation.getArguments().get(0)); - return new WasmCall(manager.getNames().forMethod(CREATE_SERVICES_METHOD), tableArg, + return new WasmCall(manager.getFunctions().forStaticMethod(CREATE_SERVICES_METHOD), tableArg, serviceClassAddress); } @@ -94,10 +94,10 @@ public class ServiceLoaderWasmSupport implements WasmIntrinsicFactory { var implPointer = manager.getClassPointer(ValueType.object(implementation)); entry.setAddress(0, implPointer); - var constructorName = manager.getNames().forMethod(new MethodReference( + var constructorFn = manager.getFunctions().forInstanceMethod(new MethodReference( implementation, "", ValueType.VOID )); - entry.setInt(1, manager.getFunctionPointer(constructorName)); + entry.setInt(1, manager.getFunctionPointer(constructorFn)); } return result; diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmFunctionRepository.java b/core/src/main/java/org/teavm/backend/wasm/WasmFunctionRepository.java new file mode 100644 index 000000000..ab849841f --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/WasmFunctionRepository.java @@ -0,0 +1,101 @@ +/* + * 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; + +import java.util.HashMap; +import java.util.Map; +import org.teavm.backend.lowlevel.generate.NameProvider; +import org.teavm.backend.wasm.generate.WasmGeneratorUtil; +import org.teavm.backend.wasm.model.WasmFunction; +import org.teavm.backend.wasm.model.WasmModule; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.model.ElementModifier; +import org.teavm.model.MethodReader; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; + +public class WasmFunctionRepository { + private WasmModule module; + private WasmFunctionTypes functionTypes; + private NameProvider nameProvider; + private Map staticMethods = new HashMap<>(); + private Map instanceMethods = new HashMap<>(); + private Map classInitializers = new HashMap<>(); + private Map supertypes = new HashMap<>(); + + public WasmFunctionRepository(WasmModule module, WasmFunctionTypes functionTypes, NameProvider nameProvider) { + this.module = module; + this.functionTypes = functionTypes; + this.nameProvider = nameProvider; + } + + public WasmFunction forMethod(MethodReader method) { + return forMethod(method.getReference(), method.hasModifier(ElementModifier.STATIC)); + } + + public WasmFunction forMethod(MethodReference reference, boolean isStatic) { + return isStatic ? forStaticMethod(reference) : forInstanceMethod(reference); + } + + public WasmFunction forStaticMethod(MethodReference reference) { + return staticMethods.computeIfAbsent(reference, key -> { + var wasmParams = new WasmType[key.parameterCount()]; + for (var i = 0; i < key.parameterCount(); ++i) { + wasmParams[i] = WasmGeneratorUtil.mapType(reference.parameterType(i)); + } + var wasmType = functionTypes.of(WasmGeneratorUtil.mapType(key.getReturnType()), wasmParams); + var wasmFunction = new WasmFunction(wasmType); + wasmFunction.setName(nameProvider.forMethod(key)); + wasmFunction.setJavaMethod(key); + module.functions.add(wasmFunction); + return wasmFunction; + }); + } + + public WasmFunction forInstanceMethod(MethodReference reference) { + return instanceMethods.computeIfAbsent(reference, key -> { + var wasmParams = new WasmType[key.parameterCount() + 1]; + wasmParams[0] = WasmGeneratorUtil.mapType(ValueType.object(reference.getClassName())); + for (var i = 0; i < key.parameterCount(); ++i) { + wasmParams[i + 1] = WasmGeneratorUtil.mapType(reference.parameterType(i)); + } + var wasmType = functionTypes.of(WasmGeneratorUtil.mapType(key.getReturnType()), wasmParams); + var wasmFunction = new WasmFunction(wasmType); + wasmFunction.setName(nameProvider.forMethod(key)); + wasmFunction.setJavaMethod(key); + module.functions.add(wasmFunction); + return wasmFunction; + }); + } + + public WasmFunction forClassInitializer(String className) { + return classInitializers.computeIfAbsent(className, key -> { + var wasmFunction = new WasmFunction(functionTypes.of(null)); + wasmFunction.setName(nameProvider.forClassInitializer(key)); + module.functions.add(wasmFunction); + return wasmFunction; + }); + } + + public WasmFunction forSupertype(ValueType type) { + return supertypes.computeIfAbsent(type, key -> { + var wasmFunction = new WasmFunction(functionTypes.of(WasmType.INT32, WasmType.INT32)); + wasmFunction.setName(nameProvider.forSupertypeFunction(key)); + module.functions.add(wasmFunction); + return wasmFunction; + }); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmFunctionTypes.java b/core/src/main/java/org/teavm/backend/wasm/WasmFunctionTypes.java new file mode 100644 index 000000000..02a5679aa --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/WasmFunctionTypes.java @@ -0,0 +1,44 @@ +/* + * 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; + +import java.util.HashMap; +import java.util.Map; +import org.teavm.backend.wasm.model.WasmFunctionType; +import org.teavm.backend.wasm.model.WasmModule; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.render.WasmSignature; + +public class WasmFunctionTypes { + private WasmModule module; + private Map types = new HashMap<>(); + + public WasmFunctionTypes(WasmModule module) { + this.module = module; + } + + public WasmFunctionType get(WasmSignature signature) { + return types.computeIfAbsent(signature, k -> { + var type = new WasmFunctionType(signature.getReturnType(), signature.getParameterTypes()); + module.types.add(type); + return type; + }); + } + + public WasmFunctionType of(WasmType returnType, WasmType... parameterTypes) { + return get(new WasmSignature(returnType, parameterTypes)); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index e194ecba4..e3df1a24e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -36,7 +36,6 @@ import org.teavm.ast.InvocationExpr; import org.teavm.ast.decompilation.Decompiler; import org.teavm.backend.lowlevel.analyze.LowLevelInliningFilterFactory; import org.teavm.backend.lowlevel.dependency.StringsDependencyListener; -import org.teavm.backend.lowlevel.generate.NameProvider; import org.teavm.backend.lowlevel.generate.NameProviderWithSpecialNames; import org.teavm.backend.lowlevel.transform.CoroutineTransformation; import org.teavm.backend.wasm.binary.BinaryWriter; @@ -107,6 +106,7 @@ import org.teavm.backend.wasm.model.expression.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmStoreInt32; import org.teavm.backend.wasm.optimization.UnusedFunctionElimination; +import org.teavm.backend.wasm.optimization.UnusedTypeElimination; import org.teavm.backend.wasm.render.ReportingWasmBinaryStatsCollector; import org.teavm.backend.wasm.render.WasmBinaryRenderer; import org.teavm.backend.wasm.render.WasmBinaryStatsCollector; @@ -475,7 +475,6 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { var statsCollector = this.statsCollector != null ? this.statsCollector : WasmBinaryStatsCollector.EMPTY; WasmModule module = new WasmModule(); - WasmFunction initFunction = new WasmFunction("__start__"); var vtableProvider = createVirtualTableProvider(classes); ClassHierarchy hierarchy = new ClassHierarchy(classes); @@ -491,20 +490,24 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { var dwarfClassGen = debugging ? new DwarfClassGenerator(dwarfGenerator.getInfoWriter(), dwarfGenerator.strings) : null; + var functionTypes = new WasmFunctionTypes(module); + var functions = new WasmFunctionRepository(module, functionTypes, names); var classGenerator = new WasmClassGenerator(classes, controller.getUnprocessedClassSource(), - vtableProvider, tagRegistry, binaryWriter, names, metadataRequirements, + vtableProvider, tagRegistry, binaryWriter, functions, module, metadataRequirements, controller.getClassInitializerInfo(), characteristics, dwarfClassGen, statsCollector); Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false); var stringPool = classGenerator.getStringPool(); WasmTag exceptionTag = null; if (exceptionsUsed) { - exceptionTag = new WasmTag(); - module.addTag(exceptionTag); + exceptionTag = new WasmTag(functionTypes.of(null)); + module.tags.add(exceptionTag); } - var context = new WasmGenerationContext(classes, module, controller.getDiagnostics(), + var context = new WasmGenerationContext(classes, module, functionTypes, functions, controller.getDiagnostics(), vtableProvider, tagRegistry, stringPool, names, characteristics, exceptionTag); + var initFunction = new WasmFunction(functionTypes.of(null)); + context.addIntrinsic(new AddressIntrinsic(classGenerator)); context.addIntrinsic(new StructureIntrinsic(classes, classGenerator)); context.addIntrinsic(new FunctionIntrinsic(classGenerator)); @@ -550,11 +553,11 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { asyncMethods::contains); generateMethods(classes, context, generator, classGenerator, binaryWriter, module, dwarfClassGen); - new WasmInteropFunctionGenerator(classGenerator).generateFunctions(module); + new WasmInteropFunctionGenerator(classGenerator, functionTypes).generateFunctions(module); exceptionHandlingIntrinsic.postProcess(context.callSites); - generateIsSupertypeFunctions(tagRegistry, module, classGenerator); + generateIsSupertypeFunctions(tagRegistry, classGenerator, functions); classGenerator.postProcess(); - new WasmSpecialFunctionGenerator(classGenerator, gcIntrinsic.regionSizeExpressions) + new WasmSpecialFunctionGenerator(classGenerator, functionTypes, gcIntrinsic.regionSizeExpressions) .generateSpecialFunctions(module); mutatorIntrinsic.setStaticGcRootsAddress(classGenerator.getStaticGcRootsAddress()); mutatorIntrinsic.setClassesAddress(classGenerator.getClassesAddress()); @@ -566,30 +569,25 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { module.getSegments().add(dataSegment); renderMemoryLayout(module, binaryWriter.getAddress(), gcIntrinsic); - renderClinit(classes, classGenerator, module); + renderClinit(classes, classGenerator, functions); if (controller.wasCancelled()) { return; } - generateInitFunction(classes, initFunction, names, binaryWriter.getAddress()); - module.add(initFunction); + generateInitFunction(classes, initFunction, functions, binaryWriter.getAddress()); + module.functions.add(initFunction); module.setStartFunction(initFunction); - module.add(createStartFunction(names)); - module.add(createStartCallerFunction(names)); - - for (String functionName : classGenerator.getFunctionTable()) { - WasmFunction function = module.getFunctions().get(functionName); - assert function != null : "Function referenced from function table not found: " + functionName; - module.getFunctionTable().add(function); - } + module.functions.add(createStartFunction(functionTypes, functions)); + module.functions.add(createStartCallerFunction(functionTypes, functions)); new UnusedFunctionElimination(module).apply(); + new UnusedTypeElimination(module).apply(); if (Boolean.parseBoolean(System.getProperty("wasm.memoryTrace", "false"))) { - new MemoryAccessTraceTransformation(module).apply(); + new MemoryAccessTraceTransformation(module, functionTypes).apply(); } if (Boolean.parseBoolean(System.getProperty("wasm.indirectCallTrace", "false"))) { - new IndirectCallTraceTransformation(module).apply(); + new IndirectCallTraceTransformation(module, functionTypes).apply(); } writeBinaryWasm(buildTarget, outputName, module, classGenerator, dwarfGenerator, dwarfClassGen, @@ -669,27 +667,31 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { }; } - private WasmFunction createStartFunction(NameProvider names) { - var function = new WasmFunction("teavm_start"); + private WasmFunction createStartFunction(WasmFunctionTypes functionTypes, WasmFunctionRepository functions) { + var function = new WasmFunction(functionTypes.of(null, WasmType.INT32)); + function.setName("teavm_start"); function.setExportName("start"); - function.getParameters().add(WasmType.INT32); var local = new WasmLocal(WasmType.INT32, "args"); function.add(local); - var call = new WasmCall(names.forMethod(new MethodReference(WasmSupport.class, "runWithArgs", - String[].class, void.class))); + var runWithArgsFunction = functions.forStaticMethod(new MethodReference(WasmSupport.class, "runWithArgs", + String[].class, void.class)); + var call = new WasmCall(runWithArgsFunction); call.getArguments().add(new WasmGetLocal(local)); function.getBody().add(call); return function; } - private WasmFunction createStartCallerFunction(NameProvider names) { - var function = new WasmFunction("teavm_call_start"); + private WasmFunction createStartCallerFunction(WasmFunctionTypes functionTypes, WasmFunctionRepository functions) { + var function = new WasmFunction(functionTypes.of(null)); function.setExportName("_start"); + function.setName("teavm_call_start"); - var call = new WasmCall(names.forMethod(new MethodReference(WasmSupport.class, "runWithoutArgs", void.class))); + var runWithoutArgsFunction = functions.forStaticMethod(new MethodReference(WasmSupport.class, "runWithoutArgs", + void.class)); + var call = new WasmCall(runWithoutArgsFunction); function.getBody().add(call); return function; @@ -718,7 +720,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } private void generateInitFunction(ListableClassReaderSource classes, WasmFunction initFunction, - NameProvider names, int heapAddress) { + WasmFunctionRepository functions, int heapAddress) { for (Class javaCls : new Class[] { WasmRuntime.class, WasmHeap.class }) { ClassReader cls = classes.get(javaCls.getName()); @@ -726,10 +728,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { if (clinit == null) { continue; } - initFunction.getBody().add(new WasmCall(names.forClassInitializer(cls.getName()))); + initFunction.getBody().add(new WasmCall(functions.forClassInitializer(cls.getName()))); } - initFunction.getBody().add(new WasmCall(names.forMethod(INIT_HEAP_REF), + initFunction.getBody().add(new WasmCall(functions.forStaticMethod(INIT_HEAP_REF), new WasmInt32Constant(heapAddress), new WasmInt32Constant(minHeapSize), new WasmInt32Constant(maxHeapSize), new WasmInt32Constant(WasmHeap.DEFAULT_STACK_SIZE), new WasmInt32Constant(WasmHeap.DEFAULT_BUFFER_SIZE))); @@ -740,7 +742,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { if (clinit == null) { continue; } - initFunction.getBody().add(new WasmCall(names.forClassInitializer(cls.getName()))); + initFunction.getBody().add(new WasmCall(functions.forClassInitializer(cls.getName()))); } for (String className : classes.getClassNames()) { @@ -756,7 +758,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { if (clinit == null) { continue; } - initFunction.getBody().add(new WasmCall(names.forClassInitializer(className))); + initFunction.getBody().add(new WasmCall(functions.forClassInitializer(className))); } } @@ -767,7 +769,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } private void emitWast(WasmModule module, BuildTarget buildTarget, String outputName) throws IOException { - WasmRenderer renderer = new WasmRenderer(); + WasmRenderer renderer = new WasmRenderer(module); renderer.setLineNumbersEmitted(debugging); renderer.render(module); try (OutputStream output = buildTarget.createResource(outputName); @@ -777,7 +779,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } private void emitC(WasmModule module, BuildTarget buildTarget, String outputName) throws IOException { - WasmCRenderer renderer = new WasmCRenderer(); + var renderer = new WasmCRenderer(module); renderer.setLineNumbersEmitted(cLineNumbersEmitted); renderer.setMemoryAccessChecked(Boolean.parseBoolean(System.getProperty("wasm.c.assertMemory", "false"))); renderer.render(module); @@ -814,13 +816,12 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { || context.getIntrinsic(method.getReference()) != null) { continue; } - module.add(generator.generateDefinition(method.getReference())); methods.add(method); } } var methodGeneratorContext = new MethodGeneratorContextImpl(binaryWriter, - context.getStringPool(), context.getDiagnostics(), context.names, classGenerator, classes); + context.getStringPool(), context.getDiagnostics(), context.functions, classGenerator, classes); for (MethodHolder method : methods) { ClassHolder cls = classes.get(method.getOwnerName()); @@ -847,7 +848,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { if (implementor.hasModifier(ElementModifier.NATIVE)) { var methodGenerator = context.getGenerator(method.getReference()); if (methodGenerator != null) { - WasmFunction function = context.getFunction(context.names.forMethod(method.getReference())); + var function = context.functions.forMethod(method); methodGenerator.apply(method.getReference(), function, methodGeneratorContext); } else if (!isShadowStackMethod(method.getReference())) { if (context.getImportedMethod(method.getReference()) == null) { @@ -855,7 +856,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { controller.getDiagnostics().error(location, "Method {{m0}} is native but " + "has no {{c1}} annotation on it", method.getReference(), Import.class.getName()); } - generator.generateNative(method.getReference()); + generator.generateNative(method); } continue; } @@ -865,7 +866,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { if (method == implementor) { generator.generate(method.getReference(), implementor); } else { - generateStub(context.names, module, method, implementor); + generateStub(context.functions, method, implementor); } if (dwarfClassGen != null) { var dwarfClass = dwarfClassGen.getClass(method.getOwnerName()); @@ -894,13 +895,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } } - private void generateIsSupertypeFunctions(TagRegistry tagRegistry, WasmModule module, - WasmClassGenerator classGenerator) { + private void generateIsSupertypeFunctions(TagRegistry tagRegistry, WasmClassGenerator classGenerator, + WasmFunctionRepository functions) { for (ValueType type : classGenerator.getRegisteredClasses()) { - WasmFunction function = new WasmFunction(classGenerator.names.forSupertypeFunction(type)); - function.getParameters().add(WasmType.INT32); - function.setResult(WasmType.INT32); - module.add(function); + var function = functions.forSupertype(type); WasmLocal subtypeVar = new WasmLocal(WasmType.INT32, "subtype"); function.add(subtypeVar); @@ -910,7 +908,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { generateIsClass(subtypeVar, classGenerator, tagRegistry, className, function.getBody()); } else if (type instanceof ValueType.Array) { ValueType itemType = ((ValueType.Array) type).getItemType(); - generateIsArray(subtypeVar, classGenerator, itemType, function.getBody()); + generateIsArray(subtypeVar, classGenerator, functions, itemType, function.getBody()); } else { int expected = classGenerator.getClassPointer(type); WasmExpression condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, @@ -971,8 +969,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { body.add(new WasmReturn(new WasmInt32Constant(1))); } - private void generateIsArray(WasmLocal subtypeVar, WasmClassGenerator classGenerator, ValueType itemType, - List body) { + private void generateIsArray(WasmLocal subtypeVar, WasmClassGenerator classGenerator, + WasmFunctionRepository functions, ValueType itemType, List body) { int itemOffset = classGenerator.getFieldOffset(new FieldReference(RuntimeClass.class.getName(), "itemType")); var itemExpression = new WasmLoadInt32(4, new WasmGetLocal(subtypeVar), WasmInt32Subtype.INT32); @@ -984,18 +982,18 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { itemTest.setType(WasmType.INT32); itemTest.getThenBlock().getBody().add(new WasmInt32Constant(0)); - WasmCall delegateToItem = new WasmCall(classGenerator.names.forSupertypeFunction(itemType)); + WasmCall delegateToItem = new WasmCall(functions.forSupertype(itemType)); delegateToItem.getArguments().add(new WasmGetLocal(subtypeVar)); itemTest.getElseBlock().getBody().add(delegateToItem); body.add(new WasmReturn(itemTest)); } - private void generateStub(NameProvider names, WasmModule module, MethodHolder method, MethodHolder implementor) { - WasmFunction function = module.getFunctions().get(names.forMethod(method.getReference())); + private void generateStub(WasmFunctionRepository functions, MethodHolder method, MethodHolder implementor) { + WasmFunction function = functions.forMethod(method); - WasmCall call = new WasmCall(names.forMethod(implementor.getReference())); - for (WasmType param : function.getParameters()) { + WasmCall call = new WasmCall(functions.forMethod(implementor)); + for (WasmType param : function.getType().getParameterTypes()) { WasmLocal local = new WasmLocal(param); function.add(local); call.getArguments().add(new WasmGetLocal(local)); @@ -1009,7 +1007,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } private void renderClinit(ListableClassReaderSource classes, WasmClassGenerator classGenerator, - WasmModule module) { + WasmFunctionRepository functions) { for (ValueType type : classGenerator.getRegisteredClasses()) { if (!(type instanceof ValueType.Object)) { continue; @@ -1028,8 +1026,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { continue; } - WasmFunction initFunction = new WasmFunction(classGenerator.names.forClassInitializer(className)); - module.add(initFunction); + var initFunction = functions.forClassInitializer(className); WasmBlock block = new WasmBlock(false); @@ -1047,7 +1044,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { block.getBody().add(new WasmStoreInt32(4, new WasmInt32Constant(index), initFlag, WasmInt32Subtype.INT32)); - block.getBody().add(new WasmCall(classGenerator.names.forMethod(method.getReference()))); + block.getBody().add(new WasmCall(functions.forMethod(method))); if (controller.wasCancelled()) { break; @@ -1129,17 +1126,18 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { private BinaryWriter binaryWriter; private WasmStringPool stringPool; private Diagnostics diagnostics; - private NameProvider names; + private WasmFunctionRepository functions; private WasmClassGenerator classGenerator; private ClassReaderSource classSource; MethodGeneratorContextImpl(BinaryWriter binaryWriter, WasmStringPool stringPool, - Diagnostics diagnostics, NameProvider names, WasmClassGenerator classGenerator, + Diagnostics diagnostics, WasmFunctionRepository functions, + WasmClassGenerator classGenerator, ClassReaderSource classSource) { this.binaryWriter = binaryWriter; this.stringPool = stringPool; this.diagnostics = diagnostics; - this.names = names; + this.functions = functions; this.classGenerator = classGenerator; this.classSource = classSource; } @@ -1159,9 +1157,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { return diagnostics; } + @Override - public NameProvider getNames() { - return names; + public WasmFunctionRepository getFunctions() { + return functions; } @Override @@ -1197,8 +1196,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { case "runMain": { var entryPoint = new MethodReference(controller.getEntryPoint(), "main", ValueType.parse(String[].class), ValueType.parse(void.class)); - String name = manager.getNames().forMethod(entryPoint); - WasmCall call = new WasmCall(name); + var function = manager.getFunctions().forStaticMethod(entryPoint); + WasmCall call = new WasmCall(function); var arg = manager.generate(invocation.getArguments().get(0)); if (manager.isManagedMethodCall(entryPoint)) { var block = new WasmBlock(false); @@ -1214,9 +1213,9 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { return call; } case "setCurrentThread": { - String name = manager.getNames().forMethod(new MethodReference(Thread.class, + var function = manager.getFunctions().forStaticMethod(new MethodReference(Thread.class, "setCurrentThread", Thread.class, void.class)); - WasmCall call = new WasmCall(name); + WasmCall call = new WasmCall(function); call.getArguments().add(manager.generate(invocation.getArguments().get(0))); call.setLocation(invocation.getLocation()); return call; diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeSectionListener.java b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeSectionListener.java index ec64e188a..1e18ca2fa 100644 --- a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeSectionListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeSectionListener.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.file.Files; import java.util.function.Consumer; +import org.teavm.backend.wasm.model.WasmNumType; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmFloatType; @@ -103,17 +104,21 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect private String typeToString(WasmType type) { if (type != null) { - switch (type) { - case INT32: - return "i32"; - case INT64: - return "i64"; - case FLOAT32: - return "f32"; - case FLOAT64: - return "f64"; - default: - break; + if (type instanceof WasmType.Number) { + switch (((WasmType.Number) type).number) { + case INT32: + return "i32"; + case INT64: + return "i64"; + case FLOAT32: + return "f32"; + case FLOAT64: + return "f64"; + default: + break; + } + } else if (type instanceof WasmType.Reference) { + return "ref"; } } return "unknown"; @@ -615,7 +620,7 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect } @Override - public void convert(WasmType sourceType, WasmType targetType, boolean signed, boolean reinterpret) { + public void convert(WasmNumType sourceType, WasmNumType targetType, boolean signed, boolean reinterpret) { switch (targetType) { case INT32: writer.write("i32."); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/TemporaryVariablePool.java b/core/src/main/java/org/teavm/backend/wasm/generate/TemporaryVariablePool.java index 5b8c6e244..44c3d10f6 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/TemporaryVariablePool.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/TemporaryVariablePool.java @@ -16,27 +16,23 @@ package org.teavm.backend.wasm.generate; import java.util.ArrayDeque; -import java.util.ArrayList; import java.util.Deque; -import java.util.List; +import java.util.HashMap; +import java.util.Map; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmType; class TemporaryVariablePool { private WasmFunction function; - private List> temporaryVariablesByType = new ArrayList<>(); + private Map> temporaryVariablesByType = new HashMap<>(); TemporaryVariablePool(WasmFunction function) { this.function = function; - int typeCount = WasmType.values().length; - for (int i = 0; i < typeCount; ++i) { - temporaryVariablesByType.add(new ArrayDeque<>()); - } } WasmLocal acquire(WasmType type) { - var stack = temporaryVariablesByType.get(type.ordinal()); + var stack = temporaryVariablesByType.computeIfAbsent(type, k -> new ArrayDeque<>()); WasmLocal variable = stack.pollFirst(); if (variable == null) { variable = new WasmLocal(type); @@ -46,7 +42,7 @@ class TemporaryVariablePool { } void release(WasmLocal variable) { - var stack = temporaryVariablesByType.get(variable.getType().ordinal()); + var stack = temporaryVariablesByType.get(variable.getType()); stack.push(variable); } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java index 58b2964f0..bfdb2c285 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java @@ -24,7 +24,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import org.teavm.backend.lowlevel.generate.NameProvider; +import org.teavm.backend.wasm.WasmFunctionRepository; import org.teavm.backend.wasm.binary.BinaryWriter; import org.teavm.backend.wasm.binary.DataArray; import org.teavm.backend.wasm.binary.DataPrimitives; @@ -33,6 +33,8 @@ import org.teavm.backend.wasm.binary.DataType; import org.teavm.backend.wasm.binary.DataValue; import org.teavm.backend.wasm.debug.DebugClassLayout; import org.teavm.backend.wasm.debug.info.FieldType; +import org.teavm.backend.wasm.model.WasmFunction; +import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.render.WasmBinaryStatsCollector; import org.teavm.common.IntegerArray; import org.teavm.interop.Address; @@ -60,12 +62,12 @@ public class WasmClassGenerator { private ClassReaderSource processedClassSource; private ClassReaderSource classSource; private Characteristics characteristics; - public final NameProvider names; + public final WasmFunctionRepository functions; + public final WasmModule module; private Map binaryDataMap = new LinkedHashMap<>(); private BinaryWriter binaryWriter; - private Map functions = new HashMap<>(); - private List functionTable = new ArrayList<>(); - private ObjectIntMap functionIdMap = new ObjectIntHashMap<>(); + private Map tableFunctions = new HashMap<>(); + private ObjectIntMap functionIdMap = new ObjectIntHashMap<>(); private VirtualTableProvider vtableProvider; private TagRegistry tagRegistry; private WasmStringPool stringPool; @@ -122,7 +124,7 @@ public class WasmClassGenerator { public WasmClassGenerator(ClassReaderSource processedClassSource, ClassReaderSource classSource, VirtualTableProvider vtableProvider, TagRegistry tagRegistry, BinaryWriter binaryWriter, - NameProvider names, ClassMetadataRequirements metadataRequirements, + WasmFunctionRepository functions, WasmModule module, ClassMetadataRequirements metadataRequirements, ClassInitializerInfo classInitializerInfo, Characteristics characteristics, DwarfClassGenerator dwarfClassGenerator, WasmBinaryStatsCollector statsCollector) { this.processedClassSource = processedClassSource; @@ -130,8 +132,9 @@ public class WasmClassGenerator { this.vtableProvider = vtableProvider; this.tagRegistry = tagRegistry; this.binaryWriter = binaryWriter; + this.functions = functions; + this.module = module; this.stringPool = new WasmStringPool(this, binaryWriter, statsCollector); - this.names = names; this.metadataRequirements = metadataRequirements; this.classInitializerInfo = classInitializerInfo; this.characteristics = characteristics; @@ -223,7 +226,7 @@ public class WasmClassGenerator { binaryData.data = wrapper.getValue(0); binaryData.data.setInt(CLASS_SIZE, 4); binaryData.data.setAddress(CLASS_ITEM_TYPE, itemBinaryData.start); - binaryData.data.setInt(CLASS_IS_INSTANCE, getFunctionPointer(names.forSupertypeFunction(type))); + binaryData.data.setInt(CLASS_IS_INSTANCE, getFunctionPointer(functions.forSupertype(type))); binaryData.data.setInt(CLASS_CANARY, RuntimeClass.computeCanary(4, 0)); binaryData.data.setAddress(CLASS_NAME, stringPool.getStringPointer(type.toString().replace('/', '.'))); binaryData.data.setAddress(CLASS_SIMPLE_NAME, 0); @@ -238,7 +241,7 @@ public class WasmClassGenerator { private DataValue createPrimitiveClassData(DataValue value, int size, ValueType type) { value.setInt(CLASS_SIZE, size); value.setInt(CLASS_FLAGS, RuntimeClass.PRIMITIVE); - value.setInt(CLASS_IS_INSTANCE, getFunctionPointer(names.forSupertypeFunction(type))); + value.setInt(CLASS_IS_INSTANCE, getFunctionPointer(functions.forSupertype(type))); value.setAddress(CLASS_SIMPLE_NAME, 0); value.setInt(CLASS_INIT, -1); value.setInt(CLASS_TAG, Integer.MAX_VALUE); @@ -282,16 +285,12 @@ public class WasmClassGenerator { return value; } - public Iterable getFunctionTable() { - return functionTable; - } - - public int getFunctionPointer(String name) { - var result = functionIdMap.getOrDefault(name, -1); + public int getFunctionPointer(WasmFunction function) { + var result = functionIdMap.getOrDefault(function, -1); if (result < 0) { - result = functionTable.size(); - functionTable.add(name); - functionIdMap.put(name, result); + result = module.getFunctionTable().size(); + module.getFunctionTable().add(function); + functionIdMap.put(function, result); } return result; } @@ -326,7 +325,7 @@ public class WasmClassGenerator { header.setInt(CLASS_CANARY, RuntimeClass.computeCanary(occupiedSize, tag)); int nameAddress = requirements.name() ? stringPool.getStringPointer(name) : 0; header.setAddress(CLASS_NAME, nameAddress); - header.setInt(CLASS_IS_INSTANCE, getFunctionPointer(names.forSupertypeFunction(ValueType.object(name)))); + header.setInt(CLASS_IS_INSTANCE, getFunctionPointer(functions.forSupertype(ValueType.object(name)))); header.setAddress(CLASS_PARENT, parentPtr); ClassReader cls = processedClassSource.get(name); @@ -381,7 +380,7 @@ public class WasmClassGenerator { if (cls != null && binaryData.start >= 0 && cls.getMethod(new MethodDescriptor("", ValueType.VOID)) != null && classInitializerInfo.isDynamicInitializer(name)) { - header.setInt(CLASS_INIT, getFunctionPointer(names.forClassInitializer(name))); + header.setInt(CLASS_INIT, getFunctionPointer(functions.forClassInitializer(name))); } else { header.setInt(CLASS_INIT, -1); } @@ -458,8 +457,8 @@ public class WasmClassGenerator { if (method != null) { VirtualTableEntry entry = vtable.getEntry(method); if (entry != null) { - methodIndex = functions.computeIfAbsent(entry.getImplementor(), - implementor -> getFunctionPointer(names.forMethod(implementor))); + methodIndex = tableFunctions.computeIfAbsent(entry.getImplementor(), + implementor -> getFunctionPointer(functions.forInstanceMethod(implementor))); } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java index 9804119f4..162b1b2e7 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java @@ -20,9 +20,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import org.teavm.backend.lowlevel.generate.NameProvider; +import org.teavm.backend.wasm.WasmFunctionRepository; +import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.generators.WasmMethodGenerator; import org.teavm.backend.wasm.intrinsics.WasmIntrinsic; -import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmTag; import org.teavm.diagnostics.Diagnostics; @@ -43,7 +44,9 @@ import org.teavm.model.lowlevel.Characteristics; public class WasmGenerationContext { private ClassReaderSource classSource; - private WasmModule module; + public final WasmModule module; + public final WasmFunctionTypes functionTypes; + public final WasmFunctionRepository functions; private Diagnostics diagnostics; private VirtualTableProvider vtableProvider; private TagRegistry tagRegistry; @@ -58,11 +61,14 @@ public class WasmGenerationContext { private WasmTag exceptionTag; public final List callSites = new ArrayList<>(); - public WasmGenerationContext(ClassReaderSource classSource, WasmModule module, Diagnostics diagnostics, - VirtualTableProvider vtableProvider, TagRegistry tagRegistry, WasmStringPool stringPool, - NameProvider names, Characteristics characteristics, WasmTag exceptionTag) { + public WasmGenerationContext(ClassReaderSource classSource, WasmModule module, WasmFunctionTypes functionTypes, + WasmFunctionRepository functions, Diagnostics diagnostics, VirtualTableProvider vtableProvider, + TagRegistry tagRegistry, WasmStringPool stringPool, NameProvider names, Characteristics characteristics, + WasmTag exceptionTag) { this.classSource = classSource; this.module = module; + this.functionTypes = functionTypes; + this.functions = functions; this.diagnostics = diagnostics; this.vtableProvider = vtableProvider; this.tagRegistry = tagRegistry; @@ -135,10 +141,6 @@ public class WasmGenerationContext { }); } - public WasmFunction getFunction(String name) { - return module.getFunctions().get(name); - } - public ClassReaderSource getClassSource() { return classSource; } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index 417c73bbc..77abc4d28 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -66,7 +66,8 @@ import org.teavm.ast.UnaryExpr; import org.teavm.ast.UnwrapArrayExpr; import org.teavm.ast.VariableExpr; import org.teavm.ast.WhileStatement; -import org.teavm.backend.lowlevel.generate.NameProvider; +import org.teavm.backend.wasm.WasmFunctionRepository; +import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.WasmHeap; import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.binary.BinaryWriter; @@ -74,6 +75,7 @@ import org.teavm.backend.wasm.binary.DataPrimitives; import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmLocal; +import org.teavm.backend.wasm.model.WasmNumType; import org.teavm.backend.wasm.model.WasmTag; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmBlock; @@ -196,12 +198,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { this.firstVariable = firstVariable; tempVars = new TemporaryVariablePool(function); exprCache = new ExpressionCache(tempVars); - typeInference = new WasmTypeInference(context); + typeInference = new WasmTypeInference(); this.async = async; this.managed = context.characteristics.isManaged(currentMethod); } - void generate(Statement statement, List target) { var lastTargetSize = target.size(); resultConsumer = target; @@ -212,7 +213,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { rethrowBlock.getBody().addAll(body); body.clear(); target.add(rethrowBlock); - var valueToReturn = WasmExpression.defaultValueOfType(function.getResult()); + var valueToReturn = WasmExpression.defaultValueOfType(function.getType().getReturnType()); if (valueToReturn != null) { target.add(new WasmReturn(valueToReturn)); } @@ -254,7 +255,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { default: Class type = convertType(expr.getType()); MethodReference method = new MethodReference(WasmRuntime.class, "remainder", type, type, type); - WasmCall call = new WasmCall(context.names.forMethod(method), false); + WasmCall call = new WasmCall(context.functions.forStaticMethod(method)); accept(expr.getFirstOperand()); call.getArguments().add(result); @@ -308,7 +309,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { case COMPARE: { Class type = convertType(expr.getType()); MethodReference method = new MethodReference(WasmRuntime.class, "compare", type, type, int.class); - WasmCall call = new WasmCall(context.names.forMethod(method), false); + var call = new WasmCall(context.functions.forStaticMethod(method)); accept(expr.getFirstOperand()); call.getArguments().add(result); @@ -367,7 +368,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { case LEFT_SHIFT: case RIGHT_SHIFT: case UNSIGNED_RIGHT_SHIFT: - second = new WasmConversion(WasmType.INT32, WasmType.INT64, false, second); + second = new WasmConversion(WasmNumType.INT32, WasmNumType.INT64, false, second); break; default: break; @@ -529,7 +530,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { var callSiteId = generateCallSiteId(location); block.getBody().add(generateRegisterCallSite(callSiteId, location)); - var call = new WasmCall(context.names.forMethod(THROW_NPE_METHOD)); + var call = new WasmCall(context.functions.forStaticMethod(THROW_NPE_METHOD)); block.getBody().add(call); if (context.getExceptionTag() == null) { @@ -1155,12 +1156,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) { MethodReader method = context.getClassSource().resolve(expr.getMethod()); MethodReference reference = method != null ? method.getReference() : expr.getMethod(); - String methodName = context.names.forMethod(reference); + var function = context.functions.forMethod(reference, expr.getType() == InvocationType.STATIC); - WasmCall call = new WasmCall(methodName); - if (context.getImportedMethod(reference) != null) { - call.setImported(true); - } + var call = new WasmCall(function); for (Expr argument : expr.getArguments()) { accept(argument); call.getArguments().add(result); @@ -1179,8 +1177,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { block.getBody().add(new WasmSetLocal(tmp, allocateObject(expr.getMethod().getClassName(), expr.getLocation()))); - String methodName = context.names.forMethod(expr.getMethod()); - WasmCall call = new WasmCall(methodName); + var function = context.functions.forInstanceMethod(expr.getMethod()); + var call = new WasmCall(function); call.getArguments().add(new WasmGetLocal(tmp)); for (Expr argument : expr.getArguments()) { accept(argument); @@ -1220,14 +1218,14 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { var methodIndex = new WasmLoadInt32(4, classRef, WasmInt32Subtype.INT32); methodIndex.setOffset(vtableIndex * 4 + vtableOffset); - WasmIndirectCall call = new WasmIndirectCall(methodIndex); - call.getParameterTypes().add(WasmType.INT32); - for (int i = 0; i < expr.getMethod().parameterCount(); ++i) { - call.getParameterTypes().add(WasmGeneratorUtil.mapType(expr.getMethod().parameterType(i))); - } - if (expr.getMethod().getReturnType() != ValueType.VOID) { - call.setReturnType(WasmGeneratorUtil.mapType(expr.getMethod().getReturnType())); + var parameterTypes = new WasmType[expr.getMethod().parameterCount() + 1]; + parameterTypes[0] = WasmType.INT32; + for (var i = 0; i < expr.getMethod().parameterCount(); ++i) { + parameterTypes[i + 1] = WasmGeneratorUtil.mapType(expr.getMethod().parameterType(i)); } + var functionType = context.functionTypes.of(WasmGeneratorUtil.mapType(expr.getMethod().getReturnType()), + parameterTypes); + var call = new WasmIndirectCall(methodIndex, functionType); call.getArguments().add(instance); for (int i = 1; i < expr.getArguments().size(); ++i) { @@ -1267,7 +1265,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { stackVariable = tempVars.acquire(WasmType.INT32); stackVariable.setName("__stack__"); InvocationExpr expr = new InvocationExpr(); - expr.setType(InvocationType.SPECIAL); + expr.setType(InvocationType.STATIC); expr.setMethod(new MethodReference(WasmRuntime.class, "allocStack", int.class, Address.class)); expr.getArguments().add(sizeExpr); expr.acceptVisitor(this); @@ -1506,9 +1504,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { private WasmExpression allocateObject(String className, TextLocation location) { int tag = classGenerator.getClassPointer(ValueType.object(className)); - String allocName = context.names.forMethod(new MethodReference(Allocator.class, "allocate", + var allocFunction = context.functions.forStaticMethod(new MethodReference(Allocator.class, "allocate", RuntimeClass.class, Address.class)); - WasmCall call = new WasmCall(allocName); + WasmCall call = new WasmCall(allocFunction); call.getArguments().add(new WasmInt32Constant(tag)); call.setLocation(location); return call; @@ -1525,9 +1523,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { ValueType type = expr.getType(); int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type)); - String allocName = context.names.forMethod(new MethodReference(Allocator.class, "allocateArray", + var allocFunction = context.functions.forStaticMethod(new MethodReference(Allocator.class, "allocateArray", RuntimeClass.class, int.class, Address.class)); - WasmCall call = new WasmCall(allocName); + var call = new WasmCall(allocFunction); call.getArguments().add(new WasmInt32Constant(classPointer)); accept(expr.getLength()); call.getArguments().add(result); @@ -1576,9 +1574,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { WasmLocal array = tempVars.acquire(WasmType.INT32); int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type)); - String allocName = context.names.forMethod(new MethodReference(Allocator.class, "allocateArray", + var allocFunction = context.functions.forStaticMethod(new MethodReference(Allocator.class, "allocateArray", RuntimeClass.class, int.class, Address.class)); - WasmCall call = new WasmCall(allocName); + WasmCall call = new WasmCall(allocFunction); call.getArguments().add(new WasmInt32Constant(classPointer)); call.getArguments().add(new WasmInt32Constant(expr.getData().size())); call.setLocation(expr.getLocation()); @@ -1616,9 +1614,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } int classPointer = classGenerator.getClassPointer(type); - String allocName = context.names.forMethod(new MethodReference(Allocator.class, "allocateMultiArray", + var allocFunction = context.functions.forStaticMethod(new MethodReference(Allocator.class, "allocateMultiArray", RuntimeClass.class, Address.class, int.class, RuntimeArray.class)); - WasmCall call = new WasmCall(allocName); + var call = new WasmCall(allocFunction); call.getArguments().add(new WasmInt32Constant(classPointer)); call.getArguments().add(new WasmInt32Constant(dimensionList)); call.getArguments().add(new WasmInt32Constant(expr.getDimensions().size())); @@ -1673,7 +1671,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } private WasmExpression instanceOfImpl(WasmExpression expression, ValueType type) { - WasmCall supertypeCall = new WasmCall(context.names.forSupertypeFunction(type)); + var supertypeCall = new WasmCall(context.functions.forSupertype(type)); WasmExpression classRef = new WasmLoadInt32(4, expression, WasmInt32Subtype.INT32); classRef = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, classRef, new WasmInt32Constant(3)); @@ -1687,7 +1685,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { resultConsumer.add(generateRegisterCallSite(callSiteId, statement.getLocation())); accept(statement.getException()); - var call = new WasmCall(context.names.forMethod(THROW_METHOD), result); + var call = new WasmCall(context.functions.forStaticMethod(THROW_METHOD), result); call.setLocation(statement.getLocation()); resultConsumer.add(call); @@ -1728,7 +1726,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { nullCheck.setResult(valueToCast.expr()); block.getBody().add(new WasmDrop(nullCheck)); - var supertypeCall = new WasmCall(context.names.forSupertypeFunction(expr.getTarget())); + var supertypeCall = new WasmCall(context.functions.forSupertype(expr.getTarget())); WasmExpression classRef = new WasmLoadInt32(4, valueToCast.expr(), WasmInt32Subtype.INT32); classRef = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, classRef, new WasmInt32Constant(3)); @@ -1741,7 +1739,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { var callSiteId = generateCallSiteId(expr.getLocation()); block.getBody().add(generateRegisterCallSite(callSiteId, expr.getLocation())); - var call = new WasmCall(context.names.forMethod(THROW_CCE_METHOD)); + var call = new WasmCall(context.functions.forStaticMethod(THROW_CCE_METHOD)); block.getBody().add(call); if (context.getExceptionTag() == null) { @@ -1762,7 +1760,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { @Override public void visit(InitClassStatement statement) { if (classGenerator.hasClinit(statement.getClassName())) { - var call = new WasmCall(context.names.forClassInitializer(statement.getClassName())); + var call = new WasmCall(context.functions.forClassInitializer(statement.getClassName())); call.setLocation(statement.getLocation()); var callSiteId = generateCallSiteId(statement.getLocation()); @@ -1853,8 +1851,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { var tryCatch = tryCatchStatements.get(i); var catchBlock = catchBlocks.get(i); catchBlock.getBody().add(currentBlock); - var catchMethodName = context.names.forMethod(CATCH_METHOD); - var catchCall = new WasmCall(catchMethodName); + var catchFunction = context.functions.forStaticMethod(CATCH_METHOD); + var catchCall = new WasmCall(catchFunction); var catchWrapper = tryCatch.getExceptionVariable() != null ? new WasmSetLocal(localVar(tryCatch.getExceptionVariable()), catchCall) : new WasmDrop(catchCall); @@ -1887,8 +1885,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { tryBlock.getCatches().add(catchClause); innerCatchBlock.getBody().add(tryBlock); - var obj = exprCache.create(new WasmCall(context.names.forMethod(PEEK_EXCEPTION_METHOD)), WasmType.INT32, - null, innerCatchBlock.getBody()); + var obj = exprCache.create(new WasmCall(context.functions.forStaticMethod(PEEK_EXCEPTION_METHOD)), + WasmType.INT32, null, innerCatchBlock.getBody()); var currentBlock = innerCatchBlock; boolean catchesAll = false; for (int i = tryCatchStatements.size() - 1; i >= 0; --i) { @@ -1916,8 +1914,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { var catchBlock = catchBlocks.get(i); catchBlock.getBody().add(currentBlock); - var catchMethodName = context.names.forMethod(CATCH_METHOD); - var catchCall = new WasmCall(catchMethodName); + var catchFunction = context.functions.forStaticMethod(CATCH_METHOD); + var catchCall = new WasmCall(catchFunction); var catchWrapper = tryCatch.getExceptionVariable() != null ? new WasmSetLocal(localVar(tryCatch.getExceptionVariable()), catchCall) : new WasmDrop(catchCall); @@ -1954,7 +1952,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { @Override public void visit(MonitorEnterStatement statement) { - WasmCall call = new WasmCall(context.names.forMethod(async ? MONITOR_ENTER : MONITOR_ENTER_SYNC)); + var call = new WasmCall(context.functions.forStaticMethod(async ? MONITOR_ENTER : MONITOR_ENTER_SYNC)); call.setLocation(statement.getLocation()); statement.getObjectRef().acceptVisitor(this); call.getArguments().add(result); @@ -1967,7 +1965,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { @Override public void visit(MonitorExitStatement statement) { - var call = new WasmCall(context.names.forMethod(async ? MONITOR_EXIT : MONITOR_EXIT_SYNC)); + var call = new WasmCall(context.functions.forStaticMethod(async ? MONITOR_EXIT : MONITOR_EXIT_SYNC)); call.setLocation(statement.getLocation()); statement.getObjectRef().acceptVisitor(this); call.getArguments().add(result); @@ -2021,7 +2019,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { var callSiteId = generateCallSiteId(expr.getLocation()); block.getBody().add(generateRegisterCallSite(callSiteId, expr.getLocation())); - block.getBody().add(new WasmCall(context.names.forMethod(THROW_AIOOBE_METHOD))); + block.getBody().add(new WasmCall(context.functions.forStaticMethod(THROW_AIOOBE_METHOD))); if (context.getExceptionTag() == null) { var br = new WasmBreak(throwJumpTarget()); if (br.getTarget() != rethrowBlock) { @@ -2213,8 +2211,13 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } @Override - public NameProvider getNames() { - return context.names; + public WasmFunctionRepository getFunctions() { + return context.functions; + } + + @Override + public WasmFunctionTypes getFunctionTypes() { + return context.functionTypes; } @Override @@ -2238,8 +2241,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } @Override - public int getFunctionPointer(String name) { - return classGenerator.getFunctionPointer(name); + public int getFunctionPointer(WasmFunction function) { + return classGenerator.getFunctionPointer(function); } @Override diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java index b9622c3ee..80e0f20c3 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java @@ -19,12 +19,10 @@ import java.util.function.Predicate; import org.teavm.ast.RegularMethodNode; import org.teavm.ast.VariableNode; import org.teavm.ast.decompilation.Decompiler; -import org.teavm.backend.lowlevel.generate.NameProvider; import org.teavm.backend.wasm.binary.BinaryWriter; import org.teavm.backend.wasm.debug.info.VariableType; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmLocal; -import org.teavm.backend.wasm.model.WasmTag; import org.teavm.backend.wasm.model.WasmType; import org.teavm.interop.Export; import org.teavm.model.AnnotationReader; @@ -32,8 +30,8 @@ import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolderSource; import org.teavm.model.ElementModifier; import org.teavm.model.MethodHolder; +import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; -import org.teavm.model.ValueType; public class WasmGenerator { private Decompiler decompiler; @@ -41,9 +39,7 @@ public class WasmGenerator { private WasmGenerationContext context; private WasmClassGenerator classGenerator; private BinaryWriter binaryWriter; - private NameProvider names; private Predicate asyncMethods; - private WasmTag exceptionTag; public WasmGenerator(Decompiler decompiler, ClassHolderSource classSource, WasmGenerationContext context, WasmClassGenerator classGenerator, BinaryWriter binaryWriter, @@ -53,35 +49,15 @@ public class WasmGenerator { this.context = context; this.classGenerator = classGenerator; this.binaryWriter = binaryWriter; - names = classGenerator.names; this.asyncMethods = asyncMethods; } - public WasmFunction generateDefinition(MethodReference methodReference) { - ClassHolder cls = classSource.get(methodReference.getClassName()); - MethodHolder method = cls.getMethod(methodReference.getDescriptor()); - WasmFunction function = new WasmFunction(names.forMethod(method.getReference())); - function.setJavaMethod(methodReference); - - if (!method.hasModifier(ElementModifier.STATIC)) { - function.getParameters().add(WasmType.INT32); - } - for (int i = 0; i < method.parameterCount(); ++i) { - function.getParameters().add(WasmGeneratorUtil.mapType(method.parameterType(i))); - } - if (method.getResultType() != ValueType.VOID) { - function.setResult(WasmGeneratorUtil.mapType(method.getResultType())); - } - - return function; - } - public WasmFunction generate(MethodReference methodReference, MethodHolder bodyMethod) { ClassHolder cls = classSource.get(methodReference.getClassName()); MethodHolder method = cls.getMethod(methodReference.getDescriptor()); RegularMethodNode methodAst = decompiler.decompileRegular(bodyMethod); - WasmFunction function = context.getFunction(names.forMethod(methodReference)); + WasmFunction function = context.functions.forMethod(method); int firstVariable = method.hasModifier(ElementModifier.STATIC) ? 1 : 0; for (int i = firstVariable; i < methodAst.getVariables().size(); ++i) { VariableNode variable = methodAst.getVariables().get(i); @@ -120,10 +96,10 @@ public class WasmGenerator { } } - public WasmFunction generateNative(MethodReference methodReference) { - WasmFunction function = context.getFunction(names.forMethod(methodReference)); + public WasmFunction generateNative(MethodReader method) { + var function = context.functions.forMethod(method); - WasmGenerationContext.ImportedMethod importedMethod = context.getImportedMethod(methodReference); + var importedMethod = context.getImportedMethod(method.getReference()); if (importedMethod != null) { function.setImportName(importedMethod.name); function.setImportModule(importedMethod.module); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGeneratorUtil.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGeneratorUtil.java index 481220b8e..0495cc86e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGeneratorUtil.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGeneratorUtil.java @@ -16,6 +16,7 @@ package org.teavm.backend.wasm.generate; import org.teavm.ast.OperationType; +import org.teavm.backend.wasm.model.WasmNumType; import org.teavm.backend.wasm.model.WasmType; import org.teavm.model.ValueType; import org.teavm.model.util.VariableType; @@ -24,16 +25,16 @@ public final class WasmGeneratorUtil { private WasmGeneratorUtil() { } - public static WasmType mapType(OperationType type) { + public static WasmNumType mapType(OperationType type) { switch (type) { case INT: - return WasmType.INT32; + return WasmNumType.INT32; case LONG: - return WasmType.INT64; + return WasmNumType.INT64; case FLOAT: - return WasmType.FLOAT32; + return WasmNumType.FLOAT32; case DOUBLE: - return WasmType.FLOAT64; + return WasmNumType.FLOAT64; } throw new IllegalArgumentException(type.toString()); } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmInteropFunctionGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmInteropFunctionGenerator.java index a4f431b33..78e63bc4a 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmInteropFunctionGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmInteropFunctionGenerator.java @@ -15,6 +15,7 @@ */ package org.teavm.backend.wasm.generate; +import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmModule; @@ -39,48 +40,49 @@ import org.teavm.runtime.RuntimeClass; public class WasmInteropFunctionGenerator { private WasmClassGenerator classGenerator; + private WasmFunctionTypes functionTypes; - public WasmInteropFunctionGenerator(WasmClassGenerator classGenerator) { + public WasmInteropFunctionGenerator(WasmClassGenerator classGenerator, WasmFunctionTypes functionTypes) { this.classGenerator = classGenerator; + this.functionTypes = functionTypes; } public void generateFunctions(WasmModule module) { - module.add(allocateString()); - module.add(stringData()); + module.functions.add(allocateString()); + module.functions.add(stringData()); - module.add(allocateArray("teavm_allocateObjectArray", ValueType.parse(Object.class))); - module.add(allocateArray("teavm_allocateStringArray", ValueType.parse(String.class))); - module.add(allocateArray("teavm_allocateByteArray", ValueType.parse(byte.class))); - module.add(allocateArray("teavm_allocateShortArray", ValueType.parse(short.class))); - module.add(allocateArray("teavm_allocateCharArray", ValueType.parse(char.class))); - module.add(allocateArray("teavm_allocateIntArray", ValueType.parse(int.class))); - module.add(allocateArray("teavm_allocateLongArray", ValueType.parse(long.class))); - module.add(allocateArray("teavm_allocateFloatArray", ValueType.parse(float.class))); - module.add(allocateArray("teavm_allocateDoubleArray", ValueType.parse(double.class))); - module.add(arrayData("teavm_objectArrayData", 4)); - module.add(arrayData("teavm_byteArrayData", 1)); - module.add(arrayData("teavm_shortArrayData", 2)); - module.add(arrayData("teavm_charArrayData", 2)); - module.add(arrayData("teavm_intArrayData", 4)); - module.add(arrayData("teavm_longArrayData", 8)); - module.add(arrayData("teavm_floatArrayData", 4)); - module.add(arrayData("teavm_doubleArrayData", 8)); - module.add(arrayLength()); + module.functions.add(allocateArray("teavm_allocateObjectArray", ValueType.parse(Object.class))); + module.functions.add(allocateArray("teavm_allocateStringArray", ValueType.parse(String.class))); + module.functions.add(allocateArray("teavm_allocateByteArray", ValueType.parse(byte.class))); + module.functions.add(allocateArray("teavm_allocateShortArray", ValueType.parse(short.class))); + module.functions.add(allocateArray("teavm_allocateCharArray", ValueType.parse(char.class))); + module.functions.add(allocateArray("teavm_allocateIntArray", ValueType.parse(int.class))); + module.functions.add(allocateArray("teavm_allocateLongArray", ValueType.parse(long.class))); + module.functions.add(allocateArray("teavm_allocateFloatArray", ValueType.parse(float.class))); + module.functions.add(allocateArray("teavm_allocateDoubleArray", ValueType.parse(double.class))); + module.functions.add(arrayData("teavm_objectArrayData", 4)); + module.functions.add(arrayData("teavm_byteArrayData", 1)); + module.functions.add(arrayData("teavm_shortArrayData", 2)); + module.functions.add(arrayData("teavm_charArrayData", 2)); + module.functions.add(arrayData("teavm_intArrayData", 4)); + module.functions.add(arrayData("teavm_longArrayData", 8)); + module.functions.add(arrayData("teavm_floatArrayData", 4)); + module.functions.add(arrayData("teavm_doubleArrayData", 8)); + module.functions.add(arrayLength()); } private WasmFunction allocateString() { - WasmFunction function = new WasmFunction("teavm_allocateString"); + var function = new WasmFunction(functionTypes.of(WasmType.INT32, WasmType.INT32)); + function.setName("teavm_allocateString"); function.setExportName(function.getName()); - function.setResult(WasmType.INT32); - function.getParameters().add(WasmType.INT32); WasmLocal sizeLocal = new WasmLocal(WasmType.INT32, "size"); function.add(sizeLocal); - String constructorName = classGenerator.names.forMethod(new MethodReference(String.class, "allocate", + var constructor = classGenerator.functions.forStaticMethod(new MethodReference(String.class, "allocate", int.class, String.class)); - WasmCall constructorCall = new WasmCall(constructorName); + WasmCall constructorCall = new WasmCall(constructor); constructorCall.getArguments().add(new WasmGetLocal(sizeLocal)); function.getBody().add(constructorCall); @@ -90,18 +92,17 @@ public class WasmInteropFunctionGenerator { } private WasmFunction allocateArray(String name, ValueType type) { - WasmFunction function = new WasmFunction(name); + var function = new WasmFunction(functionTypes.of(WasmType.INT32, WasmType.INT32)); + function.setName(name); function.setExportName(name); - function.setResult(WasmType.INT32); - function.getParameters().add(WasmType.INT32); WasmLocal sizeLocal = new WasmLocal(WasmType.INT32, "size"); function.add(sizeLocal); int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type)); - String allocName = classGenerator.names.forMethod(new MethodReference(Allocator.class, "allocateArray", - RuntimeClass.class, int.class, Address.class)); - WasmCall call = new WasmCall(allocName); + var allocFunction = classGenerator.functions.forStaticMethod(new MethodReference(Allocator.class, + "allocateArray", RuntimeClass.class, int.class, Address.class)); + WasmCall call = new WasmCall(allocFunction); call.getArguments().add(new WasmInt32Constant(classPointer)); call.getArguments().add(new WasmGetLocal(sizeLocal)); @@ -111,10 +112,9 @@ public class WasmInteropFunctionGenerator { } private WasmFunction stringData() { - WasmFunction function = new WasmFunction("teavm_stringData"); + var function = new WasmFunction(functionTypes.of(WasmType.INT32, WasmType.INT32)); + function.setName("teavm_stringData"); function.setExportName(function.getName()); - function.setResult(WasmType.INT32); - function.getParameters().add(WasmType.INT32); WasmLocal stringLocal = new WasmLocal(WasmType.INT32, "string"); function.add(stringLocal); @@ -128,10 +128,9 @@ public class WasmInteropFunctionGenerator { } private WasmFunction arrayData(String name, int alignment) { - WasmFunction function = new WasmFunction(name); + var function = new WasmFunction(functionTypes.of(WasmType.INT32, WasmType.INT32)); + function.setName(name); function.setExportName(function.getName()); - function.setResult(WasmType.INT32); - function.getParameters().add(WasmType.INT32); WasmLocal arrayLocal = new WasmLocal(WasmType.INT32, "array"); function.add(arrayLocal); @@ -147,10 +146,9 @@ public class WasmInteropFunctionGenerator { } private WasmFunction arrayLength() { - WasmFunction function = new WasmFunction("teavm_arrayLength"); + WasmFunction function = new WasmFunction(functionTypes.of(WasmType.INT32, WasmType.INT32)); + function.setName("teavm_arrayLength"); function.setExportName(function.getName()); - function.setResult(WasmType.INT32); - function.getParameters().add(WasmType.INT32); WasmLocal arrayLocal = new WasmLocal(WasmType.INT32, "array"); function.add(arrayLocal); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmSpecialFunctionGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmSpecialFunctionGenerator.java index e21ee588b..0923613ab 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmSpecialFunctionGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmSpecialFunctionGenerator.java @@ -16,6 +16,7 @@ package org.teavm.backend.wasm.generate; import java.util.List; +import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.WasmHeap; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmModule; @@ -28,25 +29,27 @@ import org.teavm.model.FieldReference; public class WasmSpecialFunctionGenerator { private WasmClassGenerator classGenerator; + private WasmFunctionTypes functionTypes; private List regionSizeExpressions; - public WasmSpecialFunctionGenerator(WasmClassGenerator classGenerator, + public WasmSpecialFunctionGenerator(WasmClassGenerator classGenerator, WasmFunctionTypes functionTypes, List regionSizeExpressions) { this.classGenerator = classGenerator; + this.functionTypes = functionTypes; this.regionSizeExpressions = regionSizeExpressions; } public void generateSpecialFunctions(WasmModule module) { - module.add(javaHeapAddress()); - module.add(availableBytes()); - module.add(regionsAddress()); - module.add(regionSize()); + module.functions.add(javaHeapAddress()); + module.functions.add(availableBytes()); + module.functions.add(regionsAddress()); + module.functions.add(regionSize()); } private WasmFunction javaHeapAddress() { - WasmFunction function = new WasmFunction("teavm_javaHeapAddress"); + var function = new WasmFunction(functionTypes.of(WasmType.INT32)); + function.setName("teavm_javaHeapAddress"); function.setExportName("teavm_javaHeapAddress"); - function.setResult(WasmType.INT32); int address = classGenerator.getFieldOffset(new FieldReference(WasmHeap.class.getName(), "heapAddress")); function.getBody().add(new WasmReturn( @@ -55,9 +58,9 @@ public class WasmSpecialFunctionGenerator { } private WasmFunction availableBytes() { - WasmFunction function = new WasmFunction("teavm_availableBytes"); + var function = new WasmFunction(functionTypes.of(WasmType.INT32)); + function.setName("teavm_availableBytes"); function.setExportName("teavm_availableBytes"); - function.setResult(WasmType.INT32); int address = classGenerator.getFieldOffset(new FieldReference(WasmHeap.class.getName(), "heapSize")); function.getBody().add(new WasmReturn( @@ -66,9 +69,9 @@ public class WasmSpecialFunctionGenerator { } private WasmFunction regionsAddress() { - WasmFunction function = new WasmFunction("teavm_regionsAddress"); + var function = new WasmFunction(functionTypes.of(WasmType.INT32)); function.setExportName("teavm_regionsAddress"); - function.setResult(WasmType.INT32); + function.setName("teavm_regionsAddress"); int address = classGenerator.getFieldOffset(new FieldReference(WasmHeap.class.getName(), "regionsAddress")); function.getBody().add(new WasmReturn( @@ -77,9 +80,9 @@ public class WasmSpecialFunctionGenerator { } private WasmFunction regionSize() { - WasmFunction function = new WasmFunction("teavm_regionSize"); + var function = new WasmFunction(functionTypes.of(WasmType.INT32)); function.setExportName("teavm_regionSize"); - function.setResult(WasmType.INT32); + function.setName("teavm_regionSize"); WasmInt32Constant constant = new WasmInt32Constant(0); regionSizeExpressions.add(constant); diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/ArrayGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generators/ArrayGenerator.java index ef0f30486..79d9ccdab 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generators/ArrayGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generators/ArrayGenerator.java @@ -133,7 +133,7 @@ public class ArrayGenerator implements WasmMethodGenerator { new WasmGetLocal(arrayVar)); int baseAddr = BinaryWriter.align(base, 1 << shift[i]); - WasmCall call = new WasmCall(context.getNames().forMethod(methodRef)); + WasmCall call = new WasmCall(context.getFunctions().forStaticMethod(methodRef)); switch (primitiveTypes[i].getKind()) { case BOOLEAN: diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/WasmMethodGeneratorContext.java b/core/src/main/java/org/teavm/backend/wasm/generators/WasmMethodGeneratorContext.java index 17c03dfa9..63d6458a3 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generators/WasmMethodGeneratorContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/generators/WasmMethodGeneratorContext.java @@ -15,7 +15,7 @@ */ package org.teavm.backend.wasm.generators; -import org.teavm.backend.lowlevel.generate.NameProvider; +import org.teavm.backend.wasm.WasmFunctionRepository; import org.teavm.backend.wasm.binary.BinaryWriter; import org.teavm.backend.wasm.generate.WasmClassGenerator; import org.teavm.backend.wasm.generate.WasmStringPool; @@ -29,7 +29,7 @@ public interface WasmMethodGeneratorContext { Diagnostics getDiagnostics(); - NameProvider getNames(); + WasmFunctionRepository getFunctions(); ClassReaderSource getClassSource(); diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/AddressIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/AddressIntrinsic.java index f5c7d210f..2d0c169a8 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/AddressIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/AddressIntrinsic.java @@ -20,7 +20,7 @@ import org.teavm.ast.ConstantExpr; import org.teavm.ast.InvocationExpr; import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.generate.WasmClassGenerator; -import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.WasmNumType; import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmDrop; @@ -64,21 +64,21 @@ public class AddressIntrinsic implements WasmIntrinsic { return manager.generate(invocation.getArguments().get(0)); case "toLong": { WasmExpression value = manager.generate(invocation.getArguments().get(0)); - return new WasmConversion(WasmType.INT32, WasmType.INT64, false, value); + return new WasmConversion(WasmNumType.INT32, WasmNumType.INT64, false, value); } case "fromInt": case "ofObject": return manager.generate(invocation.getArguments().get(0)); case "fromLong": { WasmExpression value = manager.generate(invocation.getArguments().get(0)); - return new WasmConversion(WasmType.INT64, WasmType.INT32, false, value); + return new WasmConversion(WasmNumType.INT64, WasmNumType.INT32, false, value); } case "add": { WasmExpression base = manager.generate(invocation.getArguments().get(0)); if (invocation.getMethod().parameterCount() == 1) { WasmExpression offset = manager.generate(invocation.getArguments().get(1)); if (invocation.getMethod().parameterType(0) == ValueType.LONG) { - offset = new WasmConversion(WasmType.INT64, WasmType.INT32, false, offset); + offset = new WasmConversion(WasmNumType.INT64, WasmNumType.INT32, false, offset); } return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, base, offset); } else { @@ -155,7 +155,7 @@ public class AddressIntrinsic implements WasmIntrinsic { case "align": { MethodReference delegate = new MethodReference(WasmRuntime.class.getName(), invocation.getMethod().getDescriptor()); - WasmCall call = new WasmCall(manager.getNames().forMethod(delegate)); + WasmCall call = new WasmCall(manager.getFunctions().forStaticMethod(delegate)); call.getArguments().addAll(invocation.getArguments().stream() .map(arg -> manager.generate(arg)) .collect(Collectors.toList())); @@ -180,7 +180,7 @@ public class AddressIntrinsic implements WasmIntrinsic { manager.generate(invocation.getArguments().get(0)), manager.generate(invocation.getArguments().get(1)) ); - result = new WasmConversion(WasmType.INT32, WasmType.INT64, true, result); + result = new WasmConversion(WasmNumType.INT32, WasmNumType.INT64, true, result); result.setLocation(invocation.getLocation()); return result; } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/ConsoleIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/ConsoleIntrinsic.java index 7c184ef41..44bffbd0e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/ConsoleIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/ConsoleIntrinsic.java @@ -48,14 +48,14 @@ public class ConsoleIntrinsic implements WasmIntrinsic { public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { switch (invocation.getMethod().getName()) { case "printString": { - String name = manager.getNames().forMethod(PRINT_STRING); - WasmCall call = new WasmCall(name); + var function = manager.getFunctions().forStaticMethod(PRINT_STRING); + var call = new WasmCall(function); call.getArguments().add(manager.generate(invocation.getArguments().get(0))); return call; } case "printInt": { - String name = manager.getNames().forMethod(PRINT_INT); - WasmCall call = new WasmCall(name); + var function = manager.getFunctions().forStaticMethod(PRINT_INT); + var call = new WasmCall(function); call.getArguments().add(manager.generate(invocation.getArguments().get(0))); return call; } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/DoubleIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/DoubleIntrinsic.java index 79fa976d6..aaf53e74c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/DoubleIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/DoubleIntrinsic.java @@ -17,6 +17,7 @@ package org.teavm.backend.wasm.intrinsics; import org.teavm.ast.InvocationExpr; import org.teavm.backend.wasm.model.WasmLocal; +import org.teavm.backend.wasm.model.WasmNumType; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; @@ -68,13 +69,13 @@ public class DoubleIntrinsic implements WasmIntrinsic { case "isFinite": return testIsFinite(manager.generate(invocation.getArguments().get(0))); case "doubleToRawLongBits": { - WasmConversion conversion = new WasmConversion(WasmType.FLOAT64, WasmType.INT64, false, + WasmConversion conversion = new WasmConversion(WasmNumType.FLOAT64, WasmNumType.INT64, false, manager.generate(invocation.getArguments().get(0))); conversion.setReinterpret(true); return conversion; } case "longBitsToDouble": { - WasmConversion conversion = new WasmConversion(WasmType.INT64, WasmType.FLOAT64, false, + WasmConversion conversion = new WasmConversion(WasmNumType.INT64, WasmNumType.FLOAT64, false, manager.generate(invocation.getArguments().get(0))); conversion.setReinterpret(true); return conversion; @@ -89,7 +90,7 @@ public class DoubleIntrinsic implements WasmIntrinsic { WasmBlock block = new WasmBlock(false); block.setType(WasmType.INT32); - WasmConversion conversion = new WasmConversion(WasmType.FLOAT64, WasmType.INT64, false, expression); + WasmConversion conversion = new WasmConversion(WasmNumType.FLOAT64, WasmNumType.INT64, false, expression); conversion.setReinterpret(true); block.getBody().add(new WasmSetLocal(bitsVar, conversion)); @@ -113,7 +114,7 @@ public class DoubleIntrinsic implements WasmIntrinsic { } private WasmExpression testIsInfinite(WasmExpression expression) { - var conversion = new WasmConversion(WasmType.FLOAT64, WasmType.INT64, false, expression); + var conversion = new WasmConversion(WasmNumType.FLOAT64, WasmNumType.INT64, false, expression); conversion.setReinterpret(true); var result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.AND, @@ -123,7 +124,7 @@ public class DoubleIntrinsic implements WasmIntrinsic { } private WasmExpression testIsFinite(WasmExpression expression) { - var conversion = new WasmConversion(WasmType.FLOAT64, WasmType.INT64, false, expression); + var conversion = new WasmConversion(WasmNumType.FLOAT64, WasmNumType.INT64, false, expression); conversion.setReinterpret(true); var result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.AND, diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/FloatIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/FloatIntrinsic.java index 813deca25..8d18ec4fc 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/FloatIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/FloatIntrinsic.java @@ -17,6 +17,7 @@ package org.teavm.backend.wasm.intrinsics; import org.teavm.ast.InvocationExpr; import org.teavm.backend.wasm.model.WasmLocal; +import org.teavm.backend.wasm.model.WasmNumType; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; @@ -67,13 +68,13 @@ public class FloatIntrinsic implements WasmIntrinsic { case "isFinite": return testIsFinite(manager.generate(invocation.getArguments().get(0))); case "floatToRawIntBits": { - WasmConversion conversion = new WasmConversion(WasmType.FLOAT32, WasmType.INT32, false, + WasmConversion conversion = new WasmConversion(WasmNumType.FLOAT32, WasmNumType.INT32, false, manager.generate(invocation.getArguments().get(0))); conversion.setReinterpret(true); return conversion; } case "intBitsToFloat": { - WasmConversion conversion = new WasmConversion(WasmType.INT32, WasmType.FLOAT32, false, + WasmConversion conversion = new WasmConversion(WasmNumType.INT32, WasmNumType.FLOAT32, false, manager.generate(invocation.getArguments().get(0))); conversion.setReinterpret(true); return conversion; @@ -88,7 +89,7 @@ public class FloatIntrinsic implements WasmIntrinsic { WasmBlock block = new WasmBlock(false); block.setType(WasmType.INT32); - WasmConversion conversion = new WasmConversion(WasmType.FLOAT32, WasmType.INT32, false, expression); + WasmConversion conversion = new WasmConversion(WasmNumType.FLOAT32, WasmNumType.INT32, false, expression); conversion.setReinterpret(true); block.getBody().add(new WasmSetLocal(bitsVar, conversion)); @@ -112,7 +113,7 @@ public class FloatIntrinsic implements WasmIntrinsic { } private WasmExpression testIsInfinite(WasmExpression expression) { - var conversion = new WasmConversion(WasmType.FLOAT32, WasmType.INT32, false, expression); + var conversion = new WasmConversion(WasmNumType.FLOAT32, WasmNumType.INT32, false, expression); conversion.setReinterpret(true); var result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, @@ -122,7 +123,7 @@ public class FloatIntrinsic implements WasmIntrinsic { } private WasmExpression testIsFinite(WasmExpression expression) { - var conversion = new WasmConversion(WasmType.FLOAT32, WasmType.INT32, false, expression); + var conversion = new WasmConversion(WasmNumType.FLOAT32, WasmNumType.INT32, false, expression); conversion.setReinterpret(true); var result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/FunctionIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/FunctionIntrinsic.java index a3a097149..e47b97c26 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/FunctionIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/FunctionIntrinsic.java @@ -18,11 +18,11 @@ package org.teavm.backend.wasm.intrinsics; import org.teavm.ast.InvocationExpr; import org.teavm.backend.wasm.generate.WasmClassGenerator; import org.teavm.backend.wasm.generate.WasmGeneratorUtil; +import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmIndirectCall; import org.teavm.interop.Function; import org.teavm.model.MethodReference; -import org.teavm.model.ValueType; public class FunctionIntrinsic implements WasmIntrinsic { private WasmClassGenerator classGenerator; @@ -42,15 +42,16 @@ public class FunctionIntrinsic implements WasmIntrinsic { @Override public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { - WasmExpression selector = manager.generate(invocation.getArguments().get(0)); - WasmIndirectCall call = new WasmIndirectCall(selector); + var parameterTypes = new WasmType[invocation.getMethod().parameterCount()]; + for (var i = 0; i < parameterTypes.length; ++i) { + parameterTypes[i] = WasmGeneratorUtil.mapType(invocation.getMethod().parameterType(i)); + } + var returnType = WasmGeneratorUtil.mapType(invocation.getMethod().getReturnType()); + var functionType = manager.getFunctionTypes().of(returnType, parameterTypes); + + var selector = manager.generate(invocation.getArguments().get(0)); + var call = new WasmIndirectCall(selector, functionType); - for (ValueType type : invocation.getMethod().getParameterTypes()) { - call.getParameterTypes().add(WasmGeneratorUtil.mapType(type)); - } - if (invocation.getMethod().getReturnType() != ValueType.VOID) { - call.setReturnType(WasmGeneratorUtil.mapType(invocation.getMethod().getReturnType())); - } for (int i = 1; i < invocation.getArguments().size(); ++i) { call.getArguments().add(manager.generate(invocation.getArguments().get(i))); } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/GCIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/GCIntrinsic.java index c510ab536..0b33cff54 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/GCIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/GCIntrinsic.java @@ -20,7 +20,7 @@ import java.util.List; import org.teavm.ast.InvocationExpr; import org.teavm.backend.wasm.WasmHeap; import org.teavm.backend.wasm.WasmRuntime; -import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.WasmNumType; import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmConversion; @@ -100,8 +100,8 @@ public class GCIntrinsic implements WasmIntrinsic { return intToLong(getStaticField(manager, "maxHeapSize")); case "resizeHeap": { WasmExpression amount = manager.generate(invocation.getArguments().get(0)); - amount = new WasmConversion(WasmType.INT64, WasmType.INT32, false, amount); - return new WasmCall(manager.getNames().forMethod(RESIZE_HEAP), amount); + amount = new WasmConversion(WasmNumType.INT64, WasmNumType.INT32, false, amount); + return new WasmCall(manager.getFunctions().forStaticMethod(RESIZE_HEAP), amount); } case "regionSize": { WasmInt32Constant result = new WasmInt32Constant(0); @@ -112,7 +112,7 @@ public class GCIntrinsic implements WasmIntrinsic { return intToLong(getStaticField(manager, "heapSize")); case "outOfMemory": { WasmBlock block = new WasmBlock(false); - WasmCall call = new WasmCall(manager.getNames().forMethod(PRINT_OUT_OF_MEMORY)); + WasmCall call = new WasmCall(manager.getFunctions().forStaticMethod(PRINT_OUT_OF_MEMORY)); block.getBody().add(call); block.getBody().add(new WasmUnreachable()); return block; @@ -149,6 +149,6 @@ public class GCIntrinsic implements WasmIntrinsic { } private static WasmExpression intToLong(WasmExpression expression) { - return new WasmConversion(WasmType.INT32, WasmType.INT64, false, expression); + return new WasmConversion(WasmNumType.INT32, WasmNumType.INT64, false, expression); } } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/IntegerIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/IntegerIntrinsic.java index b2bd1941b..38cb6fd79 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/IntegerIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/IntegerIntrinsic.java @@ -56,7 +56,7 @@ public class IntegerIntrinsic implements WasmIntrinsic { manager.generate(invocation.getArguments().get(0)), manager.generate(invocation.getArguments().get(1))); case "compareUnsigned": - return new WasmCall(manager.getNames().forMethod(COMPARE_UNSIGNED), + return new WasmCall(manager.getFunctions().forStaticMethod(COMPARE_UNSIGNED), manager.generate(invocation.getArguments().get(0)), manager.generate(invocation.getArguments().get(1))); default: diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/LongIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/LongIntrinsic.java index 6db12665b..9b0f22f11 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/LongIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/LongIntrinsic.java @@ -56,7 +56,7 @@ public class LongIntrinsic implements WasmIntrinsic { manager.generate(invocation.getArguments().get(0)), manager.generate(invocation.getArguments().get(1))); case "compareUnsigned": - return new WasmCall(manager.getNames().forMethod(COMPARE_UNSIGNED), + return new WasmCall(manager.getFunctions().forStaticMethod(COMPARE_UNSIGNED), manager.generate(invocation.getArguments().get(0)), manager.generate(invocation.getArguments().get(1))); default: diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/ShadowStackIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/ShadowStackIntrinsic.java index 8636b3a6a..6db621777 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/ShadowStackIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/ShadowStackIntrinsic.java @@ -49,7 +49,7 @@ public class ShadowStackIntrinsic implements WasmIntrinsic { MethodReference method = new MethodReference(WasmRuntime.class.getName(), invocation.getMethod().getDescriptor()); expr.setMethod(method); - expr.setType(InvocationType.SPECIAL); + expr.setType(InvocationType.STATIC); expr.getArguments().addAll(invocation.getArguments()); return manager.generate(expr); } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java index f5ef60e88..c182e3ed1 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java @@ -16,9 +16,11 @@ package org.teavm.backend.wasm.intrinsics; import org.teavm.ast.Expr; -import org.teavm.backend.lowlevel.generate.NameProvider; +import org.teavm.backend.wasm.WasmFunctionRepository; +import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.binary.BinaryWriter; import org.teavm.backend.wasm.generate.WasmStringPool; +import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmTag; import org.teavm.backend.wasm.model.WasmType; @@ -38,7 +40,9 @@ public interface WasmIntrinsicManager { Diagnostics getDiagnostics(); - NameProvider getNames(); + WasmFunctionRepository getFunctions(); + + WasmFunctionTypes getFunctionTypes(); WasmLocal getTemporary(WasmType type); @@ -46,7 +50,7 @@ public interface WasmIntrinsicManager { int getClassPointer(ValueType type); - int getFunctionPointer(String name); + int getFunctionPointer(WasmFunction function); void releaseTemporary(WasmLocal local); diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmRuntimeIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmRuntimeIntrinsic.java index fc31737e0..df175ad5a 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmRuntimeIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmRuntimeIntrinsic.java @@ -72,8 +72,8 @@ public class WasmRuntimeIntrinsic implements WasmIntrinsic { return comparison(WasmIntBinaryOperation.GT_SIGNED, WasmFloatBinaryOperation.MAX, invocation, manager); case "callFunctionFromTable": { - var call = new WasmIndirectCall(manager.generate(invocation.getArguments().get(0))); - call.getParameterTypes().add(WasmType.INT32); + var functionType = manager.getFunctionTypes().of(null, WasmType.INT32); + var call = new WasmIndirectCall(manager.generate(invocation.getArguments().get(0)), functionType); call.getArguments().add(manager.generate(invocation.getArguments().get(1))); return call; } @@ -84,12 +84,12 @@ public class WasmRuntimeIntrinsic implements WasmIntrinsic { private static WasmExpression comparison(WasmIntBinaryOperation intOp, WasmFloatBinaryOperation floatOp, InvocationExpr invocation, WasmIntrinsicManager manager) { - WasmType type = WasmGeneratorUtil.mapType(invocation.getMethod().parameterType(0)); + var type = (WasmType.Number) WasmGeneratorUtil.mapType(invocation.getMethod().parameterType(0)); WasmExpression first = manager.generate(invocation.getArguments().get(0)); WasmExpression second = manager.generate(invocation.getArguments().get(1)); - switch (type) { + switch (type.number) { case INT32: return new WasmIntBinary(WasmIntType.INT32, intOp, first, second); case INT64: diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmArray.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmArray.java new file mode 100644 index 000000000..ea1efa9a8 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmArray.java @@ -0,0 +1,45 @@ +/* + * 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.model; + +import java.util.Objects; +import java.util.function.Supplier; + +public class WasmArray extends WasmCompositeType { + private WasmStorageType elementType; + private Supplier elementTypeSupplier; + + public WasmArray(WasmStorageType elementType) { + this.elementType = Objects.requireNonNull(elementType); + } + + public WasmArray(Supplier elementTypeSupplier) { + this.elementTypeSupplier = elementTypeSupplier; + } + + public WasmStorageType getElementType() { + if (elementType == null) { + elementType = elementTypeSupplier.get(); + elementTypeSupplier = null; + } + return elementType; + } + + @Override + public void acceptVisitor(WasmCompositeTypeVisitor visitor) { + visitor.visit(this); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmCollection.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmCollection.java new file mode 100644 index 000000000..3024dafaf --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmCollection.java @@ -0,0 +1,93 @@ +/* + * 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.model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Stream; + +public class WasmCollection implements Iterable { + private List items = new ArrayList<>(); + private List readonlyItems = Collections.unmodifiableList(items); + private boolean indexesInvalid; + + WasmCollection() { + } + + public int size() { + return items.size(); + } + + public boolean isEmpty() { + return items.isEmpty(); + } + + public void add(T entity) { + if (!indexesInvalid) { + entity.index = items.size(); + } + entity.collection = this; + items.add(entity); + } + + public void removeIf(Predicate predicate) { + for (var item : items) { + if (predicate.test(item)) { + item.collection = null; + } + } + if (items.removeIf(predicate)) { + invalidateIndexes(); + } + } + + void invalidateIndexes() { + indexesInvalid = true; + } + + public int indexOf(T entity) { + if (entity.collection != this) { + throw new IllegalArgumentException("Given entity does not belong to this module"); + } + if (indexesInvalid) { + indexesInvalid = false; + var index = 0; + for (var item : items) { + if (item.isImported()) { + item.index = index++; + } + } + for (var item : items) { + if (!item.isImported()) { + item.index = index++; + } + } + } + return entity.index; + } + + @Override + public Iterator iterator() { + return readonlyItems.iterator(); + } + + public Stream stream() { + return readonlyItems.stream(); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmCompositeType.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmCompositeType.java new file mode 100644 index 000000000..eb89b878f --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmCompositeType.java @@ -0,0 +1,32 @@ +/* + * 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.model; + +public abstract class WasmCompositeType extends WasmEntity { + private WasmType.Reference reference; + + WasmCompositeType() { + } + + public WasmType.Reference getReference() { + if (reference == null) { + reference = new WasmType.Reference(this); + } + return reference; + } + + public abstract void acceptVisitor(WasmCompositeTypeVisitor visitor); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmCompositeTypeVisitor.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmCompositeTypeVisitor.java new file mode 100644 index 000000000..40824f3e9 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmCompositeTypeVisitor.java @@ -0,0 +1,24 @@ +/* + * 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.model; + +public interface WasmCompositeTypeVisitor { + void visit(WasmStructure type); + + void visit(WasmArray type); + + void visit(WasmFunctionType type); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmDefaultCompositeTypeVisitor.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmDefaultCompositeTypeVisitor.java new file mode 100644 index 000000000..513cea3ec --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmDefaultCompositeTypeVisitor.java @@ -0,0 +1,31 @@ +/* + * 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.model; + +public class WasmDefaultCompositeTypeVisitor implements WasmCompositeTypeVisitor { + @Override + public void visit(WasmStructure type) { + } + + @Override + public void visit(WasmArray type) { + } + + @Override + public void visit(WasmFunctionType type) { + } + +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmEntity.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmEntity.java new file mode 100644 index 000000000..3d0c99666 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmEntity.java @@ -0,0 +1,25 @@ +/* + * 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.model; + +public abstract class WasmEntity { + int index; + WasmCollection collection; + + boolean isImported() { + return false; + } +} 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 aa252be5c..113a32e62 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 @@ -22,32 +22,29 @@ import java.util.Objects; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.model.MethodReference; -public class WasmFunction { - WasmModule module; +public class WasmFunction extends WasmEntity { private String name; private String exportName; private String importName; private String importModule; - private List parameters = new ArrayList<>(); - private WasmType result; + private WasmFunctionType type; private List localVariables = new ArrayList<>(); private List readonlyLocalVariables = Collections.unmodifiableList(localVariables); private List body = new ArrayList<>(); private MethodReference javaMethod; - public WasmFunction(String name) { - Objects.requireNonNull(name); - this.name = name; - } - - public WasmModule getModule() { - return module; + public WasmFunction(WasmFunctionType type) { + this.type = Objects.requireNonNull(type); } public String getName() { return name; } + public void setName(String name) { + this.name = name; + } + public String getExportName() { return exportName; } @@ -62,6 +59,7 @@ public class WasmFunction { public void setImportName(String importName) { this.importName = importName; + collection.invalidateIndexes(); } public String getImportModule() { @@ -72,16 +70,17 @@ public class WasmFunction { this.importModule = importModule; } - public WasmType getResult() { - return result; + @Override + boolean isImported() { + return importName != null; } - public void setResult(WasmType result) { - this.result = result; + public WasmFunctionType getType() { + return type; } - public List getParameters() { - return parameters; + public void setType(WasmFunctionType type) { + this.type = Objects.requireNonNull(type); } public List getLocalVariables() { diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmFunctionType.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmFunctionType.java new file mode 100644 index 000000000..901d210a0 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmFunctionType.java @@ -0,0 +1,58 @@ +/* + * 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.model; + +import java.util.List; +import java.util.function.Supplier; + +public class WasmFunctionType extends WasmCompositeType { + private List parameterTypes; + private WasmType returnType; + private Supplier> parameterTypesSupplier; + private Supplier returnTypeSupplier; + + public WasmFunctionType(WasmType returnType, List parameterTypes) { + this.returnType = returnType; + this.parameterTypes = parameterTypes; + } + + public WasmFunctionType(Supplier returnTypeSupplier, + Supplier> parameterTypesSupplier) { + this.returnTypeSupplier = returnTypeSupplier; + this.parameterTypesSupplier = parameterTypesSupplier; + } + + public List getParameterTypes() { + if (parameterTypes == null) { + parameterTypes = List.copyOf(parameterTypesSupplier.get()); + parameterTypesSupplier = null; + } + return parameterTypes; + } + + public WasmType getReturnType() { + if (returnTypeSupplier != null) { + returnType = returnTypeSupplier.get(); + returnTypeSupplier = null; + } + return returnType; + } + + @Override + public void acceptVisitor(WasmCompositeTypeVisitor visitor) { + visitor.visit(this); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmModule.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmModule.java index 0807dc24c..799ffdff2 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmModule.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmModule.java @@ -25,37 +25,14 @@ public class WasmModule { private int minMemorySize; private int maxMemorySize; private List segments = new ArrayList<>(); - private Map functions = new LinkedHashMap<>(); - private Map readonlyFunctions = Collections.unmodifiableMap(functions); private List functionTable = new ArrayList<>(); private WasmFunction startFunction; private Map customSections = new LinkedHashMap<>(); private Map readonlyCustomSections = Collections.unmodifiableMap(customSections); - private List tags = new ArrayList<>(); - private List readonlyTags = Collections.unmodifiableList(tags); - public void add(WasmFunction function) { - if (functions.containsKey(function.getName())) { - throw new IllegalArgumentException("Function " + function.getName() + " already defined in this module"); - } - if (function.module != null) { - throw new IllegalArgumentException("Given function is already registered in another module"); - } - functions.put(function.getName(), function); - function.module = this; - } - - public void remove(WasmFunction function) { - if (function.getModule() != this) { - return; - } - function.module = null; - functions.remove(function.getName()); - } - - public Map getFunctions() { - return readonlyFunctions; - } + public final WasmCollection functions = new WasmCollection<>(); + public final WasmCollection types = new WasmCollection<>(); + public final WasmCollection tags = new WasmCollection<>(); public void add(WasmCustomSection customSection) { if (customSections.containsKey(customSection.getName())) { @@ -112,17 +89,4 @@ public class WasmModule { public void setStartFunction(WasmFunction startFunction) { this.startFunction = startFunction; } - - public void addTag(WasmTag tag) { - if (tag.module != null) { - throw new IllegalArgumentException("Given tag already belongs to some module"); - } - tags.add(tag); - tag.module = this; - tag.index = tags.size() - 1; - } - - public List getTags() { - return tags; - } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmNumType.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmNumType.java new file mode 100644 index 000000000..da87bcaf5 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmNumType.java @@ -0,0 +1,23 @@ +/* + * Copyright 2024 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.backend.wasm.model; + +public enum WasmNumType { + INT32, + INT64, + FLOAT32, + FLOAT64 +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmPackedType.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmPackedType.java new file mode 100644 index 000000000..85afbb95f --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmPackedType.java @@ -0,0 +1,21 @@ +/* + * 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.model; + +public enum WasmPackedType { + INT8, + INT16 +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmStorageType.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmStorageType.java new file mode 100644 index 000000000..00ad7d9ee --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmStorageType.java @@ -0,0 +1,51 @@ +/* + * 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.model; + +public abstract class WasmStorageType { + private static final Packed INT16 = new Packed(WasmPackedType.INT16); + private static final Packed INT8 = new Packed(WasmPackedType.INT8); + + private WasmStorageType() { + } + + public static WasmStorageType.Packed packed(WasmPackedType type) { + switch (type) { + case INT8: + return INT8; + case INT16: + return INT16; + default: + throw new IllegalArgumentException(); + } + } + + public static final class Regular extends WasmStorageType { + public final WasmType type; + + Regular(WasmType type) { + this.type = type; + } + } + + public static final class Packed extends WasmStorageType { + public final WasmPackedType type; + + private Packed(WasmPackedType type) { + this.type = type; + } + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmStructure.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmStructure.java new file mode 100644 index 000000000..645bba70a --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmStructure.java @@ -0,0 +1,45 @@ +/* + * 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.model; + +import java.util.List; +import java.util.function.Supplier; + +public class WasmStructure extends WasmCompositeType { + private List fields; + private Supplier> fieldsSupplier; + + public WasmStructure(List fields) { + this.fields = List.copyOf(fields); + } + + public WasmStructure(Supplier> fieldsSupplier) { + this.fieldsSupplier = fieldsSupplier; + } + + public List getFields() { + if (fields == null) { + fields = List.copyOf(fieldsSupplier.get()); + fieldsSupplier = null; + } + return fields; + } + + @Override + public void acceptVisitor(WasmCompositeTypeVisitor visitor) { + visitor.visit(this); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmTag.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmTag.java index 6781f719a..349a680c6 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmTag.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmTag.java @@ -15,16 +15,17 @@ */ package org.teavm.backend.wasm.model; -import java.util.ArrayList; -import java.util.List; - -public class WasmTag { - private List values = new ArrayList<>(); +public class WasmTag extends WasmEntity { + private WasmFunctionType type; WasmModule module; int index; - public List getValues() { - return values; + public WasmTag(WasmFunctionType type) { + this.type = type; + } + + public WasmFunctionType getType() { + return type; } public int getIndex() { diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java index ce90180fd..e3c3a31e2 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 Alexey Andreev. + * 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. @@ -15,9 +15,52 @@ */ package org.teavm.backend.wasm.model; -public enum WasmType { - INT32, - INT64, - FLOAT32, - FLOAT64 +public abstract class WasmType { + public static final WasmType.Number INT32 = new Number(WasmNumType.INT32); + public static final WasmType.Number INT64 = new Number(WasmNumType.INT64); + public static final WasmType.Number FLOAT32 = new Number(WasmNumType.FLOAT32); + public static final WasmType.Number FLOAT64 = new Number(WasmNumType.FLOAT64); + + private WasmStorageType.Regular storageType; + + private WasmType() { + } + + public WasmStorageType.Regular asStorage() { + if (storageType == null) { + storageType = new WasmStorageType.Regular(this); + } + return storageType; + } + + public static WasmType.Number num(WasmNumType number) { + switch (number) { + case INT32: + return INT32; + case INT64: + return INT64; + case FLOAT32: + return FLOAT32; + case FLOAT64: + return FLOAT64; + default: + throw new IllegalArgumentException(); + } + } + + public static final class Number extends WasmType { + public final WasmNumType number; + + private Number(WasmNumType number) { + this.number = number; + } + } + + public static final class Reference extends WasmType { + public final WasmCompositeType composite; + + Reference(WasmCompositeType composite) { + this.composite = composite; + } + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCall.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCall.java index 3b667d8de..1ca92579f 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCall.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCall.java @@ -19,48 +19,33 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; +import org.teavm.backend.wasm.model.WasmFunction; public class WasmCall extends WasmExpression { - private String functionName; - private boolean imported; + private WasmFunction function; private List arguments = new ArrayList<>(); - public WasmCall(String functionName, boolean imported) { - Objects.requireNonNull(functionName); - this.functionName = functionName; - this.imported = imported; + public WasmCall(WasmFunction function) { + this.function = Objects.requireNonNull(function); } - public WasmCall(String functionName) { - this(functionName, false); - } - - public WasmCall(String functionName, WasmExpression... arguments) { + public WasmCall(WasmFunction functionName, WasmExpression... arguments) { this(functionName); getArguments().addAll(Arrays.asList(arguments)); } - public String getFunctionName() { - return functionName; + public WasmFunction getFunction() { + return function; } - public void setFunctionName(String functionName) { - Objects.requireNonNull(functionName); - this.functionName = functionName; + public void setFunction(WasmFunction function) { + this.function = Objects.requireNonNull(function); } public List getArguments() { return arguments; } - public boolean isImported() { - return imported; - } - - public void setImported(boolean imported) { - this.imported = imported; - } - @Override public void acceptVisitor(WasmExpressionVisitor visitor) { visitor.visit(this); diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmConversion.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmConversion.java index 22f765bc4..949a8bf5e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmConversion.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmConversion.java @@ -16,16 +16,16 @@ package org.teavm.backend.wasm.model.expression; import java.util.Objects; -import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.WasmNumType; public class WasmConversion extends WasmExpression { - private WasmType sourceType; - private WasmType targetType; + private WasmNumType sourceType; + private WasmNumType targetType; private boolean signed; private WasmExpression operand; private boolean reinterpret; - public WasmConversion(WasmType sourceType, WasmType targetType, boolean signed, WasmExpression operand) { + public WasmConversion(WasmNumType sourceType, WasmNumType targetType, boolean signed, WasmExpression operand) { Objects.requireNonNull(sourceType); Objects.requireNonNull(targetType); Objects.requireNonNull(operand); @@ -35,20 +35,20 @@ public class WasmConversion extends WasmExpression { this.operand = operand; } - public WasmType getSourceType() { + public WasmNumType getSourceType() { return sourceType; } - public void setSourceType(WasmType sourceType) { + public void setSourceType(WasmNumType sourceType) { Objects.requireNonNull(sourceType); this.sourceType = sourceType; } - public WasmType getTargetType() { + public WasmNumType getTargetType() { return targetType; } - public void setTargetType(WasmType targetType) { + public void setTargetType(WasmNumType targetType) { Objects.requireNonNull(targetType); this.targetType = targetType; } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java index 94c94e595..1ac0c0933 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java @@ -81,6 +81,10 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor { public void visit(WasmFloat64Constant expression) { } + @Override + public void visit(WasmNullConstant expression) { + } + @Override public void visit(WasmGetLocal expression) { } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpression.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpression.java index 62089a5ab..2f3d88839 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpression.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpression.java @@ -42,17 +42,23 @@ public abstract class WasmExpression { if (type == null) { return null; } - switch (type) { - case INT32: - return new WasmInt32Constant(0); - case INT64: - return new WasmInt64Constant(0); - case FLOAT32: - return new WasmFloat32Constant(0); - case FLOAT64: - return new WasmFloat64Constant(0); - default: - throw new IllegalArgumentException(); + if (type instanceof WasmType.Number) { + switch (((WasmType.Number) type).number) { + case INT32: + return new WasmInt32Constant(0); + case INT64: + return new WasmInt64Constant(0); + case FLOAT32: + return new WasmFloat32Constant(0); + case FLOAT64: + return new WasmFloat64Constant(0); + default: + throw new IllegalArgumentException(); + } + } else if (type instanceof WasmType.Reference) { + return new WasmNullConstant(((WasmType.Reference) type).composite); + } else { + throw new IllegalArgumentException(); } } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java index 3441af816..0fbc0fc7c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java @@ -38,6 +38,8 @@ public interface WasmExpressionVisitor { void visit(WasmFloat64Constant expression); + void visit(WasmNullConstant expression); + void visit(WasmGetLocal expression); void visit(WasmSetLocal expression); diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmIndirectCall.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmIndirectCall.java index 83eea147c..1af8d7336 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmIndirectCall.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmIndirectCall.java @@ -18,17 +18,16 @@ package org.teavm.backend.wasm.model.expression; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.WasmFunctionType; public class WasmIndirectCall extends WasmExpression { - private List parameterTypes = new ArrayList<>(); - private WasmType returnType; + private WasmFunctionType type; private WasmExpression selector; private List arguments = new ArrayList<>(); - public WasmIndirectCall(WasmExpression selector) { - Objects.requireNonNull(selector); - this.selector = selector; + public WasmIndirectCall(WasmExpression selector, WasmFunctionType type) { + this.selector = Objects.requireNonNull(selector); + this.type = Objects.requireNonNull(type); } public WasmExpression getSelector() { @@ -36,26 +35,21 @@ public class WasmIndirectCall extends WasmExpression { } public void setSelector(WasmExpression selector) { - Objects.requireNonNull(selector); - this.selector = selector; - } - - public List getParameterTypes() { - return parameterTypes; - } - - public WasmType getReturnType() { - return returnType; - } - - public void setReturnType(WasmType returnType) { - this.returnType = returnType; + this.selector = Objects.requireNonNull(selector); } public List getArguments() { return arguments; } + public WasmFunctionType getType() { + return type; + } + + public void setType(WasmFunctionType type) { + this.type = Objects.requireNonNull(type); + } + @Override public void acceptVisitor(WasmExpressionVisitor visitor) { visitor.visit(this); diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmNullConstant.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmNullConstant.java new file mode 100644 index 000000000..a84c2fb0a --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmNullConstant.java @@ -0,0 +1,39 @@ +/* + * 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.model.expression; + +import org.teavm.backend.wasm.model.WasmCompositeType; + +public class WasmNullConstant extends WasmExpression { + public WasmCompositeType type; + + public WasmNullConstant(WasmCompositeType type) { + this.type = type; + } + + public WasmCompositeType getType() { + return type; + } + + public void setType(WasmCompositeType type) { + this.type = type; + } + + @Override + public void acceptVisitor(WasmExpressionVisitor visitor) { + visitor.visit(this); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java index 65be02128..60b4e4b0a 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java @@ -105,6 +105,10 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor { public void visit(WasmFloat64Constant expression) { } + @Override + public void visit(WasmNullConstant expression) { + } + @Override public void visit(WasmGetLocal expression) { } diff --git a/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedFunctionElimination.java b/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedFunctionElimination.java index b0dc43d09..12eba25bf 100644 --- a/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedFunctionElimination.java +++ b/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedFunctionElimination.java @@ -35,7 +35,7 @@ public class UnusedFunctionElimination { } public void apply() { - List exported = module.getFunctions().values().stream() + List exported = module.functions.stream() .filter(function -> function.getExportName() != null) .collect(Collectors.toList()); for (WasmFunction function : exported) { @@ -48,11 +48,7 @@ public class UnusedFunctionElimination { use(module.getStartFunction()); } - for (WasmFunction function : module.getFunctions().values().toArray(new WasmFunction[0])) { - if (!usedFunctions.contains(function)) { - module.remove(function); - } - } + module.functions.removeIf(function -> !usedFunctions.contains(function)); } private void use(WasmFunction function) { @@ -68,7 +64,7 @@ public class UnusedFunctionElimination { @Override public void visit(WasmCall expression) { super.visit(expression); - WasmFunction function = module.getFunctions().get(expression.getFunctionName()); + var function = expression.getFunction(); if (function != null) { use(function); } diff --git a/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedTypeElimination.java b/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedTypeElimination.java new file mode 100644 index 000000000..66b7d5ecd --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedTypeElimination.java @@ -0,0 +1,113 @@ +/* + * 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.optimization; + +import java.util.HashSet; +import java.util.Set; +import org.teavm.backend.wasm.model.WasmArray; +import org.teavm.backend.wasm.model.WasmCompositeType; +import org.teavm.backend.wasm.model.WasmCompositeTypeVisitor; +import org.teavm.backend.wasm.model.WasmDefaultCompositeTypeVisitor; +import org.teavm.backend.wasm.model.WasmFunction; +import org.teavm.backend.wasm.model.WasmFunctionType; +import org.teavm.backend.wasm.model.WasmModule; +import org.teavm.backend.wasm.model.WasmStorageType; +import org.teavm.backend.wasm.model.WasmStructure; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmDefaultExpressionVisitor; +import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor; +import org.teavm.backend.wasm.model.expression.WasmIndirectCall; + +public class UnusedTypeElimination { + private WasmModule module; + private Set usedTypes = new HashSet<>(); + + public UnusedTypeElimination(WasmModule module) { + this.module = module; + } + + public void apply() { + collect(); + module.types.removeIf(type -> !usedTypes.contains(type)); + } + + private void collect() { + for (var function : module.functions) { + useFrom(function); + } + for (var tag : module.tags) { + use(tag.getType()); + } + } + + private void useFrom(WasmFunction function) { + use(function.getType()); + for (var part : function.getBody()) { + part.acceptVisitor(exprVisitor); + } + } + + private void use(WasmCompositeType type) { + if (!usedTypes.add(type)) { + return; + } + type.acceptVisitor(typeVisitor); + } + + private WasmExpressionVisitor exprVisitor = new WasmDefaultExpressionVisitor() { + @Override + public void visit(WasmIndirectCall expression) { + super.visit(expression); + use(expression.getType()); + } + }; + + private WasmCompositeTypeVisitor typeVisitor = new WasmDefaultCompositeTypeVisitor() { + @Override + public void visit(WasmStructure type) { + for (var field : type.getFields()) { + visit(field); + } + } + + @Override + public void visit(WasmArray type) { + visit(type.getElementType()); + } + + @Override + public void visit(WasmFunctionType type) { + if (type.getReturnType() != null) { + visit(type.getReturnType()); + } + for (var parameter : type.getParameterTypes()) { + visit(parameter); + } + } + + private void visit(WasmStorageType type) { + if (type instanceof WasmStorageType.Regular) { + visit(((WasmStorageType.Regular) type).type); + } + } + + private void visit(WasmType type) { + if (type instanceof WasmType.Reference) { + use(((WasmType.Reference) type).composite); + } + } + }; +} diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java b/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java index 32dc0402e..ca5424c96 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java @@ -15,6 +15,7 @@ */ package org.teavm.backend.wasm.parser; +import org.teavm.backend.wasm.model.WasmNumType; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmFloatType; @@ -97,7 +98,7 @@ public interface CodeListener { default void storeFloat64(int align, int offset) { } - default void convert(WasmType sourceType, WasmType targetType, boolean signed, boolean reinterpret) { + default void convert(WasmNumType sourceType, WasmNumType targetType, boolean signed, boolean reinterpret) { } default void memoryGrow() { diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java b/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java index 2baeb5f16..bfa2801fa 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java @@ -17,6 +17,7 @@ package org.teavm.backend.wasm.parser; import java.util.ArrayList; import java.util.List; +import org.teavm.backend.wasm.model.WasmNumType; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmFloatType; @@ -544,76 +545,76 @@ public class CodeSectionParser { break; case 0xA7: - codeListener.convert(WasmType.INT64, WasmType.INT32, false, false); + codeListener.convert(WasmNumType.INT64, WasmNumType.INT32, false, false); break; case 0xA8: - codeListener.convert(WasmType.FLOAT32, WasmType.INT32, false, false); + codeListener.convert(WasmNumType.FLOAT32, WasmNumType.INT32, false, false); break; case 0xA9: - codeListener.convert(WasmType.FLOAT32, WasmType.INT32, true, false); + codeListener.convert(WasmNumType.FLOAT32, WasmNumType.INT32, true, false); break; case 0xAA: - codeListener.convert(WasmType.FLOAT64, WasmType.INT32, false, false); + codeListener.convert(WasmNumType.FLOAT64, WasmNumType.INT32, false, false); break; case 0xAB: - codeListener.convert(WasmType.FLOAT64, WasmType.INT32, true, false); + codeListener.convert(WasmNumType.FLOAT64, WasmNumType.INT32, true, false); break; case 0xAC: - codeListener.convert(WasmType.INT32, WasmType.INT64, false, false); + codeListener.convert(WasmNumType.INT32, WasmNumType.INT64, false, false); break; case 0xAD: - codeListener.convert(WasmType.INT32, WasmType.INT64, true, false); + codeListener.convert(WasmNumType.INT32, WasmNumType.INT64, true, false); break; case 0xAE: - codeListener.convert(WasmType.FLOAT32, WasmType.INT64, false, false); + codeListener.convert(WasmNumType.FLOAT32, WasmNumType.INT64, false, false); break; case 0xAF: - codeListener.convert(WasmType.FLOAT32, WasmType.INT64, true, false); + codeListener.convert(WasmNumType.FLOAT32, WasmNumType.INT64, true, false); break; case 0xB0: - codeListener.convert(WasmType.FLOAT64, WasmType.INT64, false, false); + codeListener.convert(WasmNumType.FLOAT64, WasmNumType.INT64, false, false); break; case 0xB1: - codeListener.convert(WasmType.FLOAT64, WasmType.INT64, true, false); + codeListener.convert(WasmNumType.FLOAT64, WasmNumType.INT64, true, false); break; case 0xB2: - codeListener.convert(WasmType.INT32, WasmType.FLOAT32, false, false); + codeListener.convert(WasmNumType.INT32, WasmNumType.FLOAT32, false, false); break; case 0xB3: - codeListener.convert(WasmType.INT32, WasmType.FLOAT32, true, false); + codeListener.convert(WasmNumType.INT32, WasmNumType.FLOAT32, true, false); break; case 0xB4: - codeListener.convert(WasmType.INT64, WasmType.FLOAT32, false, false); + codeListener.convert(WasmNumType.INT64, WasmNumType.FLOAT32, false, false); break; case 0xB5: - codeListener.convert(WasmType.INT64, WasmType.FLOAT32, true, false); + codeListener.convert(WasmNumType.INT64, WasmNumType.FLOAT32, true, false); break; case 0xB6: - codeListener.convert(WasmType.FLOAT64, WasmType.FLOAT32, true, false); + codeListener.convert(WasmNumType.FLOAT64, WasmNumType.FLOAT32, true, false); break; case 0xB7: - codeListener.convert(WasmType.INT32, WasmType.FLOAT64, false, false); + codeListener.convert(WasmNumType.INT32, WasmNumType.FLOAT64, false, false); break; case 0xB8: - codeListener.convert(WasmType.INT32, WasmType.FLOAT64, true, false); + codeListener.convert(WasmNumType.INT32, WasmNumType.FLOAT64, true, false); break; case 0xB9: - codeListener.convert(WasmType.INT64, WasmType.FLOAT64, false, false); + codeListener.convert(WasmNumType.INT64, WasmNumType.FLOAT64, false, false); break; case 0xBA: - codeListener.convert(WasmType.INT64, WasmType.FLOAT64, true, false); + codeListener.convert(WasmNumType.INT64, WasmNumType.FLOAT64, true, false); break; case 0xBC: - codeListener.convert(WasmType.FLOAT32, WasmType.INT32, false, true); + codeListener.convert(WasmNumType.FLOAT32, WasmNumType.INT32, false, true); break; case 0xBD: - codeListener.convert(WasmType.FLOAT64, WasmType.INT64, false, true); + codeListener.convert(WasmNumType.FLOAT64, WasmNumType.INT64, false, true); break; case 0xBE: - codeListener.convert(WasmType.INT32, WasmType.FLOAT32, false, true); + codeListener.convert(WasmNumType.INT32, WasmNumType.FLOAT32, false, true); break; case 0xBF: - codeListener.convert(WasmType.INT64, WasmType.FLOAT64, false, true); + codeListener.convert(WasmNumType.INT64, WasmNumType.FLOAT64, false, true); break; case 0xFC: 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 72010c93a..a94cfdd49 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 @@ -18,9 +18,7 @@ package org.teavm.backend.wasm.render; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.function.Supplier; import java.util.stream.Collectors; import org.teavm.backend.wasm.debug.DebugLines; @@ -32,7 +30,6 @@ import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmMemorySegment; import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmType; -import org.teavm.backend.wasm.model.expression.WasmExpression; public class WasmBinaryRenderer { private static final int SECTION_UNKNOWN = 0; @@ -53,9 +50,6 @@ public class WasmBinaryRenderer { private WasmBinaryWriter output; private WasmBinaryVersion version; - private List signatures = new ArrayList<>(); - private Map signatureIndexes = new HashMap<>(); - private Map functionIndexes = new HashMap<>(); private boolean obfuscated; private DwarfGenerator dwarfGenerator; private DwarfClassGenerator dwarfClassGen; @@ -88,7 +82,7 @@ public class WasmBinaryRenderer { break; } - renderSignatures(module); + renderTypes(module); renderImports(module); renderFunctions(module); renderTable(module); @@ -105,33 +99,13 @@ public class WasmBinaryRenderer { renderCustomSections(module, customSectionSupplier); } - private void renderSignatures(WasmModule module) { - WasmBinaryWriter section = new WasmBinaryWriter(); - WasmSignatureCollector signatureCollector = new WasmSignatureCollector(this::registerSignature); + private void renderTypes(WasmModule module) { + var section = new WasmBinaryWriter(); - for (WasmFunction function : module.getFunctions().values()) { - registerSignature(WasmSignature.fromFunction(function)); - for (WasmExpression part : function.getBody()) { - part.acceptVisitor(signatureCollector); - } - } - for (var tag : module.getTags()) { - registerSignature(WasmSignature.fromTag(tag)); - } - - section.writeLEB(signatures.size()); - for (WasmSignature signature : signatures) { - section.writeByte(0x60); - section.writeLEB(signature.types.length - 1); - for (int i = 1; i < signature.types.length; ++i) { - section.writeType(signature.types[i], version); - } - if (signature.types[0] != null) { - section.writeByte(1); - section.writeType(signature.types[0], version); - } else { - section.writeByte(0); - } + var typeRenderer = new WasmCompositeTypeBinaryRenderer(module, section); + section.writeLEB(module.types.size()); + for (var type : module.types) { + type.acceptVisitor(typeRenderer); } writeSection(SECTION_TYPE, "type", section.getData()); @@ -139,11 +113,10 @@ public class WasmBinaryRenderer { private void renderImports(WasmModule module) { List functions = new ArrayList<>(); - for (WasmFunction function : module.getFunctions().values()) { + for (var function : module.functions) { if (function.getImportName() == null) { continue; } - functionIndexes.put(function.getName(), functions.size()); functions.add(function); } if (functions.isEmpty()) { @@ -154,9 +127,7 @@ public class WasmBinaryRenderer { section.writeLEB(functions.size()); for (WasmFunction function : functions) { - WasmSignature signature = WasmSignature.fromFunction(function); - int signatureIndex = signatureIndexes.get(signature); - + int signatureIndex = module.types.indexOf(function.getType()); String moduleName = function.getImportModule(); if (moduleName == null) { moduleName = ""; @@ -175,17 +146,13 @@ public class WasmBinaryRenderer { private void renderFunctions(WasmModule module) { WasmBinaryWriter section = new WasmBinaryWriter(); - List functions = module.getFunctions().values().stream() + List functions = module.functions.stream() .filter(function -> function.getImportName() == null) .collect(Collectors.toList()); - for (WasmFunction function : functions) { - functionIndexes.put(function.getName(), functionIndexes.size()); - } section.writeLEB(functions.size()); - for (WasmFunction function : functions) { - WasmSignature signature = WasmSignature.fromFunction(function); - section.writeLEB(signatureIndexes.get(signature)); + for (var function : functions) { + section.writeLEB(module.types.indexOf(function.getType())); } writeSection(SECTION_FUNCTION, "function", section.getData()); @@ -201,7 +168,7 @@ public class WasmBinaryRenderer { section.writeByte(1); section.writeByte(0x70); section.writeByte(0); - section.writeLEB(functionIndexes.size()); + section.writeLEB(module.functions.size()); writeSection(SECTION_TABLE, "table", section.getData()); } @@ -223,13 +190,13 @@ public class WasmBinaryRenderer { WasmBinaryWriter section = new WasmBinaryWriter(); - List functions = module.getFunctions().values().stream() + List functions = module.functions.stream() .filter(function -> function.getExportName() != null) .collect(Collectors.toList()); section.writeLEB(functions.size() + 1); - for (WasmFunction function : functions) { - int functionIndex = functionIndexes.get(function.getName()); + for (var function : functions) { + int functionIndex = module.functions.indexOf(function); section.writeAsciiString(function.getExportName()); @@ -251,7 +218,7 @@ public class WasmBinaryRenderer { } WasmBinaryWriter section = new WasmBinaryWriter(); - section.writeLEB(functionIndexes.get(module.getStartFunction().getName())); + section.writeLEB(module.functions.indexOf(module.getStartFunction())); writeSection(SECTION_START, "start", section.getData()); } @@ -268,8 +235,8 @@ public class WasmBinaryRenderer { renderInitializer(section, 0); section.writeLEB(module.getFunctionTable().size()); - for (WasmFunction function : module.getFunctionTable()) { - section.writeLEB(functionIndexes.get(function.getName())); + for (var function : module.getFunctionTable()) { + section.writeLEB(module.functions.indexOf(function)); } writeSection(SECTION_ELEMENT, "element", section.getData()); @@ -278,13 +245,13 @@ public class WasmBinaryRenderer { private void renderCode(WasmModule module) { var section = new WasmBinaryWriter(); - var functions = module.getFunctions().values().stream() + var functions = module.functions.stream() .filter(function -> function.getImportName() == null) .collect(Collectors.toList()); section.writeLEB(functions.size()); for (var function : functions) { - var body = renderFunction(function, section.getPosition() + 4); + var body = renderFunction(module, function, section.getPosition() + 4); var startPos = section.getPosition(); section.writeLEB4(body.length); section.writeBytes(body); @@ -301,7 +268,7 @@ public class WasmBinaryRenderer { writeSection(SECTION_CODE, "code", section.getData()); } - private byte[] renderFunction(WasmFunction function, int offset) { + private byte[] renderFunction(WasmModule module, WasmFunction function, int offset) { var code = new WasmBinaryWriter(); var dwarfSubprogram = dwarfClassGen != null ? dwarfClassGen.getSubprogram(function.getName()) : null; @@ -314,7 +281,7 @@ public class WasmBinaryRenderer { } var localVariables = function.getLocalVariables(); - int parameterCount = Math.min(function.getParameters().size(), localVariables.size()); + int parameterCount = Math.min(function.getType().getParameterTypes().size(), localVariables.size()); localVariables = localVariables.subList(parameterCount, localVariables.size()); if (localVariables.isEmpty()) { code.writeLEB(0); @@ -335,13 +302,12 @@ public class WasmBinaryRenderer { code.writeLEB(localEntries.size()); for (var entry : localEntries) { code.writeLEB(entry.count); - code.writeType(entry.type, version); + code.writeType(entry.type, module); } } - var importIndexes = this.functionIndexes; - var visitor = new WasmBinaryRenderingVisitor(code, version, functionIndexes, importIndexes, - signatureIndexes, dwarfGenerator, function.getJavaMethod() != null ? debugLines : null, offset); + var visitor = new WasmBinaryRenderingVisitor(code, module, dwarfGenerator, + function.getJavaMethod() != null ? debugLines : null, offset); for (var part : function.getBody()) { part.acceptVisitor(visitor); } @@ -400,15 +366,15 @@ public class WasmBinaryRenderer { } private void renderTags(WasmModule module) { - if (module.getTags().isEmpty()) { + if (module.tags.isEmpty()) { return; } var section = new WasmBinaryWriter(); - section.writeLEB(module.getTags().size()); - for (var tag : module.getTags()) { + section.writeLEB(module.tags.size()); + for (var tag : module.tags) { section.writeByte(0); - section.writeLEB(signatureIndexes.get(WasmSignature.fromTag(tag))); + section.writeLEB(module.types.indexOf(tag.getType())); } writeSection(SECTION_TAGS, "tags", section.getData()); @@ -418,12 +384,13 @@ public class WasmBinaryRenderer { WasmBinaryWriter section = new WasmBinaryWriter(); WasmBinaryWriter functionsSubsection = new WasmBinaryWriter(); - Collection functions = module.getFunctions().values(); - functions = functions.stream().filter(f -> f.getImportName() == null).collect(Collectors.toList()); + var functions = module.functions.stream() + .filter(f -> f.getName() != null && f.getImportName() == null) + .collect(Collectors.toList()); functionsSubsection.writeLEB(functions.size()); for (WasmFunction function : functions) { - functionsSubsection.writeLEB(functionIndexes.get(function.getName())); + functionsSubsection.writeLEB(module.functions.indexOf(function)); functionsSubsection.writeAsciiString(function.getName()); } @@ -460,14 +427,6 @@ public class WasmBinaryRenderer { } } - private void registerSignature(WasmSignature signature) { - signatureIndexes.computeIfAbsent(signature, key -> { - int result = signatures.size(); - signatures.add(key); - return result; - }); - } - private void writeSection(int id, String name, byte[] data) { var start = output.getPosition(); output.writeByte(id); diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java index 488909af8..14165a652 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Objects; import org.teavm.backend.wasm.debug.DebugLines; import org.teavm.backend.wasm.generate.DwarfGenerator; +import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; @@ -50,6 +51,7 @@ import org.teavm.backend.wasm.model.expression.WasmLoadFloat64; import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmLoadInt64; import org.teavm.backend.wasm.model.expression.WasmMemoryGrow; +import org.teavm.backend.wasm.model.expression.WasmNullConstant; import org.teavm.backend.wasm.model.expression.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmStoreFloat32; @@ -65,10 +67,7 @@ import org.teavm.model.TextLocation; class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { private WasmBinaryWriter writer; - private WasmBinaryVersion version; - private Map functionIndexes; - private Map importedIndexes; - private Map signatureIndexes; + private WasmModule module; private DwarfGenerator dwarfGenerator; private DebugLines debugLines; private int addressOffset; @@ -82,14 +81,10 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { private int positionToEmit; private List locationStack = new ArrayList<>(); - WasmBinaryRenderingVisitor(WasmBinaryWriter writer, WasmBinaryVersion version, Map functionIndexes, - Map importedIndexes, Map signatureIndexes, + WasmBinaryRenderingVisitor(WasmBinaryWriter writer, WasmModule module, DwarfGenerator dwarfGenerator, DebugLines debugLines, int addressOffset) { this.writer = writer; - this.version = version; - this.functionIndexes = functionIndexes; - this.importedIndexes = importedIndexes; - this.signatureIndexes = signatureIndexes; + this.module = module; this.dwarfGenerator = dwarfGenerator; this.addressOffset = addressOffset; this.debugLines = debugLines; @@ -115,7 +110,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { } private void writeBlockType(WasmType type) { - writer.writeType(type, version); + writer.writeType(type, module); } @Override @@ -240,6 +235,14 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { popLocation(); } + @Override + public void visit(WasmNullConstant expression) { + pushLocation(expression); + writer.writeByte(0xD0); + writer.writeSignedLEB(module.types.indexOf(expression.getType())); + popLocation(); + } + @Override public void visit(WasmGetLocal expression) { pushLocation(expression); @@ -724,13 +727,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { for (WasmExpression argument : expression.getArguments()) { argument.acceptVisitor(this); } - Integer functionIndex = !expression.isImported() - ? functionIndexes.get(expression.getFunctionName()) - : importedIndexes.get(expression.getFunctionName()); - if (functionIndex == null) { - writer.writeByte(0x00); - return; - } + var functionIndex = module.functions.indexOf(expression.getFunction()); writer.writeByte(0x10); writer.writeLEB(functionIndex); @@ -745,13 +742,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { } expression.getSelector().acceptVisitor(this); writer.writeByte(0x11); - - WasmType[] signatureTypes = new WasmType[expression.getParameterTypes().size() + 1]; - signatureTypes[0] = expression.getReturnType(); - for (int i = 0; i < expression.getParameterTypes().size(); ++i) { - signatureTypes[i + 1] = expression.getParameterTypes().get(i); - } - writer.writeLEB(signatureIndexes.get(new WasmSignature(signatureTypes))); + writer.writeLEB(module.types.indexOf(expression.getType())); writer.writeByte(0); popLocation(); diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java index 7f1b39b6c..912062063 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java @@ -16,6 +16,8 @@ package org.teavm.backend.wasm.render; import java.util.Arrays; +import org.teavm.backend.wasm.model.WasmModule; +import org.teavm.backend.wasm.model.WasmNumType; import org.teavm.backend.wasm.model.WasmType; public class WasmBinaryWriter { @@ -27,11 +29,19 @@ public class WasmBinaryWriter { data[pointer++] = (byte) v; } - public void writeType(WasmType type, WasmBinaryVersion version) { + public void writeType(WasmType type, WasmModule module) { if (type == null) { writeByte(0x40); return; } + if (type instanceof WasmType.Number) { + writeType(((WasmType.Number) type).number); + } else if (type instanceof WasmType.Reference) { + writeSignedLEB(module.types.indexOf(((WasmType.Reference) type).composite)); + } + } + + public void writeType(WasmNumType type) { switch (type) { case INT32: writeByte(0x7F); diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java index 7db62ca66..1d72799a2 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java @@ -29,6 +29,7 @@ import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.model.TextLocation; public class WasmCRenderer { + private WasmModule module; private StringBuilder out = new StringBuilder(); private int indentLevel; String currentFile = ""; @@ -37,6 +38,10 @@ public class WasmCRenderer { boolean memoryAccessChecked; TextLocation lastReportedLocation; + public WasmCRenderer(WasmModule module) { + this.module = module; + } + public boolean isLineNumbersEmitted() { return lineNumbersEmitted; } @@ -75,7 +80,7 @@ public class WasmCRenderer { renderFunctionDeclarations(module); renderFunctionTable(module); - for (WasmFunction function : module.getFunctions().values()) { + for (var function : module.functions) { if (function.getImportName() == null) { renderFunction(function); } @@ -91,13 +96,13 @@ public class WasmCRenderer { line(module.getStartFunction().getName() + "();"); } - for (WasmFunction function : module.getFunctions().values()) { + for (var function : module.functions) { if (function.getExportName() != null && function.getExportName().equals("main")) { line(function.getName() + "(1);"); } } - for (WasmFunction function : module.getFunctions().values()) { + for (var function : module.functions) { if (Objects.equals(function.getExportName(), "_start")) { line(function.getName() + "();"); } @@ -171,26 +176,26 @@ public class WasmCRenderer { } private void renderFunctionDeclarations(WasmModule module) { - for (WasmFunction function : module.getFunctions().values()) { + for (var function : module.functions) { line(functionDeclaration(function) + ";"); } } private void renderFunction(WasmFunction function) { - WasmCRenderingVisitor visitor = new WasmCRenderingVisitor(function.getResult(), - function.getLocalVariables().size(), function.getModule()); + var visitor = new WasmCRenderingVisitor(function.getType().getReturnType(), + function.getLocalVariables().size(), module); visitor.setMemoryAccessChecked(memoryAccessChecked); StringBuilder declaration = new StringBuilder(); renderFunctionModifiers(declaration, function); - declaration.append(WasmCRenderingVisitor.mapType(function.getResult())).append(' '); + declaration.append(WasmCRenderingVisitor.mapType(function.getType().getReturnType())).append(' '); declaration.append(function.getName()).append('('); - int sz = Math.min(function.getParameters().size(), function.getLocalVariables().size()); + int sz = Math.min(function.getType().getParameterTypes().size(), function.getLocalVariables().size()); for (int i = 0; i < sz; ++i) { if (i > 0) { declaration.append(", "); } - declaration.append(WasmCRenderingVisitor.mapType(function.getParameters().get(i))); + declaration.append(WasmCRenderingVisitor.mapType(function.getType().getParameterTypes().get(i))); WasmLocal var = function.getLocalVariables().get(i); declaration.append(' ').append(visitor.getVariableName(var)); } @@ -212,7 +217,7 @@ public class WasmCRenderer { lines.addAll(visitor.getValue().getLines()); } - visitor.setRequiredType(function.getResult()); + visitor.setRequiredType(function.getType().getReturnType()); body.get(body.size() - 1).acceptVisitor(visitor); lines.addAll(visitor.getValue().getLines()); if (visitor.getValue().getText() != null) { @@ -232,7 +237,7 @@ public class WasmCRenderer { private String functionDeclaration(WasmFunction function) { StringBuilder sb = new StringBuilder(); renderFunctionModifiers(sb, function); - sb.append(WasmCRenderingVisitor.mapType(function.getResult())).append(' '); + sb.append(WasmCRenderingVisitor.mapType(function.getType().getReturnType())).append(' '); if (function.getImportName() != null) { sb.append(function.getImportModule() != null && !function.getImportModule().isEmpty() ? function.getImportModule() + "_" + function.getImportName() @@ -241,11 +246,11 @@ public class WasmCRenderer { sb.append(function.getName()); } sb.append("("); - for (int i = 0; i < function.getParameters().size(); ++i) { + for (int i = 0; i < function.getType().getParameterTypes().size(); ++i) { if (i > 0) { sb.append(", "); } - sb.append(WasmCRenderingVisitor.mapType(function.getParameters().get(i))); + sb.append(WasmCRenderingVisitor.mapType(function.getType().getParameterTypes().get(i))); } sb.append(")"); diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java index afac141c6..943520d7d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java @@ -22,9 +22,9 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmModule; +import org.teavm.backend.wasm.model.WasmNumType; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; @@ -54,6 +54,7 @@ import org.teavm.backend.wasm.model.expression.WasmLoadFloat64; import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmLoadInt64; import org.teavm.backend.wasm.model.expression.WasmMemoryGrow; +import org.teavm.backend.wasm.model.expression.WasmNullConstant; import org.teavm.backend.wasm.model.expression.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmStoreFloat32; @@ -333,6 +334,11 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor { } } + @Override + public void visit(WasmNullConstant expression) { + value = CExpression.relocatable("/* can't produce ref.null */"); + } + @Override public void visit(WasmGetLocal expression) { value = new CExpression(getVariableName(expression.getLocal())); @@ -647,7 +653,7 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor { if (type != null && expression.getSourceType() != expression.getTargetType()) { switch (expression.getTargetType()) { case INT32: - if (expression.getSourceType() == WasmType.FLOAT32 && expression.isReinterpret()) { + if (expression.getSourceType() == WasmNumType.FLOAT32 && expression.isReinterpret()) { result.setText("reinterpret_float32(" + operand.getText() + ")"); } else if (expression.isSigned()) { result.setText("(int32_t) " + operand.getText()); @@ -656,7 +662,7 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor { } break; case INT64: - if (expression.getSourceType() == WasmType.FLOAT64 && expression.isReinterpret()) { + if (expression.getSourceType() == WasmNumType.FLOAT64 && expression.isReinterpret()) { result.setText("reinterpret_float64(" + operand.getText() + ")"); } else if (expression.isSigned()) { result.setText("(int64_t) " + operand.getText()); @@ -665,9 +671,9 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor { } break; case FLOAT32: - if (expression.getSourceType() == WasmType.INT32 && expression.isReinterpret()) { + if (expression.getSourceType() == WasmNumType.INT32 && expression.isReinterpret()) { result.setText("reinterpret_int32(" + operand.getText() + ")"); - } else if (expression.getSourceType() == WasmType.FLOAT64) { + } else if (expression.getSourceType() == WasmNumType.FLOAT64) { result.setText("(float) " + operand.getText()); } else if (expression.isSigned()) { result.setText("(float) (int64_t) " + operand.getText()); @@ -676,9 +682,9 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor { } break; case FLOAT64: - if (expression.getSourceType() == WasmType.INT64 && expression.isReinterpret()) { + if (expression.getSourceType() == WasmNumType.INT64 && expression.isReinterpret()) { result.setText("reinterpret_int64(" + operand.getText() + ")"); - } else if (expression.getSourceType() == WasmType.FLOAT32) { + } else if (expression.getSourceType() == WasmNumType.FLOAT32) { result.setText("(double) " + operand.getText()); } else if (expression.isSigned()) { result.setText("(double) (int64_t) " + operand.getText()); @@ -694,25 +700,18 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor { @Override public void visit(WasmCall expression) { - WasmFunction function = module.getFunctions().get(expression.getFunctionName()); - if (function == null) { - value = new CExpression("0"); - return; - } + var function = expression.getFunction(); CExpression result = new CExpression(); WasmType type = requiredType; StringBuilder sb = new StringBuilder(); - if (expression.isImported()) { - sb.append(function.getImportModule() != null && !function.getImportModule().isEmpty() - ? function.getImportModule() + "_" + function.getImportName() - : function.getImportName()); - } else { - sb.append(expression.getFunctionName()); - } + sb.append(function.getImportModule() != null && !function.getImportModule().isEmpty() + ? function.getImportModule() + "_" + function.getImportName() + : function.getImportName()); + sb.append('('); - translateArguments(expression.getArguments(), function.getParameters(), result, sb); + translateArguments(expression.getArguments(), function.getType().getParameterTypes(), result, sb); sb.append(')'); result.setText(sb.toString()); @@ -729,12 +728,12 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor { WasmType type = requiredType; StringBuilder sb = new StringBuilder(); - sb.append("(*(" + mapType(expression.getReturnType()) + " (*)("); - for (int i = 0; i < expression.getParameterTypes().size(); ++i) { + sb.append("(*(" + mapType(expression.getType().getReturnType()) + " (*)("); + for (int i = 0; i < expression.getType().getParameterTypes().size(); ++i) { if (i > 0) { sb.append(", "); } - sb.append(mapType(expression.getParameterTypes().get(i))); + sb.append(mapType(expression.getType().getParameterTypes().get(i))); } sb.append(")) "); @@ -743,7 +742,7 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor { result.getLines().addAll(value.getLines()); value = cacheIfNeeded(WasmType.INT32, value, result); sb.append("wasm_table[" + value.getText() + "])("); - translateArguments(expression.getArguments(), expression.getParameterTypes(), result, sb); + translateArguments(expression.getArguments(), expression.getType().getParameterTypes(), result, sb); sb.append(")"); result.setText(sb.toString()); @@ -754,7 +753,7 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor { value = result; } - private void translateArguments(List wasmArguments, List signature, + private void translateArguments(List wasmArguments, List signature, CExpression result, StringBuilder sb) { if (wasmArguments.isEmpty()) { return; @@ -1148,9 +1147,18 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor { } static String mapType(WasmType type) { - if (type == null) { + if (type instanceof WasmType.Number) { + return mapType(((WasmType.Number) type).number); + } else if (type instanceof WasmType.Reference) { + return "/* unknown type */"; + } else if (type == null) { return "void"; + } else { + throw new IllegalArgumentException(); } + } + + static String mapType(WasmNumType type) { switch (type) { case INT32: return "int32_t"; diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCompositeTypeBinaryRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCompositeTypeBinaryRenderer.java new file mode 100644 index 000000000..3ef20aa5e --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCompositeTypeBinaryRenderer.java @@ -0,0 +1,56 @@ +/* + * 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.render; + +import org.teavm.backend.wasm.model.WasmArray; +import org.teavm.backend.wasm.model.WasmCompositeTypeVisitor; +import org.teavm.backend.wasm.model.WasmFunctionType; +import org.teavm.backend.wasm.model.WasmModule; +import org.teavm.backend.wasm.model.WasmStructure; + +public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor { + private WasmModule module; + private WasmBinaryWriter section; + + public WasmCompositeTypeBinaryRenderer(WasmModule module, WasmBinaryWriter section) { + this.section = section; + } + + @Override + public void visit(WasmStructure type) { + + } + + @Override + public void visit(WasmArray type) { + + } + + @Override + public void visit(WasmFunctionType type) { + section.writeByte(0x60); + section.writeLEB(type.getParameterTypes().size()); + for (var inputType : type.getParameterTypes()) { + section.writeType(inputType, module); + } + if (type.getReturnType() != null) { + section.writeByte(1); + section.writeType(type.getReturnType(), module); + } else { + section.writeByte(0); + } + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderer.java index 793f27ffb..9a179ec02 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderer.java @@ -23,7 +23,13 @@ import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.expression.WasmExpression; public class WasmRenderer { - private WasmRenderingVisitor visitor = new WasmRenderingVisitor(); + private WasmRenderingVisitor visitor; + private WasmModule module; + + public WasmRenderer(WasmModule module) { + visitor = new WasmRenderingVisitor(module); + this.module = module; + } public WasmRenderer append(String text) { visitor.append(text); @@ -58,12 +64,12 @@ public class WasmRenderer { renderTypes(module); int functionIndex = 0; - for (WasmFunction function : module.getFunctions().values()) { + for (WasmFunction function : module.functions) { if (function.getImportName() != null) { lf().append(";; function #" + functionIndex++).lf().render(function); } } - for (WasmFunction function : module.getFunctions().values()) { + for (WasmFunction function : module.functions) { if (function.getImportName() == null) { lf().append(";; function #" + functionIndex++).lf().render(function); } @@ -127,7 +133,7 @@ public class WasmRenderer { renderSignature(function); - int firstLocalVariable = function.getParameters().size(); + int firstLocalVariable = function.getType().getParameterTypes().size(); if (firstLocalVariable < function.getLocalVariables().size()) { visitor.lf().open().append("local"); List locals = function.getLocalVariables().subList(firstLocalVariable, @@ -148,44 +154,10 @@ public class WasmRenderer { } private void renderSignature(WasmFunction function) { - WasmSignature signature = WasmSignature.fromFunction(function); - visitor.append(" ").open().append("type $type" + visitor.getSignatureIndex(signature)).close(); + visitor.append(" ").open().append("type $type" + module.types.indexOf(function.getType())).close(); } private void renderTypes(WasmModule module) { - WasmSignatureCollector signatureCollector = new WasmSignatureCollector(visitor::getSignatureIndex); - for (WasmFunction function : module.getFunctions().values()) { - visitor.getSignatureIndex(WasmSignature.fromFunction(function)); - for (WasmExpression part : function.getBody()) { - part.acceptVisitor(signatureCollector); - } - } - - if (visitor.signatureList.isEmpty()) { - return; - } - - visitor.lf(); - int index = 0; - for (WasmSignature signature : visitor.signatureList) { - visitor.open().append("type $type" + index++ + " "); - visitor.open().append("func"); - if (signature.types.length > 1) { - visitor.append(" ").open().append("param"); - for (int i = 1; i < signature.types.length; ++i) { - visitor.append(" ").append(signature.types[i]); - } - visitor.close(); - } - if (signature.types[0] != null) { - visitor.append(" ").open().append("result "); - visitor.append(signature.types[0]); - visitor.close(); - } - visitor.close(); - visitor.close(); - visitor.lf(); - } } private void renderTable(WasmModule module) { diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java index f6df54660..ecf7a8138 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java @@ -15,11 +15,11 @@ */ package org.teavm.backend.wasm.render; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import org.teavm.backend.wasm.model.WasmLocal; +import org.teavm.backend.wasm.model.WasmModule; +import org.teavm.backend.wasm.model.WasmNumType; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; @@ -54,6 +54,7 @@ import org.teavm.backend.wasm.model.expression.WasmLoadFloat64; import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmLoadInt64; import org.teavm.backend.wasm.model.expression.WasmMemoryGrow; +import org.teavm.backend.wasm.model.expression.WasmNullConstant; import org.teavm.backend.wasm.model.expression.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmStoreFloat32; @@ -71,8 +72,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { private int indentLevel; private boolean lfDeferred; boolean lineNumbersEmitted; - List signatureList = new ArrayList<>(); - private Map signatureMap = new HashMap<>(); + WasmModule module; + + WasmRenderingVisitor(WasmModule module) { + this.module = module; + } void preprocess(WasmExpression expression) { expression.acceptVisitor(new WasmDefaultExpressionVisitor() { @@ -269,6 +273,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { open().append("f64.const " + Double.toHexString(expression.getValue())).close(); } + @Override + public void visit(WasmNullConstant expression) { + open().append("ref.null " + module.types.indexOf(expression.getType())).close(); + } + @Override public void visit(WasmGetLocal expression) { open().append("get_local " + asString(expression.getLocal())).close(); @@ -398,7 +407,7 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { @Override public void visit(WasmCall expression) { - open().append("call").append(" $" + expression.getFunctionName()); + open().append("call").append(" $" + module.functions.indexOf(expression.getFunction())); for (WasmExpression argument : expression.getArguments()) { line(argument); } @@ -407,25 +416,7 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { @Override public void visit(WasmIndirectCall expression) { - WasmType[] types = new WasmType[expression.getParameterTypes().size() + 1]; - types[0] = expression.getReturnType(); - for (int i = 0; i < expression.getParameterTypes().size(); ++i) { - types[i + 1] = expression.getParameterTypes().get(i); - } - open().append("call_indirect").append(" $type" + getSignatureIndex(new WasmSignature(types))); - line(expression.getSelector()); - for (WasmExpression argument : expression.getArguments()) { - line(argument); - } - close(); - } - - int getSignatureIndex(WasmSignature signature) { - return signatureMap.computeIfAbsent(signature, key -> { - signatureList.add(key); - return signatureMap.size(); - }); } @Override @@ -654,6 +645,16 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { } private String type(WasmType type) { + if (type instanceof WasmType.Number) { + return type(((WasmType.Number) type).number); + } else if (type instanceof WasmType.Reference) { + return "(ref " + module.types.indexOf(((WasmType.Reference) type).composite) + ")"; + } else { + throw new IllegalArgumentException(); + } + } + + private String type(WasmNumType type) { switch (type) { case INT32: return "i32"; diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmSignature.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmSignature.java index 9c99b2a93..9a82836ca 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmSignature.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmSignature.java @@ -15,16 +15,25 @@ */ package org.teavm.backend.wasm.render; -import java.util.Arrays; -import org.teavm.backend.wasm.model.WasmFunction; -import org.teavm.backend.wasm.model.WasmTag; +import java.util.List; +import java.util.Objects; import org.teavm.backend.wasm.model.WasmType; -final class WasmSignature { - WasmType[] types; +public final class WasmSignature { + private WasmType returnType; + private List types; - WasmSignature(WasmType[] types) { - this.types = types; + public WasmSignature(WasmType returnType, WasmType... parameterTypes) { + this.returnType = returnType; + this.types = List.of(parameterTypes); + } + + public WasmType getReturnType() { + return returnType; + } + + public List getParameterTypes() { + return types; } @Override @@ -36,28 +45,11 @@ final class WasmSignature { return false; } WasmSignature that = (WasmSignature) o; - return Arrays.equals(types, that.types); + return Objects.equals(types, that.types) && Objects.equals(returnType, that.returnType); } @Override public int hashCode() { - return Arrays.hashCode(types); - } - - static WasmSignature fromFunction(WasmFunction function) { - WasmType[] types = new WasmType[function.getParameters().size() + 1]; - types[0] = function.getResult(); - for (int i = 0; i < function.getParameters().size(); ++i) { - types[i + 1] = function.getParameters().get(i); - } - return new WasmSignature(types); - } - - static WasmSignature fromTag(WasmTag tag) { - var types = new WasmType[tag.getValues().size() + 1]; - for (var i = 0; i < tag.getValues().size(); i++) { - types[i + 1] = tag.getValues().get(i); - } - return new WasmSignature(types); + return Objects.hash(types, returnType); } } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmSignatureCollector.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmSignatureCollector.java deleted file mode 100644 index 68352c1ab..000000000 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmSignatureCollector.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2016 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.render; - -import java.util.function.Consumer; -import org.teavm.backend.wasm.model.WasmType; -import org.teavm.backend.wasm.model.expression.WasmDefaultExpressionVisitor; -import org.teavm.backend.wasm.model.expression.WasmIndirectCall; - -class WasmSignatureCollector extends WasmDefaultExpressionVisitor { - private Consumer consumer; - - public WasmSignatureCollector(Consumer consumer) { - this.consumer = consumer; - } - - @Override - public void visit(WasmIndirectCall expression) { - WasmType[] types = new WasmType[expression.getParameterTypes().size() + 1]; - types[0] = expression.getReturnType(); - for (int i = 0; i < expression.getParameterTypes().size(); ++i) { - types[i + 1] = expression.getParameterTypes().get(i); - } - - consumer.accept(new WasmSignature(types)); - } -} diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java index 99227626b..d4e04b6e5 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java @@ -15,8 +15,6 @@ */ package org.teavm.backend.wasm.render; -import org.teavm.backend.wasm.generate.WasmGenerationContext; -import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; @@ -45,6 +43,7 @@ import org.teavm.backend.wasm.model.expression.WasmLoadFloat64; import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmLoadInt64; import org.teavm.backend.wasm.model.expression.WasmMemoryGrow; +import org.teavm.backend.wasm.model.expression.WasmNullConstant; import org.teavm.backend.wasm.model.expression.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmStoreFloat32; @@ -57,13 +56,8 @@ import org.teavm.backend.wasm.model.expression.WasmTry; import org.teavm.backend.wasm.model.expression.WasmUnreachable; public class WasmTypeInference implements WasmExpressionVisitor { - private WasmGenerationContext context; private WasmType result; - public WasmTypeInference(WasmGenerationContext context) { - this.context = context; - } - public WasmType getResult() { return result; } @@ -155,18 +149,23 @@ public class WasmTypeInference implements WasmExpressionVisitor { @Override public void visit(WasmConversion expression) { - result = expression.getTargetType(); + result = WasmType.num(expression.getTargetType()); + } + + @Override + public void visit(WasmNullConstant expression) { + result = expression.type.getReference(); } @Override public void visit(WasmCall expression) { - WasmFunction function = context.getFunction(expression.getFunctionName()); - result = function == null ? null : function.getResult(); + var function = expression.getFunction(); + result = function == null ? null : function.getType().getReturnType(); } @Override public void visit(WasmIndirectCall expression) { - result = expression.getReturnType(); + result = expression.getType().getReturnType(); } @Override diff --git a/core/src/main/java/org/teavm/backend/wasm/transformation/IndirectCallTraceTransformation.java b/core/src/main/java/org/teavm/backend/wasm/transformation/IndirectCallTraceTransformation.java index cf1d97da9..1df677283 100644 --- a/core/src/main/java/org/teavm/backend/wasm/transformation/IndirectCallTraceTransformation.java +++ b/core/src/main/java/org/teavm/backend/wasm/transformation/IndirectCallTraceTransformation.java @@ -15,6 +15,7 @@ */ package org.teavm.backend.wasm.transformation; +import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmType; @@ -25,33 +26,32 @@ import org.teavm.backend.wasm.model.expression.WasmReplacingExpressionVisitor; public class IndirectCallTraceTransformation { private WasmModule module; + private WasmFunctionTypes functionTypes; - public IndirectCallTraceTransformation(WasmModule module) { + public IndirectCallTraceTransformation(WasmModule module, WasmFunctionTypes functionTypes) { this.module = module; + this.functionTypes = functionTypes; } public void apply() { - WasmFunction traceFunction = new WasmFunction("traceIndirectCall"); + var traceFunction = new WasmFunction(functionTypes.of(WasmType.INT32, WasmType.INT32, WasmType.INT32)); + traceFunction.setName("traceIndirectCall"); traceFunction.setImportModule("debug"); traceFunction.setImportName("traceIndirectCall"); - traceFunction.getParameters().add(WasmType.INT32); - traceFunction.getParameters().add(WasmType.INT32); - traceFunction.setResult(WasmType.INT32); - module.add(traceFunction); + module.functions.add(traceFunction); int[] positionHolder = { 0 }; - WasmReplacingExpressionVisitor visitor = new WasmReplacingExpressionVisitor(expression -> { + var visitor = new WasmReplacingExpressionVisitor(expression -> { if (expression instanceof WasmIndirectCall) { - WasmIndirectCall indirectCall = (WasmIndirectCall) expression; - WasmCall call = new WasmCall(traceFunction.getName()); - call.setImported(true); + var indirectCall = (WasmIndirectCall) expression; + var call = new WasmCall(traceFunction); call.getArguments().add(new WasmInt32Constant(positionHolder[0]++)); call.getArguments().add(indirectCall.getSelector()); indirectCall.setSelector(call); } return expression; }); - for (WasmFunction function : module.getFunctions().values()) { + for (var function : module.functions) { visitor.replace(function); } } diff --git a/core/src/main/java/org/teavm/backend/wasm/transformation/MemoryAccessTraceTransformation.java b/core/src/main/java/org/teavm/backend/wasm/transformation/MemoryAccessTraceTransformation.java index 62447854b..af66b1e43 100644 --- a/core/src/main/java/org/teavm/backend/wasm/transformation/MemoryAccessTraceTransformation.java +++ b/core/src/main/java/org/teavm/backend/wasm/transformation/MemoryAccessTraceTransformation.java @@ -15,6 +15,7 @@ */ package org.teavm.backend.wasm.transformation; +import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmType; @@ -25,33 +26,32 @@ import org.teavm.backend.wasm.model.expression.WasmReplacingExpressionVisitor; public class MemoryAccessTraceTransformation { private WasmModule module; + private WasmFunctionTypes functionTypes; - public MemoryAccessTraceTransformation(WasmModule module) { + public MemoryAccessTraceTransformation(WasmModule module, WasmFunctionTypes functionTypes) { this.module = module; + this.functionTypes = functionTypes; } public void apply() { - WasmFunction traceFunction = new WasmFunction("traceMemoryAccess"); + var traceFunction = new WasmFunction(functionTypes.of(WasmType.INT32, WasmType.INT32, WasmType.INT32)); traceFunction.setImportModule("debug"); + traceFunction.setName("traceMemoryAccess"); traceFunction.setImportName("traceMemoryAccess"); - traceFunction.getParameters().add(WasmType.INT32); - traceFunction.getParameters().add(WasmType.INT32); - traceFunction.setResult(WasmType.INT32); - module.add(traceFunction); + module.functions.add(traceFunction); int[] positionHolder = { 0 }; - WasmReplacingExpressionVisitor visitor = new WasmReplacingExpressionVisitor(expression -> { + var visitor = new WasmReplacingExpressionVisitor(expression -> { if (expression instanceof WasmMemoryAccess) { WasmMemoryAccess memoryAccess = (WasmMemoryAccess) expression; - WasmCall call = new WasmCall(traceFunction.getName()); - call.setImported(true); + WasmCall call = new WasmCall(traceFunction); call.getArguments().add(new WasmInt32Constant(positionHolder[0]++)); call.getArguments().add(memoryAccess.getIndex()); memoryAccess.setIndex(call); } return expression; }); - for (WasmFunction function : module.getFunctions().values()) { + for (var function : module.functions) { visitor.replace(function); } } diff --git a/platform/src/main/java/org/teavm/platform/plugin/ResourceReadIntrinsic.java b/platform/src/main/java/org/teavm/platform/plugin/ResourceReadIntrinsic.java index c40e66530..6737e80dc 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/ResourceReadIntrinsic.java +++ b/platform/src/main/java/org/teavm/platform/plugin/ResourceReadIntrinsic.java @@ -132,14 +132,14 @@ public class ResourceReadIntrinsic implements WasmIntrinsic { switch (invocation.getMethod().getName()) { case "keys": { WasmExpression map = manager.generate(invocation.getArguments().get(0)); - WasmCall call = new WasmCall(manager.getNames().forMethod(KEYS_METHOD)); + var call = new WasmCall(manager.getFunctions().forStaticMethod(KEYS_METHOD)); call.getArguments().add(map); return call; } case "has": { WasmExpression map = manager.generate(invocation.getArguments().get(0)); WasmExpression key = manager.generate(invocation.getArguments().get(1)); - WasmCall call = new WasmCall(manager.getNames().forMethod(LOOKUP_METHOD)); + WasmCall call = new WasmCall(manager.getFunctions().forStaticMethod(LOOKUP_METHOD)); call.getArguments().add(map); call.getArguments().add(key); return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.NE, call, @@ -151,7 +151,7 @@ public class ResourceReadIntrinsic implements WasmIntrinsic { WasmExpression map = manager.generate(invocation.getArguments().get(0)); WasmExpression key = manager.generate(invocation.getArguments().get(1)); - WasmCall call = new WasmCall(manager.getNames().forMethod(LOOKUP_METHOD)); + WasmCall call = new WasmCall(manager.getFunctions().forStaticMethod(LOOKUP_METHOD)); call.getArguments().add(map); call.getArguments().add(key); WasmLocal entryVar = manager.getTemporary(WasmType.INT32);