wasm: add model definitions for GC spec

- refactor Wasm type representation to include reference types
- refactor function representation for consistency with new type system

First step toward Wasm GC support
This commit is contained in:
Alexey Andreev 2024-05-13 21:54:18 +02:00
parent 55657036a1
commit d6474c3aeb
67 changed files with 1462 additions and 775 deletions

View File

@ -70,7 +70,7 @@ public class ServiceLoaderWasmSupport implements WasmIntrinsicFactory {
var table = createServiceData(manager); var table = createServiceData(manager);
var tableArg = new WasmInt32Constant(table); var tableArg = new WasmInt32Constant(table);
var serviceClassAddress = manager.generate(invocation.getArguments().get(0)); 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); serviceClassAddress);
} }
@ -94,10 +94,10 @@ public class ServiceLoaderWasmSupport implements WasmIntrinsicFactory {
var implPointer = manager.getClassPointer(ValueType.object(implementation)); var implPointer = manager.getClassPointer(ValueType.object(implementation));
entry.setAddress(0, implPointer); entry.setAddress(0, implPointer);
var constructorName = manager.getNames().forMethod(new MethodReference( var constructorFn = manager.getFunctions().forInstanceMethod(new MethodReference(
implementation, "<init>", ValueType.VOID implementation, "<init>", ValueType.VOID
)); ));
entry.setInt(1, manager.getFunctionPointer(constructorName)); entry.setInt(1, manager.getFunctionPointer(constructorFn));
} }
return result; return result;

View File

@ -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<MethodReference, WasmFunction> staticMethods = new HashMap<>();
private Map<MethodReference, WasmFunction> instanceMethods = new HashMap<>();
private Map<String, WasmFunction> classInitializers = new HashMap<>();
private Map<ValueType, WasmFunction> 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;
});
}
}

View File

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

View File

@ -36,7 +36,6 @@ import org.teavm.ast.InvocationExpr;
import org.teavm.ast.decompilation.Decompiler; import org.teavm.ast.decompilation.Decompiler;
import org.teavm.backend.lowlevel.analyze.LowLevelInliningFilterFactory; import org.teavm.backend.lowlevel.analyze.LowLevelInliningFilterFactory;
import org.teavm.backend.lowlevel.dependency.StringsDependencyListener; 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.generate.NameProviderWithSpecialNames;
import org.teavm.backend.lowlevel.transform.CoroutineTransformation; import org.teavm.backend.lowlevel.transform.CoroutineTransformation;
import org.teavm.backend.wasm.binary.BinaryWriter; 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.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmStoreInt32; import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
import org.teavm.backend.wasm.optimization.UnusedFunctionElimination; 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.ReportingWasmBinaryStatsCollector;
import org.teavm.backend.wasm.render.WasmBinaryRenderer; import org.teavm.backend.wasm.render.WasmBinaryRenderer;
import org.teavm.backend.wasm.render.WasmBinaryStatsCollector; 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; var statsCollector = this.statsCollector != null ? this.statsCollector : WasmBinaryStatsCollector.EMPTY;
WasmModule module = new WasmModule(); WasmModule module = new WasmModule();
WasmFunction initFunction = new WasmFunction("__start__");
var vtableProvider = createVirtualTableProvider(classes); var vtableProvider = createVirtualTableProvider(classes);
ClassHierarchy hierarchy = new ClassHierarchy(classes); ClassHierarchy hierarchy = new ClassHierarchy(classes);
@ -491,20 +490,24 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
var dwarfClassGen = debugging var dwarfClassGen = debugging
? new DwarfClassGenerator(dwarfGenerator.getInfoWriter(), dwarfGenerator.strings) ? new DwarfClassGenerator(dwarfGenerator.getInfoWriter(), dwarfGenerator.strings)
: null; : null;
var functionTypes = new WasmFunctionTypes(module);
var functions = new WasmFunctionRepository(module, functionTypes, names);
var classGenerator = new WasmClassGenerator(classes, controller.getUnprocessedClassSource(), var classGenerator = new WasmClassGenerator(classes, controller.getUnprocessedClassSource(),
vtableProvider, tagRegistry, binaryWriter, names, metadataRequirements, vtableProvider, tagRegistry, binaryWriter, functions, module, metadataRequirements,
controller.getClassInitializerInfo(), characteristics, dwarfClassGen, statsCollector); controller.getClassInitializerInfo(), characteristics, dwarfClassGen, statsCollector);
Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false); Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false);
var stringPool = classGenerator.getStringPool(); var stringPool = classGenerator.getStringPool();
WasmTag exceptionTag = null; WasmTag exceptionTag = null;
if (exceptionsUsed) { if (exceptionsUsed) {
exceptionTag = new WasmTag(); exceptionTag = new WasmTag(functionTypes.of(null));
module.addTag(exceptionTag); 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); vtableProvider, tagRegistry, stringPool, names, characteristics, exceptionTag);
var initFunction = new WasmFunction(functionTypes.of(null));
context.addIntrinsic(new AddressIntrinsic(classGenerator)); context.addIntrinsic(new AddressIntrinsic(classGenerator));
context.addIntrinsic(new StructureIntrinsic(classes, classGenerator)); context.addIntrinsic(new StructureIntrinsic(classes, classGenerator));
context.addIntrinsic(new FunctionIntrinsic(classGenerator)); context.addIntrinsic(new FunctionIntrinsic(classGenerator));
@ -550,11 +553,11 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
asyncMethods::contains); asyncMethods::contains);
generateMethods(classes, context, generator, classGenerator, binaryWriter, module, dwarfClassGen); generateMethods(classes, context, generator, classGenerator, binaryWriter, module, dwarfClassGen);
new WasmInteropFunctionGenerator(classGenerator).generateFunctions(module); new WasmInteropFunctionGenerator(classGenerator, functionTypes).generateFunctions(module);
exceptionHandlingIntrinsic.postProcess(context.callSites); exceptionHandlingIntrinsic.postProcess(context.callSites);
generateIsSupertypeFunctions(tagRegistry, module, classGenerator); generateIsSupertypeFunctions(tagRegistry, classGenerator, functions);
classGenerator.postProcess(); classGenerator.postProcess();
new WasmSpecialFunctionGenerator(classGenerator, gcIntrinsic.regionSizeExpressions) new WasmSpecialFunctionGenerator(classGenerator, functionTypes, gcIntrinsic.regionSizeExpressions)
.generateSpecialFunctions(module); .generateSpecialFunctions(module);
mutatorIntrinsic.setStaticGcRootsAddress(classGenerator.getStaticGcRootsAddress()); mutatorIntrinsic.setStaticGcRootsAddress(classGenerator.getStaticGcRootsAddress());
mutatorIntrinsic.setClassesAddress(classGenerator.getClassesAddress()); mutatorIntrinsic.setClassesAddress(classGenerator.getClassesAddress());
@ -566,30 +569,25 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
module.getSegments().add(dataSegment); module.getSegments().add(dataSegment);
renderMemoryLayout(module, binaryWriter.getAddress(), gcIntrinsic); renderMemoryLayout(module, binaryWriter.getAddress(), gcIntrinsic);
renderClinit(classes, classGenerator, module); renderClinit(classes, classGenerator, functions);
if (controller.wasCancelled()) { if (controller.wasCancelled()) {
return; return;
} }
generateInitFunction(classes, initFunction, names, binaryWriter.getAddress()); generateInitFunction(classes, initFunction, functions, binaryWriter.getAddress());
module.add(initFunction); module.functions.add(initFunction);
module.setStartFunction(initFunction); module.setStartFunction(initFunction);
module.add(createStartFunction(names)); module.functions.add(createStartFunction(functionTypes, functions));
module.add(createStartCallerFunction(names)); module.functions.add(createStartCallerFunction(functionTypes, functions));
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);
}
new UnusedFunctionElimination(module).apply(); new UnusedFunctionElimination(module).apply();
new UnusedTypeElimination(module).apply();
if (Boolean.parseBoolean(System.getProperty("wasm.memoryTrace", "false"))) { 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"))) { if (Boolean.parseBoolean(System.getProperty("wasm.indirectCallTrace", "false"))) {
new IndirectCallTraceTransformation(module).apply(); new IndirectCallTraceTransformation(module, functionTypes).apply();
} }
writeBinaryWasm(buildTarget, outputName, module, classGenerator, dwarfGenerator, dwarfClassGen, writeBinaryWasm(buildTarget, outputName, module, classGenerator, dwarfGenerator, dwarfClassGen,
@ -669,27 +667,31 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
}; };
} }
private WasmFunction createStartFunction(NameProvider names) { private WasmFunction createStartFunction(WasmFunctionTypes functionTypes, WasmFunctionRepository functions) {
var function = new WasmFunction("teavm_start"); var function = new WasmFunction(functionTypes.of(null, WasmType.INT32));
function.setName("teavm_start");
function.setExportName("start"); function.setExportName("start");
function.getParameters().add(WasmType.INT32);
var local = new WasmLocal(WasmType.INT32, "args"); var local = new WasmLocal(WasmType.INT32, "args");
function.add(local); function.add(local);
var call = new WasmCall(names.forMethod(new MethodReference(WasmSupport.class, "runWithArgs", var runWithArgsFunction = functions.forStaticMethod(new MethodReference(WasmSupport.class, "runWithArgs",
String[].class, void.class))); String[].class, void.class));
var call = new WasmCall(runWithArgsFunction);
call.getArguments().add(new WasmGetLocal(local)); call.getArguments().add(new WasmGetLocal(local));
function.getBody().add(call); function.getBody().add(call);
return function; return function;
} }
private WasmFunction createStartCallerFunction(NameProvider names) { private WasmFunction createStartCallerFunction(WasmFunctionTypes functionTypes, WasmFunctionRepository functions) {
var function = new WasmFunction("teavm_call_start"); var function = new WasmFunction(functionTypes.of(null));
function.setExportName("_start"); 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); function.getBody().add(call);
return function; return function;
@ -718,7 +720,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
} }
private void generateInitFunction(ListableClassReaderSource classes, WasmFunction initFunction, private void generateInitFunction(ListableClassReaderSource classes, WasmFunction initFunction,
NameProvider names, int heapAddress) { WasmFunctionRepository functions, int heapAddress) {
for (Class<?> javaCls : new Class<?>[] { WasmRuntime.class, WasmHeap.class }) { for (Class<?> javaCls : new Class<?>[] { WasmRuntime.class, WasmHeap.class }) {
ClassReader cls = classes.get(javaCls.getName()); ClassReader cls = classes.get(javaCls.getName());
@ -726,10 +728,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
if (clinit == null) { if (clinit == null) {
continue; 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(heapAddress), new WasmInt32Constant(minHeapSize),
new WasmInt32Constant(maxHeapSize), new WasmInt32Constant(WasmHeap.DEFAULT_STACK_SIZE), new WasmInt32Constant(maxHeapSize), new WasmInt32Constant(WasmHeap.DEFAULT_STACK_SIZE),
new WasmInt32Constant(WasmHeap.DEFAULT_BUFFER_SIZE))); new WasmInt32Constant(WasmHeap.DEFAULT_BUFFER_SIZE)));
@ -740,7 +742,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
if (clinit == null) { if (clinit == null) {
continue; continue;
} }
initFunction.getBody().add(new WasmCall(names.forClassInitializer(cls.getName()))); initFunction.getBody().add(new WasmCall(functions.forClassInitializer(cls.getName())));
} }
for (String className : classes.getClassNames()) { for (String className : classes.getClassNames()) {
@ -756,7 +758,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
if (clinit == null) { if (clinit == null) {
continue; 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 { private void emitWast(WasmModule module, BuildTarget buildTarget, String outputName) throws IOException {
WasmRenderer renderer = new WasmRenderer(); WasmRenderer renderer = new WasmRenderer(module);
renderer.setLineNumbersEmitted(debugging); renderer.setLineNumbersEmitted(debugging);
renderer.render(module); renderer.render(module);
try (OutputStream output = buildTarget.createResource(outputName); 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 { private void emitC(WasmModule module, BuildTarget buildTarget, String outputName) throws IOException {
WasmCRenderer renderer = new WasmCRenderer(); var renderer = new WasmCRenderer(module);
renderer.setLineNumbersEmitted(cLineNumbersEmitted); renderer.setLineNumbersEmitted(cLineNumbersEmitted);
renderer.setMemoryAccessChecked(Boolean.parseBoolean(System.getProperty("wasm.c.assertMemory", "false"))); renderer.setMemoryAccessChecked(Boolean.parseBoolean(System.getProperty("wasm.c.assertMemory", "false")));
renderer.render(module); renderer.render(module);
@ -814,13 +816,12 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|| context.getIntrinsic(method.getReference()) != null) { || context.getIntrinsic(method.getReference()) != null) {
continue; continue;
} }
module.add(generator.generateDefinition(method.getReference()));
methods.add(method); methods.add(method);
} }
} }
var methodGeneratorContext = new MethodGeneratorContextImpl(binaryWriter, 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) { for (MethodHolder method : methods) {
ClassHolder cls = classes.get(method.getOwnerName()); ClassHolder cls = classes.get(method.getOwnerName());
@ -847,7 +848,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
if (implementor.hasModifier(ElementModifier.NATIVE)) { if (implementor.hasModifier(ElementModifier.NATIVE)) {
var methodGenerator = context.getGenerator(method.getReference()); var methodGenerator = context.getGenerator(method.getReference());
if (methodGenerator != null) { if (methodGenerator != null) {
WasmFunction function = context.getFunction(context.names.forMethod(method.getReference())); var function = context.functions.forMethod(method);
methodGenerator.apply(method.getReference(), function, methodGeneratorContext); methodGenerator.apply(method.getReference(), function, methodGeneratorContext);
} else if (!isShadowStackMethod(method.getReference())) { } else if (!isShadowStackMethod(method.getReference())) {
if (context.getImportedMethod(method.getReference()) == null) { 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 " controller.getDiagnostics().error(location, "Method {{m0}} is native but "
+ "has no {{c1}} annotation on it", method.getReference(), Import.class.getName()); + "has no {{c1}} annotation on it", method.getReference(), Import.class.getName());
} }
generator.generateNative(method.getReference()); generator.generateNative(method);
} }
continue; continue;
} }
@ -865,7 +866,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
if (method == implementor) { if (method == implementor) {
generator.generate(method.getReference(), implementor); generator.generate(method.getReference(), implementor);
} else { } else {
generateStub(context.names, module, method, implementor); generateStub(context.functions, method, implementor);
} }
if (dwarfClassGen != null) { if (dwarfClassGen != null) {
var dwarfClass = dwarfClassGen.getClass(method.getOwnerName()); var dwarfClass = dwarfClassGen.getClass(method.getOwnerName());
@ -894,13 +895,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
} }
} }
private void generateIsSupertypeFunctions(TagRegistry tagRegistry, WasmModule module, private void generateIsSupertypeFunctions(TagRegistry tagRegistry, WasmClassGenerator classGenerator,
WasmClassGenerator classGenerator) { WasmFunctionRepository functions) {
for (ValueType type : classGenerator.getRegisteredClasses()) { for (ValueType type : classGenerator.getRegisteredClasses()) {
WasmFunction function = new WasmFunction(classGenerator.names.forSupertypeFunction(type)); var function = functions.forSupertype(type);
function.getParameters().add(WasmType.INT32);
function.setResult(WasmType.INT32);
module.add(function);
WasmLocal subtypeVar = new WasmLocal(WasmType.INT32, "subtype"); WasmLocal subtypeVar = new WasmLocal(WasmType.INT32, "subtype");
function.add(subtypeVar); function.add(subtypeVar);
@ -910,7 +908,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
generateIsClass(subtypeVar, classGenerator, tagRegistry, className, function.getBody()); generateIsClass(subtypeVar, classGenerator, tagRegistry, className, function.getBody());
} else if (type instanceof ValueType.Array) { } else if (type instanceof ValueType.Array) {
ValueType itemType = ((ValueType.Array) type).getItemType(); ValueType itemType = ((ValueType.Array) type).getItemType();
generateIsArray(subtypeVar, classGenerator, itemType, function.getBody()); generateIsArray(subtypeVar, classGenerator, functions, itemType, function.getBody());
} else { } else {
int expected = classGenerator.getClassPointer(type); int expected = classGenerator.getClassPointer(type);
WasmExpression condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, 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))); body.add(new WasmReturn(new WasmInt32Constant(1)));
} }
private void generateIsArray(WasmLocal subtypeVar, WasmClassGenerator classGenerator, ValueType itemType, private void generateIsArray(WasmLocal subtypeVar, WasmClassGenerator classGenerator,
List<WasmExpression> body) { WasmFunctionRepository functions, ValueType itemType, List<WasmExpression> body) {
int itemOffset = classGenerator.getFieldOffset(new FieldReference(RuntimeClass.class.getName(), "itemType")); int itemOffset = classGenerator.getFieldOffset(new FieldReference(RuntimeClass.class.getName(), "itemType"));
var itemExpression = new WasmLoadInt32(4, new WasmGetLocal(subtypeVar), WasmInt32Subtype.INT32); 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.setType(WasmType.INT32);
itemTest.getThenBlock().getBody().add(new WasmInt32Constant(0)); 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)); delegateToItem.getArguments().add(new WasmGetLocal(subtypeVar));
itemTest.getElseBlock().getBody().add(delegateToItem); itemTest.getElseBlock().getBody().add(delegateToItem);
body.add(new WasmReturn(itemTest)); body.add(new WasmReturn(itemTest));
} }
private void generateStub(NameProvider names, WasmModule module, MethodHolder method, MethodHolder implementor) { private void generateStub(WasmFunctionRepository functions, MethodHolder method, MethodHolder implementor) {
WasmFunction function = module.getFunctions().get(names.forMethod(method.getReference())); WasmFunction function = functions.forMethod(method);
WasmCall call = new WasmCall(names.forMethod(implementor.getReference())); WasmCall call = new WasmCall(functions.forMethod(implementor));
for (WasmType param : function.getParameters()) { for (WasmType param : function.getType().getParameterTypes()) {
WasmLocal local = new WasmLocal(param); WasmLocal local = new WasmLocal(param);
function.add(local); function.add(local);
call.getArguments().add(new WasmGetLocal(local)); call.getArguments().add(new WasmGetLocal(local));
@ -1009,7 +1007,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
} }
private void renderClinit(ListableClassReaderSource classes, WasmClassGenerator classGenerator, private void renderClinit(ListableClassReaderSource classes, WasmClassGenerator classGenerator,
WasmModule module) { WasmFunctionRepository functions) {
for (ValueType type : classGenerator.getRegisteredClasses()) { for (ValueType type : classGenerator.getRegisteredClasses()) {
if (!(type instanceof ValueType.Object)) { if (!(type instanceof ValueType.Object)) {
continue; continue;
@ -1028,8 +1026,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
continue; continue;
} }
WasmFunction initFunction = new WasmFunction(classGenerator.names.forClassInitializer(className)); var initFunction = functions.forClassInitializer(className);
module.add(initFunction);
WasmBlock block = new WasmBlock(false); 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, block.getBody().add(new WasmStoreInt32(4, new WasmInt32Constant(index), initFlag,
WasmInt32Subtype.INT32)); WasmInt32Subtype.INT32));
block.getBody().add(new WasmCall(classGenerator.names.forMethod(method.getReference()))); block.getBody().add(new WasmCall(functions.forMethod(method)));
if (controller.wasCancelled()) { if (controller.wasCancelled()) {
break; break;
@ -1129,17 +1126,18 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
private BinaryWriter binaryWriter; private BinaryWriter binaryWriter;
private WasmStringPool stringPool; private WasmStringPool stringPool;
private Diagnostics diagnostics; private Diagnostics diagnostics;
private NameProvider names; private WasmFunctionRepository functions;
private WasmClassGenerator classGenerator; private WasmClassGenerator classGenerator;
private ClassReaderSource classSource; private ClassReaderSource classSource;
MethodGeneratorContextImpl(BinaryWriter binaryWriter, WasmStringPool stringPool, MethodGeneratorContextImpl(BinaryWriter binaryWriter, WasmStringPool stringPool,
Diagnostics diagnostics, NameProvider names, WasmClassGenerator classGenerator, Diagnostics diagnostics, WasmFunctionRepository functions,
WasmClassGenerator classGenerator,
ClassReaderSource classSource) { ClassReaderSource classSource) {
this.binaryWriter = binaryWriter; this.binaryWriter = binaryWriter;
this.stringPool = stringPool; this.stringPool = stringPool;
this.diagnostics = diagnostics; this.diagnostics = diagnostics;
this.names = names; this.functions = functions;
this.classGenerator = classGenerator; this.classGenerator = classGenerator;
this.classSource = classSource; this.classSource = classSource;
} }
@ -1159,9 +1157,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
return diagnostics; return diagnostics;
} }
@Override @Override
public NameProvider getNames() { public WasmFunctionRepository getFunctions() {
return names; return functions;
} }
@Override @Override
@ -1197,8 +1196,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
case "runMain": { case "runMain": {
var entryPoint = new MethodReference(controller.getEntryPoint(), var entryPoint = new MethodReference(controller.getEntryPoint(),
"main", ValueType.parse(String[].class), ValueType.parse(void.class)); "main", ValueType.parse(String[].class), ValueType.parse(void.class));
String name = manager.getNames().forMethod(entryPoint); var function = manager.getFunctions().forStaticMethod(entryPoint);
WasmCall call = new WasmCall(name); WasmCall call = new WasmCall(function);
var arg = manager.generate(invocation.getArguments().get(0)); var arg = manager.generate(invocation.getArguments().get(0));
if (manager.isManagedMethodCall(entryPoint)) { if (manager.isManagedMethodCall(entryPoint)) {
var block = new WasmBlock(false); var block = new WasmBlock(false);
@ -1214,9 +1213,9 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
return call; return call;
} }
case "setCurrentThread": { 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)); "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.getArguments().add(manager.generate(invocation.getArguments().get(0)));
call.setLocation(invocation.getLocation()); call.setLocation(invocation.getLocation());
return call; return call;

View File

@ -20,6 +20,7 @@ import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.function.Consumer; 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.WasmType;
import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmFloatType; import org.teavm.backend.wasm.model.expression.WasmFloatType;
@ -103,17 +104,21 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
private String typeToString(WasmType type) { private String typeToString(WasmType type) {
if (type != null) { if (type != null) {
switch (type) { if (type instanceof WasmType.Number) {
case INT32: switch (((WasmType.Number) type).number) {
return "i32"; case INT32:
case INT64: return "i32";
return "i64"; case INT64:
case FLOAT32: return "i64";
return "f32"; case FLOAT32:
case FLOAT64: return "f32";
return "f64"; case FLOAT64:
default: return "f64";
break; default:
break;
}
} else if (type instanceof WasmType.Reference) {
return "ref";
} }
} }
return "unknown"; return "unknown";
@ -615,7 +620,7 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
} }
@Override @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) { switch (targetType) {
case INT32: case INT32:
writer.write("i32."); writer.write("i32.");

View File

@ -16,27 +16,23 @@
package org.teavm.backend.wasm.generate; package org.teavm.backend.wasm.generate;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque; 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.WasmFunction;
import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
class TemporaryVariablePool { class TemporaryVariablePool {
private WasmFunction function; private WasmFunction function;
private List<Deque<WasmLocal>> temporaryVariablesByType = new ArrayList<>(); private Map<WasmType, Deque<WasmLocal>> temporaryVariablesByType = new HashMap<>();
TemporaryVariablePool(WasmFunction function) { TemporaryVariablePool(WasmFunction function) {
this.function = function; this.function = function;
int typeCount = WasmType.values().length;
for (int i = 0; i < typeCount; ++i) {
temporaryVariablesByType.add(new ArrayDeque<>());
}
} }
WasmLocal acquire(WasmType type) { WasmLocal acquire(WasmType type) {
var stack = temporaryVariablesByType.get(type.ordinal()); var stack = temporaryVariablesByType.computeIfAbsent(type, k -> new ArrayDeque<>());
WasmLocal variable = stack.pollFirst(); WasmLocal variable = stack.pollFirst();
if (variable == null) { if (variable == null) {
variable = new WasmLocal(type); variable = new WasmLocal(type);
@ -46,7 +42,7 @@ class TemporaryVariablePool {
} }
void release(WasmLocal variable) { void release(WasmLocal variable) {
var stack = temporaryVariablesByType.get(variable.getType().ordinal()); var stack = temporaryVariablesByType.get(variable.getType());
stack.push(variable); stack.push(variable);
} }
} }

View File

@ -24,7 +24,7 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; 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.BinaryWriter;
import org.teavm.backend.wasm.binary.DataArray; import org.teavm.backend.wasm.binary.DataArray;
import org.teavm.backend.wasm.binary.DataPrimitives; 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.binary.DataValue;
import org.teavm.backend.wasm.debug.DebugClassLayout; import org.teavm.backend.wasm.debug.DebugClassLayout;
import org.teavm.backend.wasm.debug.info.FieldType; 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.backend.wasm.render.WasmBinaryStatsCollector;
import org.teavm.common.IntegerArray; import org.teavm.common.IntegerArray;
import org.teavm.interop.Address; import org.teavm.interop.Address;
@ -60,12 +62,12 @@ public class WasmClassGenerator {
private ClassReaderSource processedClassSource; private ClassReaderSource processedClassSource;
private ClassReaderSource classSource; private ClassReaderSource classSource;
private Characteristics characteristics; private Characteristics characteristics;
public final NameProvider names; public final WasmFunctionRepository functions;
public final WasmModule module;
private Map<ValueType, ClassBinaryData> binaryDataMap = new LinkedHashMap<>(); private Map<ValueType, ClassBinaryData> binaryDataMap = new LinkedHashMap<>();
private BinaryWriter binaryWriter; private BinaryWriter binaryWriter;
private Map<MethodReference, Integer> functions = new HashMap<>(); private Map<MethodReference, Integer> tableFunctions = new HashMap<>();
private List<String> functionTable = new ArrayList<>(); private ObjectIntMap<WasmFunction> functionIdMap = new ObjectIntHashMap<>();
private ObjectIntMap<String> functionIdMap = new ObjectIntHashMap<>();
private VirtualTableProvider vtableProvider; private VirtualTableProvider vtableProvider;
private TagRegistry tagRegistry; private TagRegistry tagRegistry;
private WasmStringPool stringPool; private WasmStringPool stringPool;
@ -122,7 +124,7 @@ public class WasmClassGenerator {
public WasmClassGenerator(ClassReaderSource processedClassSource, ClassReaderSource classSource, public WasmClassGenerator(ClassReaderSource processedClassSource, ClassReaderSource classSource,
VirtualTableProvider vtableProvider, TagRegistry tagRegistry, BinaryWriter binaryWriter, VirtualTableProvider vtableProvider, TagRegistry tagRegistry, BinaryWriter binaryWriter,
NameProvider names, ClassMetadataRequirements metadataRequirements, WasmFunctionRepository functions, WasmModule module, ClassMetadataRequirements metadataRequirements,
ClassInitializerInfo classInitializerInfo, Characteristics characteristics, ClassInitializerInfo classInitializerInfo, Characteristics characteristics,
DwarfClassGenerator dwarfClassGenerator, WasmBinaryStatsCollector statsCollector) { DwarfClassGenerator dwarfClassGenerator, WasmBinaryStatsCollector statsCollector) {
this.processedClassSource = processedClassSource; this.processedClassSource = processedClassSource;
@ -130,8 +132,9 @@ public class WasmClassGenerator {
this.vtableProvider = vtableProvider; this.vtableProvider = vtableProvider;
this.tagRegistry = tagRegistry; this.tagRegistry = tagRegistry;
this.binaryWriter = binaryWriter; this.binaryWriter = binaryWriter;
this.functions = functions;
this.module = module;
this.stringPool = new WasmStringPool(this, binaryWriter, statsCollector); this.stringPool = new WasmStringPool(this, binaryWriter, statsCollector);
this.names = names;
this.metadataRequirements = metadataRequirements; this.metadataRequirements = metadataRequirements;
this.classInitializerInfo = classInitializerInfo; this.classInitializerInfo = classInitializerInfo;
this.characteristics = characteristics; this.characteristics = characteristics;
@ -223,7 +226,7 @@ public class WasmClassGenerator {
binaryData.data = wrapper.getValue(0); binaryData.data = wrapper.getValue(0);
binaryData.data.setInt(CLASS_SIZE, 4); binaryData.data.setInt(CLASS_SIZE, 4);
binaryData.data.setAddress(CLASS_ITEM_TYPE, itemBinaryData.start); 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.setInt(CLASS_CANARY, RuntimeClass.computeCanary(4, 0));
binaryData.data.setAddress(CLASS_NAME, stringPool.getStringPointer(type.toString().replace('/', '.'))); binaryData.data.setAddress(CLASS_NAME, stringPool.getStringPointer(type.toString().replace('/', '.')));
binaryData.data.setAddress(CLASS_SIMPLE_NAME, 0); binaryData.data.setAddress(CLASS_SIMPLE_NAME, 0);
@ -238,7 +241,7 @@ public class WasmClassGenerator {
private DataValue createPrimitiveClassData(DataValue value, int size, ValueType type) { private DataValue createPrimitiveClassData(DataValue value, int size, ValueType type) {
value.setInt(CLASS_SIZE, size); value.setInt(CLASS_SIZE, size);
value.setInt(CLASS_FLAGS, RuntimeClass.PRIMITIVE); 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.setAddress(CLASS_SIMPLE_NAME, 0);
value.setInt(CLASS_INIT, -1); value.setInt(CLASS_INIT, -1);
value.setInt(CLASS_TAG, Integer.MAX_VALUE); value.setInt(CLASS_TAG, Integer.MAX_VALUE);
@ -282,16 +285,12 @@ public class WasmClassGenerator {
return value; return value;
} }
public Iterable<? extends String> getFunctionTable() { public int getFunctionPointer(WasmFunction function) {
return functionTable; var result = functionIdMap.getOrDefault(function, -1);
}
public int getFunctionPointer(String name) {
var result = functionIdMap.getOrDefault(name, -1);
if (result < 0) { if (result < 0) {
result = functionTable.size(); result = module.getFunctionTable().size();
functionTable.add(name); module.getFunctionTable().add(function);
functionIdMap.put(name, result); functionIdMap.put(function, result);
} }
return result; return result;
} }
@ -326,7 +325,7 @@ public class WasmClassGenerator {
header.setInt(CLASS_CANARY, RuntimeClass.computeCanary(occupiedSize, tag)); header.setInt(CLASS_CANARY, RuntimeClass.computeCanary(occupiedSize, tag));
int nameAddress = requirements.name() ? stringPool.getStringPointer(name) : 0; int nameAddress = requirements.name() ? stringPool.getStringPointer(name) : 0;
header.setAddress(CLASS_NAME, nameAddress); 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); header.setAddress(CLASS_PARENT, parentPtr);
ClassReader cls = processedClassSource.get(name); ClassReader cls = processedClassSource.get(name);
@ -381,7 +380,7 @@ public class WasmClassGenerator {
if (cls != null && binaryData.start >= 0 if (cls != null && binaryData.start >= 0
&& cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID)) != null && cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID)) != null
&& classInitializerInfo.isDynamicInitializer(name)) { && classInitializerInfo.isDynamicInitializer(name)) {
header.setInt(CLASS_INIT, getFunctionPointer(names.forClassInitializer(name))); header.setInt(CLASS_INIT, getFunctionPointer(functions.forClassInitializer(name)));
} else { } else {
header.setInt(CLASS_INIT, -1); header.setInt(CLASS_INIT, -1);
} }
@ -458,8 +457,8 @@ public class WasmClassGenerator {
if (method != null) { if (method != null) {
VirtualTableEntry entry = vtable.getEntry(method); VirtualTableEntry entry = vtable.getEntry(method);
if (entry != null) { if (entry != null) {
methodIndex = functions.computeIfAbsent(entry.getImplementor(), methodIndex = tableFunctions.computeIfAbsent(entry.getImplementor(),
implementor -> getFunctionPointer(names.forMethod(implementor))); implementor -> getFunctionPointer(functions.forInstanceMethod(implementor)));
} }
} }

View File

@ -20,9 +20,10 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.teavm.backend.lowlevel.generate.NameProvider; 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.generators.WasmMethodGenerator;
import org.teavm.backend.wasm.intrinsics.WasmIntrinsic; 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.WasmModule;
import org.teavm.backend.wasm.model.WasmTag; import org.teavm.backend.wasm.model.WasmTag;
import org.teavm.diagnostics.Diagnostics; import org.teavm.diagnostics.Diagnostics;
@ -43,7 +44,9 @@ import org.teavm.model.lowlevel.Characteristics;
public class WasmGenerationContext { public class WasmGenerationContext {
private ClassReaderSource classSource; private ClassReaderSource classSource;
private WasmModule module; public final WasmModule module;
public final WasmFunctionTypes functionTypes;
public final WasmFunctionRepository functions;
private Diagnostics diagnostics; private Diagnostics diagnostics;
private VirtualTableProvider vtableProvider; private VirtualTableProvider vtableProvider;
private TagRegistry tagRegistry; private TagRegistry tagRegistry;
@ -58,11 +61,14 @@ public class WasmGenerationContext {
private WasmTag exceptionTag; private WasmTag exceptionTag;
public final List<CallSiteDescriptor> callSites = new ArrayList<>(); public final List<CallSiteDescriptor> callSites = new ArrayList<>();
public WasmGenerationContext(ClassReaderSource classSource, WasmModule module, Diagnostics diagnostics, public WasmGenerationContext(ClassReaderSource classSource, WasmModule module, WasmFunctionTypes functionTypes,
VirtualTableProvider vtableProvider, TagRegistry tagRegistry, WasmStringPool stringPool, WasmFunctionRepository functions, Diagnostics diagnostics, VirtualTableProvider vtableProvider,
NameProvider names, Characteristics characteristics, WasmTag exceptionTag) { TagRegistry tagRegistry, WasmStringPool stringPool, NameProvider names, Characteristics characteristics,
WasmTag exceptionTag) {
this.classSource = classSource; this.classSource = classSource;
this.module = module; this.module = module;
this.functionTypes = functionTypes;
this.functions = functions;
this.diagnostics = diagnostics; this.diagnostics = diagnostics;
this.vtableProvider = vtableProvider; this.vtableProvider = vtableProvider;
this.tagRegistry = tagRegistry; this.tagRegistry = tagRegistry;
@ -135,10 +141,6 @@ public class WasmGenerationContext {
}); });
} }
public WasmFunction getFunction(String name) {
return module.getFunctions().get(name);
}
public ClassReaderSource getClassSource() { public ClassReaderSource getClassSource() {
return classSource; return classSource;
} }

View File

@ -66,7 +66,8 @@ import org.teavm.ast.UnaryExpr;
import org.teavm.ast.UnwrapArrayExpr; import org.teavm.ast.UnwrapArrayExpr;
import org.teavm.ast.VariableExpr; import org.teavm.ast.VariableExpr;
import org.teavm.ast.WhileStatement; 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.WasmHeap;
import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.WasmRuntime;
import org.teavm.backend.wasm.binary.BinaryWriter; 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.intrinsics.WasmIntrinsicManager;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmLocal; 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.WasmTag;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBlock;
@ -196,12 +198,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
this.firstVariable = firstVariable; this.firstVariable = firstVariable;
tempVars = new TemporaryVariablePool(function); tempVars = new TemporaryVariablePool(function);
exprCache = new ExpressionCache(tempVars); exprCache = new ExpressionCache(tempVars);
typeInference = new WasmTypeInference(context); typeInference = new WasmTypeInference();
this.async = async; this.async = async;
this.managed = context.characteristics.isManaged(currentMethod); this.managed = context.characteristics.isManaged(currentMethod);
} }
void generate(Statement statement, List<WasmExpression> target) { void generate(Statement statement, List<WasmExpression> target) {
var lastTargetSize = target.size(); var lastTargetSize = target.size();
resultConsumer = target; resultConsumer = target;
@ -212,7 +213,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
rethrowBlock.getBody().addAll(body); rethrowBlock.getBody().addAll(body);
body.clear(); body.clear();
target.add(rethrowBlock); target.add(rethrowBlock);
var valueToReturn = WasmExpression.defaultValueOfType(function.getResult()); var valueToReturn = WasmExpression.defaultValueOfType(function.getType().getReturnType());
if (valueToReturn != null) { if (valueToReturn != null) {
target.add(new WasmReturn(valueToReturn)); target.add(new WasmReturn(valueToReturn));
} }
@ -254,7 +255,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
default: default:
Class<?> type = convertType(expr.getType()); Class<?> type = convertType(expr.getType());
MethodReference method = new MethodReference(WasmRuntime.class, "remainder", type, type, type); 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()); accept(expr.getFirstOperand());
call.getArguments().add(result); call.getArguments().add(result);
@ -308,7 +309,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
case COMPARE: { case COMPARE: {
Class<?> type = convertType(expr.getType()); Class<?> type = convertType(expr.getType());
MethodReference method = new MethodReference(WasmRuntime.class, "compare", type, type, int.class); 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()); accept(expr.getFirstOperand());
call.getArguments().add(result); call.getArguments().add(result);
@ -367,7 +368,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
case LEFT_SHIFT: case LEFT_SHIFT:
case RIGHT_SHIFT: case RIGHT_SHIFT:
case UNSIGNED_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; break;
default: default:
break; break;
@ -529,7 +530,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
var callSiteId = generateCallSiteId(location); var callSiteId = generateCallSiteId(location);
block.getBody().add(generateRegisterCallSite(callSiteId, 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); block.getBody().add(call);
if (context.getExceptionTag() == null) { if (context.getExceptionTag() == null) {
@ -1155,12 +1156,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) { if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) {
MethodReader method = context.getClassSource().resolve(expr.getMethod()); MethodReader method = context.getClassSource().resolve(expr.getMethod());
MethodReference reference = method != null ? method.getReference() : 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); var call = new WasmCall(function);
if (context.getImportedMethod(reference) != null) {
call.setImported(true);
}
for (Expr argument : expr.getArguments()) { for (Expr argument : expr.getArguments()) {
accept(argument); accept(argument);
call.getArguments().add(result); call.getArguments().add(result);
@ -1179,8 +1177,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
block.getBody().add(new WasmSetLocal(tmp, allocateObject(expr.getMethod().getClassName(), block.getBody().add(new WasmSetLocal(tmp, allocateObject(expr.getMethod().getClassName(),
expr.getLocation()))); expr.getLocation())));
String methodName = context.names.forMethod(expr.getMethod()); var function = context.functions.forInstanceMethod(expr.getMethod());
WasmCall call = new WasmCall(methodName); var call = new WasmCall(function);
call.getArguments().add(new WasmGetLocal(tmp)); call.getArguments().add(new WasmGetLocal(tmp));
for (Expr argument : expr.getArguments()) { for (Expr argument : expr.getArguments()) {
accept(argument); accept(argument);
@ -1220,14 +1218,14 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
var methodIndex = new WasmLoadInt32(4, classRef, WasmInt32Subtype.INT32); var methodIndex = new WasmLoadInt32(4, classRef, WasmInt32Subtype.INT32);
methodIndex.setOffset(vtableIndex * 4 + vtableOffset); methodIndex.setOffset(vtableIndex * 4 + vtableOffset);
WasmIndirectCall call = new WasmIndirectCall(methodIndex); var parameterTypes = new WasmType[expr.getMethod().parameterCount() + 1];
call.getParameterTypes().add(WasmType.INT32); parameterTypes[0] = WasmType.INT32;
for (int i = 0; i < expr.getMethod().parameterCount(); ++i) { for (var i = 0; i < expr.getMethod().parameterCount(); ++i) {
call.getParameterTypes().add(WasmGeneratorUtil.mapType(expr.getMethod().parameterType(i))); parameterTypes[i + 1] = WasmGeneratorUtil.mapType(expr.getMethod().parameterType(i));
}
if (expr.getMethod().getReturnType() != ValueType.VOID) {
call.setReturnType(WasmGeneratorUtil.mapType(expr.getMethod().getReturnType()));
} }
var functionType = context.functionTypes.of(WasmGeneratorUtil.mapType(expr.getMethod().getReturnType()),
parameterTypes);
var call = new WasmIndirectCall(methodIndex, functionType);
call.getArguments().add(instance); call.getArguments().add(instance);
for (int i = 1; i < expr.getArguments().size(); ++i) { for (int i = 1; i < expr.getArguments().size(); ++i) {
@ -1267,7 +1265,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
stackVariable = tempVars.acquire(WasmType.INT32); stackVariable = tempVars.acquire(WasmType.INT32);
stackVariable.setName("__stack__"); stackVariable.setName("__stack__");
InvocationExpr expr = new InvocationExpr(); InvocationExpr expr = new InvocationExpr();
expr.setType(InvocationType.SPECIAL); expr.setType(InvocationType.STATIC);
expr.setMethod(new MethodReference(WasmRuntime.class, "allocStack", int.class, Address.class)); expr.setMethod(new MethodReference(WasmRuntime.class, "allocStack", int.class, Address.class));
expr.getArguments().add(sizeExpr); expr.getArguments().add(sizeExpr);
expr.acceptVisitor(this); expr.acceptVisitor(this);
@ -1506,9 +1504,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
private WasmExpression allocateObject(String className, TextLocation location) { private WasmExpression allocateObject(String className, TextLocation location) {
int tag = classGenerator.getClassPointer(ValueType.object(className)); 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)); RuntimeClass.class, Address.class));
WasmCall call = new WasmCall(allocName); WasmCall call = new WasmCall(allocFunction);
call.getArguments().add(new WasmInt32Constant(tag)); call.getArguments().add(new WasmInt32Constant(tag));
call.setLocation(location); call.setLocation(location);
return call; return call;
@ -1525,9 +1523,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
ValueType type = expr.getType(); ValueType type = expr.getType();
int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type)); 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)); RuntimeClass.class, int.class, Address.class));
WasmCall call = new WasmCall(allocName); var call = new WasmCall(allocFunction);
call.getArguments().add(new WasmInt32Constant(classPointer)); call.getArguments().add(new WasmInt32Constant(classPointer));
accept(expr.getLength()); accept(expr.getLength());
call.getArguments().add(result); call.getArguments().add(result);
@ -1576,9 +1574,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
WasmLocal array = tempVars.acquire(WasmType.INT32); WasmLocal array = tempVars.acquire(WasmType.INT32);
int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type)); 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)); 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(classPointer));
call.getArguments().add(new WasmInt32Constant(expr.getData().size())); call.getArguments().add(new WasmInt32Constant(expr.getData().size()));
call.setLocation(expr.getLocation()); call.setLocation(expr.getLocation());
@ -1616,9 +1614,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
} }
int classPointer = classGenerator.getClassPointer(type); 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)); 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(classPointer));
call.getArguments().add(new WasmInt32Constant(dimensionList)); call.getArguments().add(new WasmInt32Constant(dimensionList));
call.getArguments().add(new WasmInt32Constant(expr.getDimensions().size())); call.getArguments().add(new WasmInt32Constant(expr.getDimensions().size()));
@ -1673,7 +1671,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
} }
private WasmExpression instanceOfImpl(WasmExpression expression, ValueType type) { 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); WasmExpression classRef = new WasmLoadInt32(4, expression, WasmInt32Subtype.INT32);
classRef = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, classRef, classRef = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, classRef,
new WasmInt32Constant(3)); new WasmInt32Constant(3));
@ -1687,7 +1685,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
resultConsumer.add(generateRegisterCallSite(callSiteId, statement.getLocation())); resultConsumer.add(generateRegisterCallSite(callSiteId, statement.getLocation()));
accept(statement.getException()); 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()); call.setLocation(statement.getLocation());
resultConsumer.add(call); resultConsumer.add(call);
@ -1728,7 +1726,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
nullCheck.setResult(valueToCast.expr()); nullCheck.setResult(valueToCast.expr());
block.getBody().add(new WasmDrop(nullCheck)); 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); WasmExpression classRef = new WasmLoadInt32(4, valueToCast.expr(), WasmInt32Subtype.INT32);
classRef = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, classRef, classRef = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, classRef,
new WasmInt32Constant(3)); new WasmInt32Constant(3));
@ -1741,7 +1739,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
var callSiteId = generateCallSiteId(expr.getLocation()); var callSiteId = generateCallSiteId(expr.getLocation());
block.getBody().add(generateRegisterCallSite(callSiteId, 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); block.getBody().add(call);
if (context.getExceptionTag() == null) { if (context.getExceptionTag() == null) {
@ -1762,7 +1760,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
@Override @Override
public void visit(InitClassStatement statement) { public void visit(InitClassStatement statement) {
if (classGenerator.hasClinit(statement.getClassName())) { 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()); call.setLocation(statement.getLocation());
var callSiteId = generateCallSiteId(statement.getLocation()); var callSiteId = generateCallSiteId(statement.getLocation());
@ -1853,8 +1851,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
var tryCatch = tryCatchStatements.get(i); var tryCatch = tryCatchStatements.get(i);
var catchBlock = catchBlocks.get(i); var catchBlock = catchBlocks.get(i);
catchBlock.getBody().add(currentBlock); catchBlock.getBody().add(currentBlock);
var catchMethodName = context.names.forMethod(CATCH_METHOD); var catchFunction = context.functions.forStaticMethod(CATCH_METHOD);
var catchCall = new WasmCall(catchMethodName); var catchCall = new WasmCall(catchFunction);
var catchWrapper = tryCatch.getExceptionVariable() != null var catchWrapper = tryCatch.getExceptionVariable() != null
? new WasmSetLocal(localVar(tryCatch.getExceptionVariable()), catchCall) ? new WasmSetLocal(localVar(tryCatch.getExceptionVariable()), catchCall)
: new WasmDrop(catchCall); : new WasmDrop(catchCall);
@ -1887,8 +1885,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
tryBlock.getCatches().add(catchClause); tryBlock.getCatches().add(catchClause);
innerCatchBlock.getBody().add(tryBlock); innerCatchBlock.getBody().add(tryBlock);
var obj = exprCache.create(new WasmCall(context.names.forMethod(PEEK_EXCEPTION_METHOD)), WasmType.INT32, var obj = exprCache.create(new WasmCall(context.functions.forStaticMethod(PEEK_EXCEPTION_METHOD)),
null, innerCatchBlock.getBody()); WasmType.INT32, null, innerCatchBlock.getBody());
var currentBlock = innerCatchBlock; var currentBlock = innerCatchBlock;
boolean catchesAll = false; boolean catchesAll = false;
for (int i = tryCatchStatements.size() - 1; i >= 0; --i) { for (int i = tryCatchStatements.size() - 1; i >= 0; --i) {
@ -1916,8 +1914,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
var catchBlock = catchBlocks.get(i); var catchBlock = catchBlocks.get(i);
catchBlock.getBody().add(currentBlock); catchBlock.getBody().add(currentBlock);
var catchMethodName = context.names.forMethod(CATCH_METHOD); var catchFunction = context.functions.forStaticMethod(CATCH_METHOD);
var catchCall = new WasmCall(catchMethodName); var catchCall = new WasmCall(catchFunction);
var catchWrapper = tryCatch.getExceptionVariable() != null var catchWrapper = tryCatch.getExceptionVariable() != null
? new WasmSetLocal(localVar(tryCatch.getExceptionVariable()), catchCall) ? new WasmSetLocal(localVar(tryCatch.getExceptionVariable()), catchCall)
: new WasmDrop(catchCall); : new WasmDrop(catchCall);
@ -1954,7 +1952,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
@Override @Override
public void visit(MonitorEnterStatement statement) { 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()); call.setLocation(statement.getLocation());
statement.getObjectRef().acceptVisitor(this); statement.getObjectRef().acceptVisitor(this);
call.getArguments().add(result); call.getArguments().add(result);
@ -1967,7 +1965,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
@Override @Override
public void visit(MonitorExitStatement statement) { 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()); call.setLocation(statement.getLocation());
statement.getObjectRef().acceptVisitor(this); statement.getObjectRef().acceptVisitor(this);
call.getArguments().add(result); call.getArguments().add(result);
@ -2021,7 +2019,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
var callSiteId = generateCallSiteId(expr.getLocation()); var callSiteId = generateCallSiteId(expr.getLocation());
block.getBody().add(generateRegisterCallSite(callSiteId, 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) { if (context.getExceptionTag() == null) {
var br = new WasmBreak(throwJumpTarget()); var br = new WasmBreak(throwJumpTarget());
if (br.getTarget() != rethrowBlock) { if (br.getTarget() != rethrowBlock) {
@ -2213,8 +2211,13 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
} }
@Override @Override
public NameProvider getNames() { public WasmFunctionRepository getFunctions() {
return context.names; return context.functions;
}
@Override
public WasmFunctionTypes getFunctionTypes() {
return context.functionTypes;
} }
@Override @Override
@ -2238,8 +2241,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
} }
@Override @Override
public int getFunctionPointer(String name) { public int getFunctionPointer(WasmFunction function) {
return classGenerator.getFunctionPointer(name); return classGenerator.getFunctionPointer(function);
} }
@Override @Override

View File

@ -19,12 +19,10 @@ import java.util.function.Predicate;
import org.teavm.ast.RegularMethodNode; import org.teavm.ast.RegularMethodNode;
import org.teavm.ast.VariableNode; import org.teavm.ast.VariableNode;
import org.teavm.ast.decompilation.Decompiler; 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.binary.BinaryWriter;
import org.teavm.backend.wasm.debug.info.VariableType; import org.teavm.backend.wasm.debug.info.VariableType;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmTag;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
import org.teavm.interop.Export; import org.teavm.interop.Export;
import org.teavm.model.AnnotationReader; import org.teavm.model.AnnotationReader;
@ -32,8 +30,8 @@ import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderSource; import org.teavm.model.ClassHolderSource;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
public class WasmGenerator { public class WasmGenerator {
private Decompiler decompiler; private Decompiler decompiler;
@ -41,9 +39,7 @@ public class WasmGenerator {
private WasmGenerationContext context; private WasmGenerationContext context;
private WasmClassGenerator classGenerator; private WasmClassGenerator classGenerator;
private BinaryWriter binaryWriter; private BinaryWriter binaryWriter;
private NameProvider names;
private Predicate<MethodReference> asyncMethods; private Predicate<MethodReference> asyncMethods;
private WasmTag exceptionTag;
public WasmGenerator(Decompiler decompiler, ClassHolderSource classSource, public WasmGenerator(Decompiler decompiler, ClassHolderSource classSource,
WasmGenerationContext context, WasmClassGenerator classGenerator, BinaryWriter binaryWriter, WasmGenerationContext context, WasmClassGenerator classGenerator, BinaryWriter binaryWriter,
@ -53,35 +49,15 @@ public class WasmGenerator {
this.context = context; this.context = context;
this.classGenerator = classGenerator; this.classGenerator = classGenerator;
this.binaryWriter = binaryWriter; this.binaryWriter = binaryWriter;
names = classGenerator.names;
this.asyncMethods = asyncMethods; 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) { public WasmFunction generate(MethodReference methodReference, MethodHolder bodyMethod) {
ClassHolder cls = classSource.get(methodReference.getClassName()); ClassHolder cls = classSource.get(methodReference.getClassName());
MethodHolder method = cls.getMethod(methodReference.getDescriptor()); MethodHolder method = cls.getMethod(methodReference.getDescriptor());
RegularMethodNode methodAst = decompiler.decompileRegular(bodyMethod); 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; int firstVariable = method.hasModifier(ElementModifier.STATIC) ? 1 : 0;
for (int i = firstVariable; i < methodAst.getVariables().size(); ++i) { for (int i = firstVariable; i < methodAst.getVariables().size(); ++i) {
VariableNode variable = methodAst.getVariables().get(i); VariableNode variable = methodAst.getVariables().get(i);
@ -120,10 +96,10 @@ public class WasmGenerator {
} }
} }
public WasmFunction generateNative(MethodReference methodReference) { public WasmFunction generateNative(MethodReader method) {
WasmFunction function = context.getFunction(names.forMethod(methodReference)); var function = context.functions.forMethod(method);
WasmGenerationContext.ImportedMethod importedMethod = context.getImportedMethod(methodReference); var importedMethod = context.getImportedMethod(method.getReference());
if (importedMethod != null) { if (importedMethod != null) {
function.setImportName(importedMethod.name); function.setImportName(importedMethod.name);
function.setImportModule(importedMethod.module); function.setImportModule(importedMethod.module);

View File

@ -16,6 +16,7 @@
package org.teavm.backend.wasm.generate; package org.teavm.backend.wasm.generate;
import org.teavm.ast.OperationType; import org.teavm.ast.OperationType;
import org.teavm.backend.wasm.model.WasmNumType;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.util.VariableType; import org.teavm.model.util.VariableType;
@ -24,16 +25,16 @@ public final class WasmGeneratorUtil {
private WasmGeneratorUtil() { private WasmGeneratorUtil() {
} }
public static WasmType mapType(OperationType type) { public static WasmNumType mapType(OperationType type) {
switch (type) { switch (type) {
case INT: case INT:
return WasmType.INT32; return WasmNumType.INT32;
case LONG: case LONG:
return WasmType.INT64; return WasmNumType.INT64;
case FLOAT: case FLOAT:
return WasmType.FLOAT32; return WasmNumType.FLOAT32;
case DOUBLE: case DOUBLE:
return WasmType.FLOAT64; return WasmNumType.FLOAT64;
} }
throw new IllegalArgumentException(type.toString()); throw new IllegalArgumentException(type.toString());
} }

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.backend.wasm.generate; 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.WasmFunction;
import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmModule;
@ -39,48 +40,49 @@ import org.teavm.runtime.RuntimeClass;
public class WasmInteropFunctionGenerator { public class WasmInteropFunctionGenerator {
private WasmClassGenerator classGenerator; private WasmClassGenerator classGenerator;
private WasmFunctionTypes functionTypes;
public WasmInteropFunctionGenerator(WasmClassGenerator classGenerator) { public WasmInteropFunctionGenerator(WasmClassGenerator classGenerator, WasmFunctionTypes functionTypes) {
this.classGenerator = classGenerator; this.classGenerator = classGenerator;
this.functionTypes = functionTypes;
} }
public void generateFunctions(WasmModule module) { public void generateFunctions(WasmModule module) {
module.add(allocateString()); module.functions.add(allocateString());
module.add(stringData()); module.functions.add(stringData());
module.add(allocateArray("teavm_allocateObjectArray", ValueType.parse(Object.class))); module.functions.add(allocateArray("teavm_allocateObjectArray", ValueType.parse(Object.class)));
module.add(allocateArray("teavm_allocateStringArray", ValueType.parse(String.class))); module.functions.add(allocateArray("teavm_allocateStringArray", ValueType.parse(String.class)));
module.add(allocateArray("teavm_allocateByteArray", ValueType.parse(byte.class))); module.functions.add(allocateArray("teavm_allocateByteArray", ValueType.parse(byte.class)));
module.add(allocateArray("teavm_allocateShortArray", ValueType.parse(short.class))); module.functions.add(allocateArray("teavm_allocateShortArray", ValueType.parse(short.class)));
module.add(allocateArray("teavm_allocateCharArray", ValueType.parse(char.class))); module.functions.add(allocateArray("teavm_allocateCharArray", ValueType.parse(char.class)));
module.add(allocateArray("teavm_allocateIntArray", ValueType.parse(int.class))); module.functions.add(allocateArray("teavm_allocateIntArray", ValueType.parse(int.class)));
module.add(allocateArray("teavm_allocateLongArray", ValueType.parse(long.class))); module.functions.add(allocateArray("teavm_allocateLongArray", ValueType.parse(long.class)));
module.add(allocateArray("teavm_allocateFloatArray", ValueType.parse(float.class))); module.functions.add(allocateArray("teavm_allocateFloatArray", ValueType.parse(float.class)));
module.add(allocateArray("teavm_allocateDoubleArray", ValueType.parse(double.class))); module.functions.add(allocateArray("teavm_allocateDoubleArray", ValueType.parse(double.class)));
module.add(arrayData("teavm_objectArrayData", 4)); module.functions.add(arrayData("teavm_objectArrayData", 4));
module.add(arrayData("teavm_byteArrayData", 1)); module.functions.add(arrayData("teavm_byteArrayData", 1));
module.add(arrayData("teavm_shortArrayData", 2)); module.functions.add(arrayData("teavm_shortArrayData", 2));
module.add(arrayData("teavm_charArrayData", 2)); module.functions.add(arrayData("teavm_charArrayData", 2));
module.add(arrayData("teavm_intArrayData", 4)); module.functions.add(arrayData("teavm_intArrayData", 4));
module.add(arrayData("teavm_longArrayData", 8)); module.functions.add(arrayData("teavm_longArrayData", 8));
module.add(arrayData("teavm_floatArrayData", 4)); module.functions.add(arrayData("teavm_floatArrayData", 4));
module.add(arrayData("teavm_doubleArrayData", 8)); module.functions.add(arrayData("teavm_doubleArrayData", 8));
module.add(arrayLength()); module.functions.add(arrayLength());
} }
private WasmFunction allocateString() { 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.setExportName(function.getName());
function.setResult(WasmType.INT32);
function.getParameters().add(WasmType.INT32);
WasmLocal sizeLocal = new WasmLocal(WasmType.INT32, "size"); WasmLocal sizeLocal = new WasmLocal(WasmType.INT32, "size");
function.add(sizeLocal); 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)); int.class, String.class));
WasmCall constructorCall = new WasmCall(constructorName); WasmCall constructorCall = new WasmCall(constructor);
constructorCall.getArguments().add(new WasmGetLocal(sizeLocal)); constructorCall.getArguments().add(new WasmGetLocal(sizeLocal));
function.getBody().add(constructorCall); function.getBody().add(constructorCall);
@ -90,18 +92,17 @@ public class WasmInteropFunctionGenerator {
} }
private WasmFunction allocateArray(String name, ValueType type) { 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.setExportName(name);
function.setResult(WasmType.INT32);
function.getParameters().add(WasmType.INT32);
WasmLocal sizeLocal = new WasmLocal(WasmType.INT32, "size"); WasmLocal sizeLocal = new WasmLocal(WasmType.INT32, "size");
function.add(sizeLocal); function.add(sizeLocal);
int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type)); int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type));
String allocName = classGenerator.names.forMethod(new MethodReference(Allocator.class, "allocateArray", var allocFunction = classGenerator.functions.forStaticMethod(new MethodReference(Allocator.class,
RuntimeClass.class, int.class, Address.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(classPointer));
call.getArguments().add(new WasmGetLocal(sizeLocal)); call.getArguments().add(new WasmGetLocal(sizeLocal));
@ -111,10 +112,9 @@ public class WasmInteropFunctionGenerator {
} }
private WasmFunction stringData() { 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.setExportName(function.getName());
function.setResult(WasmType.INT32);
function.getParameters().add(WasmType.INT32);
WasmLocal stringLocal = new WasmLocal(WasmType.INT32, "string"); WasmLocal stringLocal = new WasmLocal(WasmType.INT32, "string");
function.add(stringLocal); function.add(stringLocal);
@ -128,10 +128,9 @@ public class WasmInteropFunctionGenerator {
} }
private WasmFunction arrayData(String name, int alignment) { 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.setExportName(function.getName());
function.setResult(WasmType.INT32);
function.getParameters().add(WasmType.INT32);
WasmLocal arrayLocal = new WasmLocal(WasmType.INT32, "array"); WasmLocal arrayLocal = new WasmLocal(WasmType.INT32, "array");
function.add(arrayLocal); function.add(arrayLocal);
@ -147,10 +146,9 @@ public class WasmInteropFunctionGenerator {
} }
private WasmFunction arrayLength() { 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.setExportName(function.getName());
function.setResult(WasmType.INT32);
function.getParameters().add(WasmType.INT32);
WasmLocal arrayLocal = new WasmLocal(WasmType.INT32, "array"); WasmLocal arrayLocal = new WasmLocal(WasmType.INT32, "array");
function.add(arrayLocal); function.add(arrayLocal);

View File

@ -16,6 +16,7 @@
package org.teavm.backend.wasm.generate; package org.teavm.backend.wasm.generate;
import java.util.List; import java.util.List;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.WasmHeap; import org.teavm.backend.wasm.WasmHeap;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmModule;
@ -28,25 +29,27 @@ import org.teavm.model.FieldReference;
public class WasmSpecialFunctionGenerator { public class WasmSpecialFunctionGenerator {
private WasmClassGenerator classGenerator; private WasmClassGenerator classGenerator;
private WasmFunctionTypes functionTypes;
private List<WasmInt32Constant> regionSizeExpressions; private List<WasmInt32Constant> regionSizeExpressions;
public WasmSpecialFunctionGenerator(WasmClassGenerator classGenerator, public WasmSpecialFunctionGenerator(WasmClassGenerator classGenerator, WasmFunctionTypes functionTypes,
List<WasmInt32Constant> regionSizeExpressions) { List<WasmInt32Constant> regionSizeExpressions) {
this.classGenerator = classGenerator; this.classGenerator = classGenerator;
this.functionTypes = functionTypes;
this.regionSizeExpressions = regionSizeExpressions; this.regionSizeExpressions = regionSizeExpressions;
} }
public void generateSpecialFunctions(WasmModule module) { public void generateSpecialFunctions(WasmModule module) {
module.add(javaHeapAddress()); module.functions.add(javaHeapAddress());
module.add(availableBytes()); module.functions.add(availableBytes());
module.add(regionsAddress()); module.functions.add(regionsAddress());
module.add(regionSize()); module.functions.add(regionSize());
} }
private WasmFunction javaHeapAddress() { 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.setExportName("teavm_javaHeapAddress");
function.setResult(WasmType.INT32);
int address = classGenerator.getFieldOffset(new FieldReference(WasmHeap.class.getName(), "heapAddress")); int address = classGenerator.getFieldOffset(new FieldReference(WasmHeap.class.getName(), "heapAddress"));
function.getBody().add(new WasmReturn( function.getBody().add(new WasmReturn(
@ -55,9 +58,9 @@ public class WasmSpecialFunctionGenerator {
} }
private WasmFunction availableBytes() { 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.setExportName("teavm_availableBytes");
function.setResult(WasmType.INT32);
int address = classGenerator.getFieldOffset(new FieldReference(WasmHeap.class.getName(), "heapSize")); int address = classGenerator.getFieldOffset(new FieldReference(WasmHeap.class.getName(), "heapSize"));
function.getBody().add(new WasmReturn( function.getBody().add(new WasmReturn(
@ -66,9 +69,9 @@ public class WasmSpecialFunctionGenerator {
} }
private WasmFunction regionsAddress() { private WasmFunction regionsAddress() {
WasmFunction function = new WasmFunction("teavm_regionsAddress"); var function = new WasmFunction(functionTypes.of(WasmType.INT32));
function.setExportName("teavm_regionsAddress"); function.setExportName("teavm_regionsAddress");
function.setResult(WasmType.INT32); function.setName("teavm_regionsAddress");
int address = classGenerator.getFieldOffset(new FieldReference(WasmHeap.class.getName(), "regionsAddress")); int address = classGenerator.getFieldOffset(new FieldReference(WasmHeap.class.getName(), "regionsAddress"));
function.getBody().add(new WasmReturn( function.getBody().add(new WasmReturn(
@ -77,9 +80,9 @@ public class WasmSpecialFunctionGenerator {
} }
private WasmFunction regionSize() { private WasmFunction regionSize() {
WasmFunction function = new WasmFunction("teavm_regionSize"); var function = new WasmFunction(functionTypes.of(WasmType.INT32));
function.setExportName("teavm_regionSize"); function.setExportName("teavm_regionSize");
function.setResult(WasmType.INT32); function.setName("teavm_regionSize");
WasmInt32Constant constant = new WasmInt32Constant(0); WasmInt32Constant constant = new WasmInt32Constant(0);
regionSizeExpressions.add(constant); regionSizeExpressions.add(constant);

View File

@ -133,7 +133,7 @@ public class ArrayGenerator implements WasmMethodGenerator {
new WasmGetLocal(arrayVar)); new WasmGetLocal(arrayVar));
int baseAddr = BinaryWriter.align(base, 1 << shift[i]); 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()) { switch (primitiveTypes[i].getKind()) {
case BOOLEAN: case BOOLEAN:

View File

@ -15,7 +15,7 @@
*/ */
package org.teavm.backend.wasm.generators; 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.binary.BinaryWriter;
import org.teavm.backend.wasm.generate.WasmClassGenerator; import org.teavm.backend.wasm.generate.WasmClassGenerator;
import org.teavm.backend.wasm.generate.WasmStringPool; import org.teavm.backend.wasm.generate.WasmStringPool;
@ -29,7 +29,7 @@ public interface WasmMethodGeneratorContext {
Diagnostics getDiagnostics(); Diagnostics getDiagnostics();
NameProvider getNames(); WasmFunctionRepository getFunctions();
ClassReaderSource getClassSource(); ClassReaderSource getClassSource();

View File

@ -20,7 +20,7 @@ import org.teavm.ast.ConstantExpr;
import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.WasmRuntime;
import org.teavm.backend.wasm.generate.WasmClassGenerator; 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.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmConversion;
import org.teavm.backend.wasm.model.expression.WasmDrop; import org.teavm.backend.wasm.model.expression.WasmDrop;
@ -64,21 +64,21 @@ public class AddressIntrinsic implements WasmIntrinsic {
return manager.generate(invocation.getArguments().get(0)); return manager.generate(invocation.getArguments().get(0));
case "toLong": { case "toLong": {
WasmExpression value = manager.generate(invocation.getArguments().get(0)); 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 "fromInt":
case "ofObject": case "ofObject":
return manager.generate(invocation.getArguments().get(0)); return manager.generate(invocation.getArguments().get(0));
case "fromLong": { case "fromLong": {
WasmExpression value = manager.generate(invocation.getArguments().get(0)); 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": { case "add": {
WasmExpression base = manager.generate(invocation.getArguments().get(0)); WasmExpression base = manager.generate(invocation.getArguments().get(0));
if (invocation.getMethod().parameterCount() == 1) { if (invocation.getMethod().parameterCount() == 1) {
WasmExpression offset = manager.generate(invocation.getArguments().get(1)); WasmExpression offset = manager.generate(invocation.getArguments().get(1));
if (invocation.getMethod().parameterType(0) == ValueType.LONG) { 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); return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, base, offset);
} else { } else {
@ -155,7 +155,7 @@ public class AddressIntrinsic implements WasmIntrinsic {
case "align": { case "align": {
MethodReference delegate = new MethodReference(WasmRuntime.class.getName(), MethodReference delegate = new MethodReference(WasmRuntime.class.getName(),
invocation.getMethod().getDescriptor()); 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() call.getArguments().addAll(invocation.getArguments().stream()
.map(arg -> manager.generate(arg)) .map(arg -> manager.generate(arg))
.collect(Collectors.toList())); .collect(Collectors.toList()));
@ -180,7 +180,7 @@ public class AddressIntrinsic implements WasmIntrinsic {
manager.generate(invocation.getArguments().get(0)), manager.generate(invocation.getArguments().get(0)),
manager.generate(invocation.getArguments().get(1)) 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()); result.setLocation(invocation.getLocation());
return result; return result;
} }

View File

@ -48,14 +48,14 @@ public class ConsoleIntrinsic implements WasmIntrinsic {
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
switch (invocation.getMethod().getName()) { switch (invocation.getMethod().getName()) {
case "printString": { case "printString": {
String name = manager.getNames().forMethod(PRINT_STRING); var function = manager.getFunctions().forStaticMethod(PRINT_STRING);
WasmCall call = new WasmCall(name); var call = new WasmCall(function);
call.getArguments().add(manager.generate(invocation.getArguments().get(0))); call.getArguments().add(manager.generate(invocation.getArguments().get(0)));
return call; return call;
} }
case "printInt": { case "printInt": {
String name = manager.getNames().forMethod(PRINT_INT); var function = manager.getFunctions().forStaticMethod(PRINT_INT);
WasmCall call = new WasmCall(name); var call = new WasmCall(function);
call.getArguments().add(manager.generate(invocation.getArguments().get(0))); call.getArguments().add(manager.generate(invocation.getArguments().get(0)));
return call; return call;
} }

View File

@ -17,6 +17,7 @@ package org.teavm.backend.wasm.intrinsics;
import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.model.WasmLocal; 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.WasmType;
import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmBranch; import org.teavm.backend.wasm.model.expression.WasmBranch;
@ -68,13 +69,13 @@ public class DoubleIntrinsic implements WasmIntrinsic {
case "isFinite": case "isFinite":
return testIsFinite(manager.generate(invocation.getArguments().get(0))); return testIsFinite(manager.generate(invocation.getArguments().get(0)));
case "doubleToRawLongBits": { 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))); manager.generate(invocation.getArguments().get(0)));
conversion.setReinterpret(true); conversion.setReinterpret(true);
return conversion; return conversion;
} }
case "longBitsToDouble": { 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))); manager.generate(invocation.getArguments().get(0)));
conversion.setReinterpret(true); conversion.setReinterpret(true);
return conversion; return conversion;
@ -89,7 +90,7 @@ public class DoubleIntrinsic implements WasmIntrinsic {
WasmBlock block = new WasmBlock(false); WasmBlock block = new WasmBlock(false);
block.setType(WasmType.INT32); 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); conversion.setReinterpret(true);
block.getBody().add(new WasmSetLocal(bitsVar, conversion)); block.getBody().add(new WasmSetLocal(bitsVar, conversion));
@ -113,7 +114,7 @@ public class DoubleIntrinsic implements WasmIntrinsic {
} }
private WasmExpression testIsInfinite(WasmExpression expression) { 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); conversion.setReinterpret(true);
var result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.AND, var result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.AND,
@ -123,7 +124,7 @@ public class DoubleIntrinsic implements WasmIntrinsic {
} }
private WasmExpression testIsFinite(WasmExpression expression) { 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); conversion.setReinterpret(true);
var result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.AND, var result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.AND,

View File

@ -17,6 +17,7 @@ package org.teavm.backend.wasm.intrinsics;
import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.model.WasmLocal; 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.WasmType;
import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmBranch; import org.teavm.backend.wasm.model.expression.WasmBranch;
@ -67,13 +68,13 @@ public class FloatIntrinsic implements WasmIntrinsic {
case "isFinite": case "isFinite":
return testIsFinite(manager.generate(invocation.getArguments().get(0))); return testIsFinite(manager.generate(invocation.getArguments().get(0)));
case "floatToRawIntBits": { 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))); manager.generate(invocation.getArguments().get(0)));
conversion.setReinterpret(true); conversion.setReinterpret(true);
return conversion; return conversion;
} }
case "intBitsToFloat": { 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))); manager.generate(invocation.getArguments().get(0)));
conversion.setReinterpret(true); conversion.setReinterpret(true);
return conversion; return conversion;
@ -88,7 +89,7 @@ public class FloatIntrinsic implements WasmIntrinsic {
WasmBlock block = new WasmBlock(false); WasmBlock block = new WasmBlock(false);
block.setType(WasmType.INT32); 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); conversion.setReinterpret(true);
block.getBody().add(new WasmSetLocal(bitsVar, conversion)); block.getBody().add(new WasmSetLocal(bitsVar, conversion));
@ -112,7 +113,7 @@ public class FloatIntrinsic implements WasmIntrinsic {
} }
private WasmExpression testIsInfinite(WasmExpression expression) { 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); conversion.setReinterpret(true);
var result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, var result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND,
@ -122,7 +123,7 @@ public class FloatIntrinsic implements WasmIntrinsic {
} }
private WasmExpression testIsFinite(WasmExpression expression) { 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); conversion.setReinterpret(true);
var result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, var result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND,

View File

@ -18,11 +18,11 @@ package org.teavm.backend.wasm.intrinsics;
import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.generate.WasmClassGenerator; import org.teavm.backend.wasm.generate.WasmClassGenerator;
import org.teavm.backend.wasm.generate.WasmGeneratorUtil; 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.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmIndirectCall; import org.teavm.backend.wasm.model.expression.WasmIndirectCall;
import org.teavm.interop.Function; import org.teavm.interop.Function;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
public class FunctionIntrinsic implements WasmIntrinsic { public class FunctionIntrinsic implements WasmIntrinsic {
private WasmClassGenerator classGenerator; private WasmClassGenerator classGenerator;
@ -42,15 +42,16 @@ public class FunctionIntrinsic implements WasmIntrinsic {
@Override @Override
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
WasmExpression selector = manager.generate(invocation.getArguments().get(0)); var parameterTypes = new WasmType[invocation.getMethod().parameterCount()];
WasmIndirectCall call = new WasmIndirectCall(selector); 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) { for (int i = 1; i < invocation.getArguments().size(); ++i) {
call.getArguments().add(manager.generate(invocation.getArguments().get(i))); call.getArguments().add(manager.generate(invocation.getArguments().get(i)));
} }

View File

@ -20,7 +20,7 @@ import java.util.List;
import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.WasmHeap; import org.teavm.backend.wasm.WasmHeap;
import org.teavm.backend.wasm.WasmRuntime; 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.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmConversion;
@ -100,8 +100,8 @@ public class GCIntrinsic implements WasmIntrinsic {
return intToLong(getStaticField(manager, "maxHeapSize")); return intToLong(getStaticField(manager, "maxHeapSize"));
case "resizeHeap": { case "resizeHeap": {
WasmExpression amount = manager.generate(invocation.getArguments().get(0)); WasmExpression amount = manager.generate(invocation.getArguments().get(0));
amount = new WasmConversion(WasmType.INT64, WasmType.INT32, false, amount); amount = new WasmConversion(WasmNumType.INT64, WasmNumType.INT32, false, amount);
return new WasmCall(manager.getNames().forMethod(RESIZE_HEAP), amount); return new WasmCall(manager.getFunctions().forStaticMethod(RESIZE_HEAP), amount);
} }
case "regionSize": { case "regionSize": {
WasmInt32Constant result = new WasmInt32Constant(0); WasmInt32Constant result = new WasmInt32Constant(0);
@ -112,7 +112,7 @@ public class GCIntrinsic implements WasmIntrinsic {
return intToLong(getStaticField(manager, "heapSize")); return intToLong(getStaticField(manager, "heapSize"));
case "outOfMemory": { case "outOfMemory": {
WasmBlock block = new WasmBlock(false); 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(call);
block.getBody().add(new WasmUnreachable()); block.getBody().add(new WasmUnreachable());
return block; return block;
@ -149,6 +149,6 @@ public class GCIntrinsic implements WasmIntrinsic {
} }
private static WasmExpression intToLong(WasmExpression expression) { private static WasmExpression intToLong(WasmExpression expression) {
return new WasmConversion(WasmType.INT32, WasmType.INT64, false, expression); return new WasmConversion(WasmNumType.INT32, WasmNumType.INT64, false, expression);
} }
} }

View File

@ -56,7 +56,7 @@ public class IntegerIntrinsic implements WasmIntrinsic {
manager.generate(invocation.getArguments().get(0)), manager.generate(invocation.getArguments().get(0)),
manager.generate(invocation.getArguments().get(1))); manager.generate(invocation.getArguments().get(1)));
case "compareUnsigned": 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(0)),
manager.generate(invocation.getArguments().get(1))); manager.generate(invocation.getArguments().get(1)));
default: default:

View File

@ -56,7 +56,7 @@ public class LongIntrinsic implements WasmIntrinsic {
manager.generate(invocation.getArguments().get(0)), manager.generate(invocation.getArguments().get(0)),
manager.generate(invocation.getArguments().get(1))); manager.generate(invocation.getArguments().get(1)));
case "compareUnsigned": 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(0)),
manager.generate(invocation.getArguments().get(1))); manager.generate(invocation.getArguments().get(1)));
default: default:

View File

@ -49,7 +49,7 @@ public class ShadowStackIntrinsic implements WasmIntrinsic {
MethodReference method = new MethodReference(WasmRuntime.class.getName(), MethodReference method = new MethodReference(WasmRuntime.class.getName(),
invocation.getMethod().getDescriptor()); invocation.getMethod().getDescriptor());
expr.setMethod(method); expr.setMethod(method);
expr.setType(InvocationType.SPECIAL); expr.setType(InvocationType.STATIC);
expr.getArguments().addAll(invocation.getArguments()); expr.getArguments().addAll(invocation.getArguments());
return manager.generate(expr); return manager.generate(expr);
} }

View File

@ -16,9 +16,11 @@
package org.teavm.backend.wasm.intrinsics; package org.teavm.backend.wasm.intrinsics;
import org.teavm.ast.Expr; 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.binary.BinaryWriter;
import org.teavm.backend.wasm.generate.WasmStringPool; 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.WasmLocal;
import org.teavm.backend.wasm.model.WasmTag; import org.teavm.backend.wasm.model.WasmTag;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
@ -38,7 +40,9 @@ public interface WasmIntrinsicManager {
Diagnostics getDiagnostics(); Diagnostics getDiagnostics();
NameProvider getNames(); WasmFunctionRepository getFunctions();
WasmFunctionTypes getFunctionTypes();
WasmLocal getTemporary(WasmType type); WasmLocal getTemporary(WasmType type);
@ -46,7 +50,7 @@ public interface WasmIntrinsicManager {
int getClassPointer(ValueType type); int getClassPointer(ValueType type);
int getFunctionPointer(String name); int getFunctionPointer(WasmFunction function);
void releaseTemporary(WasmLocal local); void releaseTemporary(WasmLocal local);

View File

@ -72,8 +72,8 @@ public class WasmRuntimeIntrinsic implements WasmIntrinsic {
return comparison(WasmIntBinaryOperation.GT_SIGNED, WasmFloatBinaryOperation.MAX, return comparison(WasmIntBinaryOperation.GT_SIGNED, WasmFloatBinaryOperation.MAX,
invocation, manager); invocation, manager);
case "callFunctionFromTable": { case "callFunctionFromTable": {
var call = new WasmIndirectCall(manager.generate(invocation.getArguments().get(0))); var functionType = manager.getFunctionTypes().of(null, WasmType.INT32);
call.getParameterTypes().add(WasmType.INT32); var call = new WasmIndirectCall(manager.generate(invocation.getArguments().get(0)), functionType);
call.getArguments().add(manager.generate(invocation.getArguments().get(1))); call.getArguments().add(manager.generate(invocation.getArguments().get(1)));
return call; return call;
} }
@ -84,12 +84,12 @@ public class WasmRuntimeIntrinsic implements WasmIntrinsic {
private static WasmExpression comparison(WasmIntBinaryOperation intOp, WasmFloatBinaryOperation floatOp, private static WasmExpression comparison(WasmIntBinaryOperation intOp, WasmFloatBinaryOperation floatOp,
InvocationExpr invocation, WasmIntrinsicManager manager) { 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 first = manager.generate(invocation.getArguments().get(0));
WasmExpression second = manager.generate(invocation.getArguments().get(1)); WasmExpression second = manager.generate(invocation.getArguments().get(1));
switch (type) { switch (type.number) {
case INT32: case INT32:
return new WasmIntBinary(WasmIntType.INT32, intOp, first, second); return new WasmIntBinary(WasmIntType.INT32, intOp, first, second);
case INT64: case INT64:

View File

@ -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<WasmStorageType> elementTypeSupplier;
public WasmArray(WasmStorageType elementType) {
this.elementType = Objects.requireNonNull(elementType);
}
public WasmArray(Supplier<WasmStorageType> 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);
}
}

View File

@ -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<T extends WasmEntity> implements Iterable<T> {
private List<T> items = new ArrayList<>();
private List<T> 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<T> 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<T> iterator() {
return readonlyItems.iterator();
}
public Stream<T> stream() {
return readonlyItems.stream();
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -22,32 +22,29 @@ import java.util.Objects;
import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
public class WasmFunction { public class WasmFunction extends WasmEntity {
WasmModule module;
private String name; private String name;
private String exportName; private String exportName;
private String importName; private String importName;
private String importModule; private String importModule;
private List<WasmType> parameters = new ArrayList<>(); private WasmFunctionType type;
private WasmType result;
private List<WasmLocal> localVariables = new ArrayList<>(); private List<WasmLocal> localVariables = new ArrayList<>();
private List<WasmLocal> readonlyLocalVariables = Collections.unmodifiableList(localVariables); private List<WasmLocal> readonlyLocalVariables = Collections.unmodifiableList(localVariables);
private List<WasmExpression> body = new ArrayList<>(); private List<WasmExpression> body = new ArrayList<>();
private MethodReference javaMethod; private MethodReference javaMethod;
public WasmFunction(String name) { public WasmFunction(WasmFunctionType type) {
Objects.requireNonNull(name); this.type = Objects.requireNonNull(type);
this.name = name;
}
public WasmModule getModule() {
return module;
} }
public String getName() { public String getName() {
return name; return name;
} }
public void setName(String name) {
this.name = name;
}
public String getExportName() { public String getExportName() {
return exportName; return exportName;
} }
@ -62,6 +59,7 @@ public class WasmFunction {
public void setImportName(String importName) { public void setImportName(String importName) {
this.importName = importName; this.importName = importName;
collection.invalidateIndexes();
} }
public String getImportModule() { public String getImportModule() {
@ -72,16 +70,17 @@ public class WasmFunction {
this.importModule = importModule; this.importModule = importModule;
} }
public WasmType getResult() { @Override
return result; boolean isImported() {
return importName != null;
} }
public void setResult(WasmType result) { public WasmFunctionType getType() {
this.result = result; return type;
} }
public List<WasmType> getParameters() { public void setType(WasmFunctionType type) {
return parameters; this.type = Objects.requireNonNull(type);
} }
public List<WasmLocal> getLocalVariables() { public List<WasmLocal> getLocalVariables() {

View File

@ -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<? extends WasmType> parameterTypes;
private WasmType returnType;
private Supplier<List<? extends WasmType>> parameterTypesSupplier;
private Supplier<WasmType> returnTypeSupplier;
public WasmFunctionType(WasmType returnType, List<? extends WasmType> parameterTypes) {
this.returnType = returnType;
this.parameterTypes = parameterTypes;
}
public WasmFunctionType(Supplier<WasmType> returnTypeSupplier,
Supplier<List<? extends WasmType>> parameterTypesSupplier) {
this.returnTypeSupplier = returnTypeSupplier;
this.parameterTypesSupplier = parameterTypesSupplier;
}
public List<? extends WasmType> 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);
}
}

View File

@ -25,37 +25,14 @@ public class WasmModule {
private int minMemorySize; private int minMemorySize;
private int maxMemorySize; private int maxMemorySize;
private List<WasmMemorySegment> segments = new ArrayList<>(); private List<WasmMemorySegment> segments = new ArrayList<>();
private Map<String, WasmFunction> functions = new LinkedHashMap<>();
private Map<String, WasmFunction> readonlyFunctions = Collections.unmodifiableMap(functions);
private List<WasmFunction> functionTable = new ArrayList<>(); private List<WasmFunction> functionTable = new ArrayList<>();
private WasmFunction startFunction; private WasmFunction startFunction;
private Map<String, WasmCustomSection> customSections = new LinkedHashMap<>(); private Map<String, WasmCustomSection> customSections = new LinkedHashMap<>();
private Map<String, WasmCustomSection> readonlyCustomSections = Collections.unmodifiableMap(customSections); private Map<String, WasmCustomSection> readonlyCustomSections = Collections.unmodifiableMap(customSections);
private List<WasmTag> tags = new ArrayList<>();
private List<? extends WasmTag> readonlyTags = Collections.unmodifiableList(tags);
public void add(WasmFunction function) { public final WasmCollection<WasmFunction> functions = new WasmCollection<>();
if (functions.containsKey(function.getName())) { public final WasmCollection<WasmCompositeType> types = new WasmCollection<>();
throw new IllegalArgumentException("Function " + function.getName() + " already defined in this module"); public final WasmCollection<WasmTag> tags = new WasmCollection<>();
}
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<String, WasmFunction> getFunctions() {
return readonlyFunctions;
}
public void add(WasmCustomSection customSection) { public void add(WasmCustomSection customSection) {
if (customSections.containsKey(customSection.getName())) { if (customSections.containsKey(customSection.getName())) {
@ -112,17 +89,4 @@ public class WasmModule {
public void setStartFunction(WasmFunction startFunction) { public void setStartFunction(WasmFunction startFunction) {
this.startFunction = 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<? extends WasmTag> getTags() {
return tags;
}
} }

View File

@ -0,0 +1,23 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.model;
public enum WasmNumType {
INT32,
INT64,
FLOAT32,
FLOAT64
}

View File

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

View File

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

View File

@ -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<? extends WasmStorageType> fields;
private Supplier<List<? extends WasmStorageType>> fieldsSupplier;
public WasmStructure(List<? extends WasmStorageType> fields) {
this.fields = List.copyOf(fields);
}
public WasmStructure(Supplier<List<? extends WasmStorageType>> fieldsSupplier) {
this.fieldsSupplier = fieldsSupplier;
}
public List<? extends WasmStorageType> getFields() {
if (fields == null) {
fields = List.copyOf(fieldsSupplier.get());
fieldsSupplier = null;
}
return fields;
}
@Override
public void acceptVisitor(WasmCompositeTypeVisitor visitor) {
visitor.visit(this);
}
}

View File

@ -15,16 +15,17 @@
*/ */
package org.teavm.backend.wasm.model; package org.teavm.backend.wasm.model;
import java.util.ArrayList; public class WasmTag extends WasmEntity {
import java.util.List; private WasmFunctionType type;
public class WasmTag {
private List<WasmType> values = new ArrayList<>();
WasmModule module; WasmModule module;
int index; int index;
public List<WasmType> getValues() { public WasmTag(WasmFunctionType type) {
return values; this.type = type;
}
public WasmFunctionType getType() {
return type;
} }
public int getIndex() { public int getIndex() {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016 Alexey Andreev. * Copyright 2024 Alexey Andreev.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,9 +15,52 @@
*/ */
package org.teavm.backend.wasm.model; package org.teavm.backend.wasm.model;
public enum WasmType { public abstract class WasmType {
INT32, public static final WasmType.Number INT32 = new Number(WasmNumType.INT32);
INT64, public static final WasmType.Number INT64 = new Number(WasmNumType.INT64);
FLOAT32, public static final WasmType.Number FLOAT32 = new Number(WasmNumType.FLOAT32);
FLOAT64 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;
}
}
} }

View File

@ -19,48 +19,33 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import org.teavm.backend.wasm.model.WasmFunction;
public class WasmCall extends WasmExpression { public class WasmCall extends WasmExpression {
private String functionName; private WasmFunction function;
private boolean imported;
private List<WasmExpression> arguments = new ArrayList<>(); private List<WasmExpression> arguments = new ArrayList<>();
public WasmCall(String functionName, boolean imported) { public WasmCall(WasmFunction function) {
Objects.requireNonNull(functionName); this.function = Objects.requireNonNull(function);
this.functionName = functionName;
this.imported = imported;
} }
public WasmCall(String functionName) { public WasmCall(WasmFunction functionName, WasmExpression... arguments) {
this(functionName, false);
}
public WasmCall(String functionName, WasmExpression... arguments) {
this(functionName); this(functionName);
getArguments().addAll(Arrays.asList(arguments)); getArguments().addAll(Arrays.asList(arguments));
} }
public String getFunctionName() { public WasmFunction getFunction() {
return functionName; return function;
} }
public void setFunctionName(String functionName) { public void setFunction(WasmFunction function) {
Objects.requireNonNull(functionName); this.function = Objects.requireNonNull(function);
this.functionName = functionName;
} }
public List<WasmExpression> getArguments() { public List<WasmExpression> getArguments() {
return arguments; return arguments;
} }
public boolean isImported() {
return imported;
}
public void setImported(boolean imported) {
this.imported = imported;
}
@Override @Override
public void acceptVisitor(WasmExpressionVisitor visitor) { public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this); visitor.visit(this);

View File

@ -16,16 +16,16 @@
package org.teavm.backend.wasm.model.expression; package org.teavm.backend.wasm.model.expression;
import java.util.Objects; import java.util.Objects;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmNumType;
public class WasmConversion extends WasmExpression { public class WasmConversion extends WasmExpression {
private WasmType sourceType; private WasmNumType sourceType;
private WasmType targetType; private WasmNumType targetType;
private boolean signed; private boolean signed;
private WasmExpression operand; private WasmExpression operand;
private boolean reinterpret; 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(sourceType);
Objects.requireNonNull(targetType); Objects.requireNonNull(targetType);
Objects.requireNonNull(operand); Objects.requireNonNull(operand);
@ -35,20 +35,20 @@ public class WasmConversion extends WasmExpression {
this.operand = operand; this.operand = operand;
} }
public WasmType getSourceType() { public WasmNumType getSourceType() {
return sourceType; return sourceType;
} }
public void setSourceType(WasmType sourceType) { public void setSourceType(WasmNumType sourceType) {
Objects.requireNonNull(sourceType); Objects.requireNonNull(sourceType);
this.sourceType = sourceType; this.sourceType = sourceType;
} }
public WasmType getTargetType() { public WasmNumType getTargetType() {
return targetType; return targetType;
} }
public void setTargetType(WasmType targetType) { public void setTargetType(WasmNumType targetType) {
Objects.requireNonNull(targetType); Objects.requireNonNull(targetType);
this.targetType = targetType; this.targetType = targetType;
} }

View File

@ -81,6 +81,10 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor {
public void visit(WasmFloat64Constant expression) { public void visit(WasmFloat64Constant expression) {
} }
@Override
public void visit(WasmNullConstant expression) {
}
@Override @Override
public void visit(WasmGetLocal expression) { public void visit(WasmGetLocal expression) {
} }

View File

@ -42,17 +42,23 @@ public abstract class WasmExpression {
if (type == null) { if (type == null) {
return null; return null;
} }
switch (type) { if (type instanceof WasmType.Number) {
case INT32: switch (((WasmType.Number) type).number) {
return new WasmInt32Constant(0); case INT32:
case INT64: return new WasmInt32Constant(0);
return new WasmInt64Constant(0); case INT64:
case FLOAT32: return new WasmInt64Constant(0);
return new WasmFloat32Constant(0); case FLOAT32:
case FLOAT64: return new WasmFloat32Constant(0);
return new WasmFloat64Constant(0); case FLOAT64:
default: return new WasmFloat64Constant(0);
throw new IllegalArgumentException(); default:
throw new IllegalArgumentException();
}
} else if (type instanceof WasmType.Reference) {
return new WasmNullConstant(((WasmType.Reference) type).composite);
} else {
throw new IllegalArgumentException();
} }
} }
} }

View File

@ -38,6 +38,8 @@ public interface WasmExpressionVisitor {
void visit(WasmFloat64Constant expression); void visit(WasmFloat64Constant expression);
void visit(WasmNullConstant expression);
void visit(WasmGetLocal expression); void visit(WasmGetLocal expression);
void visit(WasmSetLocal expression); void visit(WasmSetLocal expression);

View File

@ -18,17 +18,16 @@ package org.teavm.backend.wasm.model.expression;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmFunctionType;
public class WasmIndirectCall extends WasmExpression { public class WasmIndirectCall extends WasmExpression {
private List<WasmType> parameterTypes = new ArrayList<>(); private WasmFunctionType type;
private WasmType returnType;
private WasmExpression selector; private WasmExpression selector;
private List<WasmExpression> arguments = new ArrayList<>(); private List<WasmExpression> arguments = new ArrayList<>();
public WasmIndirectCall(WasmExpression selector) { public WasmIndirectCall(WasmExpression selector, WasmFunctionType type) {
Objects.requireNonNull(selector); this.selector = Objects.requireNonNull(selector);
this.selector = selector; this.type = Objects.requireNonNull(type);
} }
public WasmExpression getSelector() { public WasmExpression getSelector() {
@ -36,26 +35,21 @@ public class WasmIndirectCall extends WasmExpression {
} }
public void setSelector(WasmExpression selector) { public void setSelector(WasmExpression selector) {
Objects.requireNonNull(selector); this.selector = Objects.requireNonNull(selector);
this.selector = selector;
}
public List<WasmType> getParameterTypes() {
return parameterTypes;
}
public WasmType getReturnType() {
return returnType;
}
public void setReturnType(WasmType returnType) {
this.returnType = returnType;
} }
public List<WasmExpression> getArguments() { public List<WasmExpression> getArguments() {
return arguments; return arguments;
} }
public WasmFunctionType getType() {
return type;
}
public void setType(WasmFunctionType type) {
this.type = Objects.requireNonNull(type);
}
@Override @Override
public void acceptVisitor(WasmExpressionVisitor visitor) { public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this); visitor.visit(this);

View File

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

View File

@ -105,6 +105,10 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor {
public void visit(WasmFloat64Constant expression) { public void visit(WasmFloat64Constant expression) {
} }
@Override
public void visit(WasmNullConstant expression) {
}
@Override @Override
public void visit(WasmGetLocal expression) { public void visit(WasmGetLocal expression) {
} }

View File

@ -35,7 +35,7 @@ public class UnusedFunctionElimination {
} }
public void apply() { public void apply() {
List<WasmFunction> exported = module.getFunctions().values().stream() List<WasmFunction> exported = module.functions.stream()
.filter(function -> function.getExportName() != null) .filter(function -> function.getExportName() != null)
.collect(Collectors.toList()); .collect(Collectors.toList());
for (WasmFunction function : exported) { for (WasmFunction function : exported) {
@ -48,11 +48,7 @@ public class UnusedFunctionElimination {
use(module.getStartFunction()); use(module.getStartFunction());
} }
for (WasmFunction function : module.getFunctions().values().toArray(new WasmFunction[0])) { module.functions.removeIf(function -> !usedFunctions.contains(function));
if (!usedFunctions.contains(function)) {
module.remove(function);
}
}
} }
private void use(WasmFunction function) { private void use(WasmFunction function) {
@ -68,7 +64,7 @@ public class UnusedFunctionElimination {
@Override @Override
public void visit(WasmCall expression) { public void visit(WasmCall expression) {
super.visit(expression); super.visit(expression);
WasmFunction function = module.getFunctions().get(expression.getFunctionName()); var function = expression.getFunction();
if (function != null) { if (function != null) {
use(function); use(function);
} }

View File

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

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.backend.wasm.parser; 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.WasmType;
import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmFloatType; import org.teavm.backend.wasm.model.expression.WasmFloatType;
@ -97,7 +98,7 @@ public interface CodeListener {
default void storeFloat64(int align, int offset) { 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() { default void memoryGrow() {

View File

@ -17,6 +17,7 @@ package org.teavm.backend.wasm.parser;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.teavm.backend.wasm.model.WasmNumType;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmFloatType; import org.teavm.backend.wasm.model.expression.WasmFloatType;
@ -544,76 +545,76 @@ public class CodeSectionParser {
break; break;
case 0xA7: case 0xA7:
codeListener.convert(WasmType.INT64, WasmType.INT32, false, false); codeListener.convert(WasmNumType.INT64, WasmNumType.INT32, false, false);
break; break;
case 0xA8: case 0xA8:
codeListener.convert(WasmType.FLOAT32, WasmType.INT32, false, false); codeListener.convert(WasmNumType.FLOAT32, WasmNumType.INT32, false, false);
break; break;
case 0xA9: case 0xA9:
codeListener.convert(WasmType.FLOAT32, WasmType.INT32, true, false); codeListener.convert(WasmNumType.FLOAT32, WasmNumType.INT32, true, false);
break; break;
case 0xAA: case 0xAA:
codeListener.convert(WasmType.FLOAT64, WasmType.INT32, false, false); codeListener.convert(WasmNumType.FLOAT64, WasmNumType.INT32, false, false);
break; break;
case 0xAB: case 0xAB:
codeListener.convert(WasmType.FLOAT64, WasmType.INT32, true, false); codeListener.convert(WasmNumType.FLOAT64, WasmNumType.INT32, true, false);
break; break;
case 0xAC: case 0xAC:
codeListener.convert(WasmType.INT32, WasmType.INT64, false, false); codeListener.convert(WasmNumType.INT32, WasmNumType.INT64, false, false);
break; break;
case 0xAD: case 0xAD:
codeListener.convert(WasmType.INT32, WasmType.INT64, true, false); codeListener.convert(WasmNumType.INT32, WasmNumType.INT64, true, false);
break; break;
case 0xAE: case 0xAE:
codeListener.convert(WasmType.FLOAT32, WasmType.INT64, false, false); codeListener.convert(WasmNumType.FLOAT32, WasmNumType.INT64, false, false);
break; break;
case 0xAF: case 0xAF:
codeListener.convert(WasmType.FLOAT32, WasmType.INT64, true, false); codeListener.convert(WasmNumType.FLOAT32, WasmNumType.INT64, true, false);
break; break;
case 0xB0: case 0xB0:
codeListener.convert(WasmType.FLOAT64, WasmType.INT64, false, false); codeListener.convert(WasmNumType.FLOAT64, WasmNumType.INT64, false, false);
break; break;
case 0xB1: case 0xB1:
codeListener.convert(WasmType.FLOAT64, WasmType.INT64, true, false); codeListener.convert(WasmNumType.FLOAT64, WasmNumType.INT64, true, false);
break; break;
case 0xB2: case 0xB2:
codeListener.convert(WasmType.INT32, WasmType.FLOAT32, false, false); codeListener.convert(WasmNumType.INT32, WasmNumType.FLOAT32, false, false);
break; break;
case 0xB3: case 0xB3:
codeListener.convert(WasmType.INT32, WasmType.FLOAT32, true, false); codeListener.convert(WasmNumType.INT32, WasmNumType.FLOAT32, true, false);
break; break;
case 0xB4: case 0xB4:
codeListener.convert(WasmType.INT64, WasmType.FLOAT32, false, false); codeListener.convert(WasmNumType.INT64, WasmNumType.FLOAT32, false, false);
break; break;
case 0xB5: case 0xB5:
codeListener.convert(WasmType.INT64, WasmType.FLOAT32, true, false); codeListener.convert(WasmNumType.INT64, WasmNumType.FLOAT32, true, false);
break; break;
case 0xB6: case 0xB6:
codeListener.convert(WasmType.FLOAT64, WasmType.FLOAT32, true, false); codeListener.convert(WasmNumType.FLOAT64, WasmNumType.FLOAT32, true, false);
break; break;
case 0xB7: case 0xB7:
codeListener.convert(WasmType.INT32, WasmType.FLOAT64, false, false); codeListener.convert(WasmNumType.INT32, WasmNumType.FLOAT64, false, false);
break; break;
case 0xB8: case 0xB8:
codeListener.convert(WasmType.INT32, WasmType.FLOAT64, true, false); codeListener.convert(WasmNumType.INT32, WasmNumType.FLOAT64, true, false);
break; break;
case 0xB9: case 0xB9:
codeListener.convert(WasmType.INT64, WasmType.FLOAT64, false, false); codeListener.convert(WasmNumType.INT64, WasmNumType.FLOAT64, false, false);
break; break;
case 0xBA: case 0xBA:
codeListener.convert(WasmType.INT64, WasmType.FLOAT64, true, false); codeListener.convert(WasmNumType.INT64, WasmNumType.FLOAT64, true, false);
break; break;
case 0xBC: case 0xBC:
codeListener.convert(WasmType.FLOAT32, WasmType.INT32, false, true); codeListener.convert(WasmNumType.FLOAT32, WasmNumType.INT32, false, true);
break; break;
case 0xBD: case 0xBD:
codeListener.convert(WasmType.FLOAT64, WasmType.INT64, false, true); codeListener.convert(WasmNumType.FLOAT64, WasmNumType.INT64, false, true);
break; break;
case 0xBE: case 0xBE:
codeListener.convert(WasmType.INT32, WasmType.FLOAT32, false, true); codeListener.convert(WasmNumType.INT32, WasmNumType.FLOAT32, false, true);
break; break;
case 0xBF: case 0xBF:
codeListener.convert(WasmType.INT64, WasmType.FLOAT64, false, true); codeListener.convert(WasmNumType.INT64, WasmNumType.FLOAT64, false, true);
break; break;
case 0xFC: case 0xFC:

View File

@ -18,9 +18,7 @@ package org.teavm.backend.wasm.render;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.teavm.backend.wasm.debug.DebugLines; 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.WasmMemorySegment;
import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmExpression;
public class WasmBinaryRenderer { public class WasmBinaryRenderer {
private static final int SECTION_UNKNOWN = 0; private static final int SECTION_UNKNOWN = 0;
@ -53,9 +50,6 @@ public class WasmBinaryRenderer {
private WasmBinaryWriter output; private WasmBinaryWriter output;
private WasmBinaryVersion version; private WasmBinaryVersion version;
private List<WasmSignature> signatures = new ArrayList<>();
private Map<WasmSignature, Integer> signatureIndexes = new HashMap<>();
private Map<String, Integer> functionIndexes = new HashMap<>();
private boolean obfuscated; private boolean obfuscated;
private DwarfGenerator dwarfGenerator; private DwarfGenerator dwarfGenerator;
private DwarfClassGenerator dwarfClassGen; private DwarfClassGenerator dwarfClassGen;
@ -88,7 +82,7 @@ public class WasmBinaryRenderer {
break; break;
} }
renderSignatures(module); renderTypes(module);
renderImports(module); renderImports(module);
renderFunctions(module); renderFunctions(module);
renderTable(module); renderTable(module);
@ -105,33 +99,13 @@ public class WasmBinaryRenderer {
renderCustomSections(module, customSectionSupplier); renderCustomSections(module, customSectionSupplier);
} }
private void renderSignatures(WasmModule module) { private void renderTypes(WasmModule module) {
WasmBinaryWriter section = new WasmBinaryWriter(); var section = new WasmBinaryWriter();
WasmSignatureCollector signatureCollector = new WasmSignatureCollector(this::registerSignature);
for (WasmFunction function : module.getFunctions().values()) { var typeRenderer = new WasmCompositeTypeBinaryRenderer(module, section);
registerSignature(WasmSignature.fromFunction(function)); section.writeLEB(module.types.size());
for (WasmExpression part : function.getBody()) { for (var type : module.types) {
part.acceptVisitor(signatureCollector); type.acceptVisitor(typeRenderer);
}
}
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);
}
} }
writeSection(SECTION_TYPE, "type", section.getData()); writeSection(SECTION_TYPE, "type", section.getData());
@ -139,11 +113,10 @@ public class WasmBinaryRenderer {
private void renderImports(WasmModule module) { private void renderImports(WasmModule module) {
List<WasmFunction> functions = new ArrayList<>(); List<WasmFunction> functions = new ArrayList<>();
for (WasmFunction function : module.getFunctions().values()) { for (var function : module.functions) {
if (function.getImportName() == null) { if (function.getImportName() == null) {
continue; continue;
} }
functionIndexes.put(function.getName(), functions.size());
functions.add(function); functions.add(function);
} }
if (functions.isEmpty()) { if (functions.isEmpty()) {
@ -154,9 +127,7 @@ public class WasmBinaryRenderer {
section.writeLEB(functions.size()); section.writeLEB(functions.size());
for (WasmFunction function : functions) { for (WasmFunction function : functions) {
WasmSignature signature = WasmSignature.fromFunction(function); int signatureIndex = module.types.indexOf(function.getType());
int signatureIndex = signatureIndexes.get(signature);
String moduleName = function.getImportModule(); String moduleName = function.getImportModule();
if (moduleName == null) { if (moduleName == null) {
moduleName = ""; moduleName = "";
@ -175,17 +146,13 @@ public class WasmBinaryRenderer {
private void renderFunctions(WasmModule module) { private void renderFunctions(WasmModule module) {
WasmBinaryWriter section = new WasmBinaryWriter(); WasmBinaryWriter section = new WasmBinaryWriter();
List<WasmFunction> functions = module.getFunctions().values().stream() List<WasmFunction> functions = module.functions.stream()
.filter(function -> function.getImportName() == null) .filter(function -> function.getImportName() == null)
.collect(Collectors.toList()); .collect(Collectors.toList());
for (WasmFunction function : functions) {
functionIndexes.put(function.getName(), functionIndexes.size());
}
section.writeLEB(functions.size()); section.writeLEB(functions.size());
for (WasmFunction function : functions) { for (var function : functions) {
WasmSignature signature = WasmSignature.fromFunction(function); section.writeLEB(module.types.indexOf(function.getType()));
section.writeLEB(signatureIndexes.get(signature));
} }
writeSection(SECTION_FUNCTION, "function", section.getData()); writeSection(SECTION_FUNCTION, "function", section.getData());
@ -201,7 +168,7 @@ public class WasmBinaryRenderer {
section.writeByte(1); section.writeByte(1);
section.writeByte(0x70); section.writeByte(0x70);
section.writeByte(0); section.writeByte(0);
section.writeLEB(functionIndexes.size()); section.writeLEB(module.functions.size());
writeSection(SECTION_TABLE, "table", section.getData()); writeSection(SECTION_TABLE, "table", section.getData());
} }
@ -223,13 +190,13 @@ public class WasmBinaryRenderer {
WasmBinaryWriter section = new WasmBinaryWriter(); WasmBinaryWriter section = new WasmBinaryWriter();
List<WasmFunction> functions = module.getFunctions().values().stream() List<WasmFunction> functions = module.functions.stream()
.filter(function -> function.getExportName() != null) .filter(function -> function.getExportName() != null)
.collect(Collectors.toList()); .collect(Collectors.toList());
section.writeLEB(functions.size() + 1); section.writeLEB(functions.size() + 1);
for (WasmFunction function : functions) { for (var function : functions) {
int functionIndex = functionIndexes.get(function.getName()); int functionIndex = module.functions.indexOf(function);
section.writeAsciiString(function.getExportName()); section.writeAsciiString(function.getExportName());
@ -251,7 +218,7 @@ public class WasmBinaryRenderer {
} }
WasmBinaryWriter section = new WasmBinaryWriter(); WasmBinaryWriter section = new WasmBinaryWriter();
section.writeLEB(functionIndexes.get(module.getStartFunction().getName())); section.writeLEB(module.functions.indexOf(module.getStartFunction()));
writeSection(SECTION_START, "start", section.getData()); writeSection(SECTION_START, "start", section.getData());
} }
@ -268,8 +235,8 @@ public class WasmBinaryRenderer {
renderInitializer(section, 0); renderInitializer(section, 0);
section.writeLEB(module.getFunctionTable().size()); section.writeLEB(module.getFunctionTable().size());
for (WasmFunction function : module.getFunctionTable()) { for (var function : module.getFunctionTable()) {
section.writeLEB(functionIndexes.get(function.getName())); section.writeLEB(module.functions.indexOf(function));
} }
writeSection(SECTION_ELEMENT, "element", section.getData()); writeSection(SECTION_ELEMENT, "element", section.getData());
@ -278,13 +245,13 @@ public class WasmBinaryRenderer {
private void renderCode(WasmModule module) { private void renderCode(WasmModule module) {
var section = new WasmBinaryWriter(); var section = new WasmBinaryWriter();
var functions = module.getFunctions().values().stream() var functions = module.functions.stream()
.filter(function -> function.getImportName() == null) .filter(function -> function.getImportName() == null)
.collect(Collectors.toList()); .collect(Collectors.toList());
section.writeLEB(functions.size()); section.writeLEB(functions.size());
for (var function : functions) { for (var function : functions) {
var body = renderFunction(function, section.getPosition() + 4); var body = renderFunction(module, function, section.getPosition() + 4);
var startPos = section.getPosition(); var startPos = section.getPosition();
section.writeLEB4(body.length); section.writeLEB4(body.length);
section.writeBytes(body); section.writeBytes(body);
@ -301,7 +268,7 @@ public class WasmBinaryRenderer {
writeSection(SECTION_CODE, "code", section.getData()); 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 code = new WasmBinaryWriter();
var dwarfSubprogram = dwarfClassGen != null ? dwarfClassGen.getSubprogram(function.getName()) : null; var dwarfSubprogram = dwarfClassGen != null ? dwarfClassGen.getSubprogram(function.getName()) : null;
@ -314,7 +281,7 @@ public class WasmBinaryRenderer {
} }
var localVariables = function.getLocalVariables(); 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()); localVariables = localVariables.subList(parameterCount, localVariables.size());
if (localVariables.isEmpty()) { if (localVariables.isEmpty()) {
code.writeLEB(0); code.writeLEB(0);
@ -335,13 +302,12 @@ public class WasmBinaryRenderer {
code.writeLEB(localEntries.size()); code.writeLEB(localEntries.size());
for (var entry : localEntries) { for (var entry : localEntries) {
code.writeLEB(entry.count); code.writeLEB(entry.count);
code.writeType(entry.type, version); code.writeType(entry.type, module);
} }
} }
var importIndexes = this.functionIndexes; var visitor = new WasmBinaryRenderingVisitor(code, module, dwarfGenerator,
var visitor = new WasmBinaryRenderingVisitor(code, version, functionIndexes, importIndexes, function.getJavaMethod() != null ? debugLines : null, offset);
signatureIndexes, dwarfGenerator, function.getJavaMethod() != null ? debugLines : null, offset);
for (var part : function.getBody()) { for (var part : function.getBody()) {
part.acceptVisitor(visitor); part.acceptVisitor(visitor);
} }
@ -400,15 +366,15 @@ public class WasmBinaryRenderer {
} }
private void renderTags(WasmModule module) { private void renderTags(WasmModule module) {
if (module.getTags().isEmpty()) { if (module.tags.isEmpty()) {
return; return;
} }
var section = new WasmBinaryWriter(); var section = new WasmBinaryWriter();
section.writeLEB(module.getTags().size()); section.writeLEB(module.tags.size());
for (var tag : module.getTags()) { for (var tag : module.tags) {
section.writeByte(0); section.writeByte(0);
section.writeLEB(signatureIndexes.get(WasmSignature.fromTag(tag))); section.writeLEB(module.types.indexOf(tag.getType()));
} }
writeSection(SECTION_TAGS, "tags", section.getData()); writeSection(SECTION_TAGS, "tags", section.getData());
@ -418,12 +384,13 @@ public class WasmBinaryRenderer {
WasmBinaryWriter section = new WasmBinaryWriter(); WasmBinaryWriter section = new WasmBinaryWriter();
WasmBinaryWriter functionsSubsection = new WasmBinaryWriter(); WasmBinaryWriter functionsSubsection = new WasmBinaryWriter();
Collection<WasmFunction> functions = module.getFunctions().values(); var functions = module.functions.stream()
functions = functions.stream().filter(f -> f.getImportName() == null).collect(Collectors.toList()); .filter(f -> f.getName() != null && f.getImportName() == null)
.collect(Collectors.toList());
functionsSubsection.writeLEB(functions.size()); functionsSubsection.writeLEB(functions.size());
for (WasmFunction function : functions) { for (WasmFunction function : functions) {
functionsSubsection.writeLEB(functionIndexes.get(function.getName())); functionsSubsection.writeLEB(module.functions.indexOf(function));
functionsSubsection.writeAsciiString(function.getName()); 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) { private void writeSection(int id, String name, byte[] data) {
var start = output.getPosition(); var start = output.getPosition();
output.writeByte(id); output.writeByte(id);

View File

@ -23,6 +23,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import org.teavm.backend.wasm.debug.DebugLines; import org.teavm.backend.wasm.debug.DebugLines;
import org.teavm.backend.wasm.generate.DwarfGenerator; 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.WasmType;
import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmBranch; 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.WasmLoadInt32;
import org.teavm.backend.wasm.model.expression.WasmLoadInt64; import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
import org.teavm.backend.wasm.model.expression.WasmMemoryGrow; 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.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32; import org.teavm.backend.wasm.model.expression.WasmStoreFloat32;
@ -65,10 +67,7 @@ import org.teavm.model.TextLocation;
class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
private WasmBinaryWriter writer; private WasmBinaryWriter writer;
private WasmBinaryVersion version; private WasmModule module;
private Map<String, Integer> functionIndexes;
private Map<String, Integer> importedIndexes;
private Map<WasmSignature, Integer> signatureIndexes;
private DwarfGenerator dwarfGenerator; private DwarfGenerator dwarfGenerator;
private DebugLines debugLines; private DebugLines debugLines;
private int addressOffset; private int addressOffset;
@ -82,14 +81,10 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
private int positionToEmit; private int positionToEmit;
private List<TextLocation> locationStack = new ArrayList<>(); private List<TextLocation> locationStack = new ArrayList<>();
WasmBinaryRenderingVisitor(WasmBinaryWriter writer, WasmBinaryVersion version, Map<String, Integer> functionIndexes, WasmBinaryRenderingVisitor(WasmBinaryWriter writer, WasmModule module,
Map<String, Integer> importedIndexes, Map<WasmSignature, Integer> signatureIndexes,
DwarfGenerator dwarfGenerator, DebugLines debugLines, int addressOffset) { DwarfGenerator dwarfGenerator, DebugLines debugLines, int addressOffset) {
this.writer = writer; this.writer = writer;
this.version = version; this.module = module;
this.functionIndexes = functionIndexes;
this.importedIndexes = importedIndexes;
this.signatureIndexes = signatureIndexes;
this.dwarfGenerator = dwarfGenerator; this.dwarfGenerator = dwarfGenerator;
this.addressOffset = addressOffset; this.addressOffset = addressOffset;
this.debugLines = debugLines; this.debugLines = debugLines;
@ -115,7 +110,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
} }
private void writeBlockType(WasmType type) { private void writeBlockType(WasmType type) {
writer.writeType(type, version); writer.writeType(type, module);
} }
@Override @Override
@ -240,6 +235,14 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
popLocation(); popLocation();
} }
@Override
public void visit(WasmNullConstant expression) {
pushLocation(expression);
writer.writeByte(0xD0);
writer.writeSignedLEB(module.types.indexOf(expression.getType()));
popLocation();
}
@Override @Override
public void visit(WasmGetLocal expression) { public void visit(WasmGetLocal expression) {
pushLocation(expression); pushLocation(expression);
@ -724,13 +727,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
for (WasmExpression argument : expression.getArguments()) { for (WasmExpression argument : expression.getArguments()) {
argument.acceptVisitor(this); argument.acceptVisitor(this);
} }
Integer functionIndex = !expression.isImported() var functionIndex = module.functions.indexOf(expression.getFunction());
? functionIndexes.get(expression.getFunctionName())
: importedIndexes.get(expression.getFunctionName());
if (functionIndex == null) {
writer.writeByte(0x00);
return;
}
writer.writeByte(0x10); writer.writeByte(0x10);
writer.writeLEB(functionIndex); writer.writeLEB(functionIndex);
@ -745,13 +742,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
} }
expression.getSelector().acceptVisitor(this); expression.getSelector().acceptVisitor(this);
writer.writeByte(0x11); writer.writeByte(0x11);
writer.writeLEB(module.types.indexOf(expression.getType()));
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.writeByte(0); writer.writeByte(0);
popLocation(); popLocation();

View File

@ -16,6 +16,8 @@
package org.teavm.backend.wasm.render; package org.teavm.backend.wasm.render;
import java.util.Arrays; 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; import org.teavm.backend.wasm.model.WasmType;
public class WasmBinaryWriter { public class WasmBinaryWriter {
@ -27,11 +29,19 @@ public class WasmBinaryWriter {
data[pointer++] = (byte) v; data[pointer++] = (byte) v;
} }
public void writeType(WasmType type, WasmBinaryVersion version) { public void writeType(WasmType type, WasmModule module) {
if (type == null) { if (type == null) {
writeByte(0x40); writeByte(0x40);
return; 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) { switch (type) {
case INT32: case INT32:
writeByte(0x7F); writeByte(0x7F);

View File

@ -29,6 +29,7 @@ import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.model.TextLocation; import org.teavm.model.TextLocation;
public class WasmCRenderer { public class WasmCRenderer {
private WasmModule module;
private StringBuilder out = new StringBuilder(); private StringBuilder out = new StringBuilder();
private int indentLevel; private int indentLevel;
String currentFile = ""; String currentFile = "";
@ -37,6 +38,10 @@ public class WasmCRenderer {
boolean memoryAccessChecked; boolean memoryAccessChecked;
TextLocation lastReportedLocation; TextLocation lastReportedLocation;
public WasmCRenderer(WasmModule module) {
this.module = module;
}
public boolean isLineNumbersEmitted() { public boolean isLineNumbersEmitted() {
return lineNumbersEmitted; return lineNumbersEmitted;
} }
@ -75,7 +80,7 @@ public class WasmCRenderer {
renderFunctionDeclarations(module); renderFunctionDeclarations(module);
renderFunctionTable(module); renderFunctionTable(module);
for (WasmFunction function : module.getFunctions().values()) { for (var function : module.functions) {
if (function.getImportName() == null) { if (function.getImportName() == null) {
renderFunction(function); renderFunction(function);
} }
@ -91,13 +96,13 @@ public class WasmCRenderer {
line(module.getStartFunction().getName() + "();"); line(module.getStartFunction().getName() + "();");
} }
for (WasmFunction function : module.getFunctions().values()) { for (var function : module.functions) {
if (function.getExportName() != null && function.getExportName().equals("main")) { if (function.getExportName() != null && function.getExportName().equals("main")) {
line(function.getName() + "(1);"); line(function.getName() + "(1);");
} }
} }
for (WasmFunction function : module.getFunctions().values()) { for (var function : module.functions) {
if (Objects.equals(function.getExportName(), "_start")) { if (Objects.equals(function.getExportName(), "_start")) {
line(function.getName() + "();"); line(function.getName() + "();");
} }
@ -171,26 +176,26 @@ public class WasmCRenderer {
} }
private void renderFunctionDeclarations(WasmModule module) { private void renderFunctionDeclarations(WasmModule module) {
for (WasmFunction function : module.getFunctions().values()) { for (var function : module.functions) {
line(functionDeclaration(function) + ";"); line(functionDeclaration(function) + ";");
} }
} }
private void renderFunction(WasmFunction function) { private void renderFunction(WasmFunction function) {
WasmCRenderingVisitor visitor = new WasmCRenderingVisitor(function.getResult(), var visitor = new WasmCRenderingVisitor(function.getType().getReturnType(),
function.getLocalVariables().size(), function.getModule()); function.getLocalVariables().size(), module);
visitor.setMemoryAccessChecked(memoryAccessChecked); visitor.setMemoryAccessChecked(memoryAccessChecked);
StringBuilder declaration = new StringBuilder(); StringBuilder declaration = new StringBuilder();
renderFunctionModifiers(declaration, function); renderFunctionModifiers(declaration, function);
declaration.append(WasmCRenderingVisitor.mapType(function.getResult())).append(' '); declaration.append(WasmCRenderingVisitor.mapType(function.getType().getReturnType())).append(' ');
declaration.append(function.getName()).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) { for (int i = 0; i < sz; ++i) {
if (i > 0) { if (i > 0) {
declaration.append(", "); 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); WasmLocal var = function.getLocalVariables().get(i);
declaration.append(' ').append(visitor.getVariableName(var)); declaration.append(' ').append(visitor.getVariableName(var));
} }
@ -212,7 +217,7 @@ public class WasmCRenderer {
lines.addAll(visitor.getValue().getLines()); lines.addAll(visitor.getValue().getLines());
} }
visitor.setRequiredType(function.getResult()); visitor.setRequiredType(function.getType().getReturnType());
body.get(body.size() - 1).acceptVisitor(visitor); body.get(body.size() - 1).acceptVisitor(visitor);
lines.addAll(visitor.getValue().getLines()); lines.addAll(visitor.getValue().getLines());
if (visitor.getValue().getText() != null) { if (visitor.getValue().getText() != null) {
@ -232,7 +237,7 @@ public class WasmCRenderer {
private String functionDeclaration(WasmFunction function) { private String functionDeclaration(WasmFunction function) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
renderFunctionModifiers(sb, function); renderFunctionModifiers(sb, function);
sb.append(WasmCRenderingVisitor.mapType(function.getResult())).append(' '); sb.append(WasmCRenderingVisitor.mapType(function.getType().getReturnType())).append(' ');
if (function.getImportName() != null) { if (function.getImportName() != null) {
sb.append(function.getImportModule() != null && !function.getImportModule().isEmpty() sb.append(function.getImportModule() != null && !function.getImportModule().isEmpty()
? function.getImportModule() + "_" + function.getImportName() ? function.getImportModule() + "_" + function.getImportName()
@ -241,11 +246,11 @@ public class WasmCRenderer {
sb.append(function.getName()); sb.append(function.getName());
} }
sb.append("("); sb.append("(");
for (int i = 0; i < function.getParameters().size(); ++i) { for (int i = 0; i < function.getType().getParameterTypes().size(); ++i) {
if (i > 0) { if (i > 0) {
sb.append(", "); sb.append(", ");
} }
sb.append(WasmCRenderingVisitor.mapType(function.getParameters().get(i))); sb.append(WasmCRenderingVisitor.mapType(function.getType().getParameterTypes().get(i)));
} }
sb.append(")"); sb.append(")");

View File

@ -22,9 +22,9 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmModule; 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.WasmType;
import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmBranch; 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.WasmLoadInt32;
import org.teavm.backend.wasm.model.expression.WasmLoadInt64; import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
import org.teavm.backend.wasm.model.expression.WasmMemoryGrow; 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.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32; 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 @Override
public void visit(WasmGetLocal expression) { public void visit(WasmGetLocal expression) {
value = new CExpression(getVariableName(expression.getLocal())); value = new CExpression(getVariableName(expression.getLocal()));
@ -647,7 +653,7 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
if (type != null && expression.getSourceType() != expression.getTargetType()) { if (type != null && expression.getSourceType() != expression.getTargetType()) {
switch (expression.getTargetType()) { switch (expression.getTargetType()) {
case INT32: case INT32:
if (expression.getSourceType() == WasmType.FLOAT32 && expression.isReinterpret()) { if (expression.getSourceType() == WasmNumType.FLOAT32 && expression.isReinterpret()) {
result.setText("reinterpret_float32(" + operand.getText() + ")"); result.setText("reinterpret_float32(" + operand.getText() + ")");
} else if (expression.isSigned()) { } else if (expression.isSigned()) {
result.setText("(int32_t) " + operand.getText()); result.setText("(int32_t) " + operand.getText());
@ -656,7 +662,7 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
} }
break; break;
case INT64: case INT64:
if (expression.getSourceType() == WasmType.FLOAT64 && expression.isReinterpret()) { if (expression.getSourceType() == WasmNumType.FLOAT64 && expression.isReinterpret()) {
result.setText("reinterpret_float64(" + operand.getText() + ")"); result.setText("reinterpret_float64(" + operand.getText() + ")");
} else if (expression.isSigned()) { } else if (expression.isSigned()) {
result.setText("(int64_t) " + operand.getText()); result.setText("(int64_t) " + operand.getText());
@ -665,9 +671,9 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
} }
break; break;
case FLOAT32: case FLOAT32:
if (expression.getSourceType() == WasmType.INT32 && expression.isReinterpret()) { if (expression.getSourceType() == WasmNumType.INT32 && expression.isReinterpret()) {
result.setText("reinterpret_int32(" + operand.getText() + ")"); result.setText("reinterpret_int32(" + operand.getText() + ")");
} else if (expression.getSourceType() == WasmType.FLOAT64) { } else if (expression.getSourceType() == WasmNumType.FLOAT64) {
result.setText("(float) " + operand.getText()); result.setText("(float) " + operand.getText());
} else if (expression.isSigned()) { } else if (expression.isSigned()) {
result.setText("(float) (int64_t) " + operand.getText()); result.setText("(float) (int64_t) " + operand.getText());
@ -676,9 +682,9 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
} }
break; break;
case FLOAT64: case FLOAT64:
if (expression.getSourceType() == WasmType.INT64 && expression.isReinterpret()) { if (expression.getSourceType() == WasmNumType.INT64 && expression.isReinterpret()) {
result.setText("reinterpret_int64(" + operand.getText() + ")"); result.setText("reinterpret_int64(" + operand.getText() + ")");
} else if (expression.getSourceType() == WasmType.FLOAT32) { } else if (expression.getSourceType() == WasmNumType.FLOAT32) {
result.setText("(double) " + operand.getText()); result.setText("(double) " + operand.getText());
} else if (expression.isSigned()) { } else if (expression.isSigned()) {
result.setText("(double) (int64_t) " + operand.getText()); result.setText("(double) (int64_t) " + operand.getText());
@ -694,25 +700,18 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
@Override @Override
public void visit(WasmCall expression) { public void visit(WasmCall expression) {
WasmFunction function = module.getFunctions().get(expression.getFunctionName()); var function = expression.getFunction();
if (function == null) {
value = new CExpression("0");
return;
}
CExpression result = new CExpression(); CExpression result = new CExpression();
WasmType type = requiredType; WasmType type = requiredType;
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (expression.isImported()) { sb.append(function.getImportModule() != null && !function.getImportModule().isEmpty()
sb.append(function.getImportModule() != null && !function.getImportModule().isEmpty() ? function.getImportModule() + "_" + function.getImportName()
? function.getImportModule() + "_" + function.getImportName() : function.getImportName());
: function.getImportName());
} else {
sb.append(expression.getFunctionName());
}
sb.append('('); sb.append('(');
translateArguments(expression.getArguments(), function.getParameters(), result, sb); translateArguments(expression.getArguments(), function.getType().getParameterTypes(), result, sb);
sb.append(')'); sb.append(')');
result.setText(sb.toString()); result.setText(sb.toString());
@ -729,12 +728,12 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
WasmType type = requiredType; WasmType type = requiredType;
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("(*(" + mapType(expression.getReturnType()) + " (*)("); sb.append("(*(" + mapType(expression.getType().getReturnType()) + " (*)(");
for (int i = 0; i < expression.getParameterTypes().size(); ++i) { for (int i = 0; i < expression.getType().getParameterTypes().size(); ++i) {
if (i > 0) { if (i > 0) {
sb.append(", "); sb.append(", ");
} }
sb.append(mapType(expression.getParameterTypes().get(i))); sb.append(mapType(expression.getType().getParameterTypes().get(i)));
} }
sb.append(")) "); sb.append(")) ");
@ -743,7 +742,7 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
result.getLines().addAll(value.getLines()); result.getLines().addAll(value.getLines());
value = cacheIfNeeded(WasmType.INT32, value, result); value = cacheIfNeeded(WasmType.INT32, value, result);
sb.append("wasm_table[" + value.getText() + "])("); sb.append("wasm_table[" + value.getText() + "])(");
translateArguments(expression.getArguments(), expression.getParameterTypes(), result, sb); translateArguments(expression.getArguments(), expression.getType().getParameterTypes(), result, sb);
sb.append(")"); sb.append(")");
result.setText(sb.toString()); result.setText(sb.toString());
@ -754,7 +753,7 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
value = result; value = result;
} }
private void translateArguments(List<WasmExpression> wasmArguments, List<WasmType> signature, private void translateArguments(List<? extends WasmExpression> wasmArguments, List<? extends WasmType> signature,
CExpression result, StringBuilder sb) { CExpression result, StringBuilder sb) {
if (wasmArguments.isEmpty()) { if (wasmArguments.isEmpty()) {
return; return;
@ -1148,9 +1147,18 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
} }
static String mapType(WasmType type) { 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"; return "void";
} else {
throw new IllegalArgumentException();
} }
}
static String mapType(WasmNumType type) {
switch (type) { switch (type) {
case INT32: case INT32:
return "int32_t"; return "int32_t";

View File

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

View File

@ -23,7 +23,13 @@ import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpression;
public class WasmRenderer { 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) { public WasmRenderer append(String text) {
visitor.append(text); visitor.append(text);
@ -58,12 +64,12 @@ public class WasmRenderer {
renderTypes(module); renderTypes(module);
int functionIndex = 0; int functionIndex = 0;
for (WasmFunction function : module.getFunctions().values()) { for (WasmFunction function : module.functions) {
if (function.getImportName() != null) { if (function.getImportName() != null) {
lf().append(";; function #" + functionIndex++).lf().render(function); lf().append(";; function #" + functionIndex++).lf().render(function);
} }
} }
for (WasmFunction function : module.getFunctions().values()) { for (WasmFunction function : module.functions) {
if (function.getImportName() == null) { if (function.getImportName() == null) {
lf().append(";; function #" + functionIndex++).lf().render(function); lf().append(";; function #" + functionIndex++).lf().render(function);
} }
@ -127,7 +133,7 @@ public class WasmRenderer {
renderSignature(function); renderSignature(function);
int firstLocalVariable = function.getParameters().size(); int firstLocalVariable = function.getType().getParameterTypes().size();
if (firstLocalVariable < function.getLocalVariables().size()) { if (firstLocalVariable < function.getLocalVariables().size()) {
visitor.lf().open().append("local"); visitor.lf().open().append("local");
List<WasmLocal> locals = function.getLocalVariables().subList(firstLocalVariable, List<WasmLocal> locals = function.getLocalVariables().subList(firstLocalVariable,
@ -148,44 +154,10 @@ public class WasmRenderer {
} }
private void renderSignature(WasmFunction function) { private void renderSignature(WasmFunction function) {
WasmSignature signature = WasmSignature.fromFunction(function); visitor.append(" ").open().append("type $type" + module.types.indexOf(function.getType())).close();
visitor.append(" ").open().append("type $type" + visitor.getSignatureIndex(signature)).close();
} }
private void renderTypes(WasmModule module) { 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) { private void renderTable(WasmModule module) {

View File

@ -15,11 +15,11 @@
*/ */
package org.teavm.backend.wasm.render; package org.teavm.backend.wasm.render;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.teavm.backend.wasm.model.WasmLocal; 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.WasmType;
import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmBranch; 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.WasmLoadInt32;
import org.teavm.backend.wasm.model.expression.WasmLoadInt64; import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
import org.teavm.backend.wasm.model.expression.WasmMemoryGrow; 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.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32; import org.teavm.backend.wasm.model.expression.WasmStoreFloat32;
@ -71,8 +72,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
private int indentLevel; private int indentLevel;
private boolean lfDeferred; private boolean lfDeferred;
boolean lineNumbersEmitted; boolean lineNumbersEmitted;
List<WasmSignature> signatureList = new ArrayList<>(); WasmModule module;
private Map<WasmSignature, Integer> signatureMap = new HashMap<>();
WasmRenderingVisitor(WasmModule module) {
this.module = module;
}
void preprocess(WasmExpression expression) { void preprocess(WasmExpression expression) {
expression.acceptVisitor(new WasmDefaultExpressionVisitor() { expression.acceptVisitor(new WasmDefaultExpressionVisitor() {
@ -269,6 +273,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
open().append("f64.const " + Double.toHexString(expression.getValue())).close(); 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 @Override
public void visit(WasmGetLocal expression) { public void visit(WasmGetLocal expression) {
open().append("get_local " + asString(expression.getLocal())).close(); open().append("get_local " + asString(expression.getLocal())).close();
@ -398,7 +407,7 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
@Override @Override
public void visit(WasmCall expression) { 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()) { for (WasmExpression argument : expression.getArguments()) {
line(argument); line(argument);
} }
@ -407,25 +416,7 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
@Override @Override
public void visit(WasmIndirectCall expression) { 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 @Override
@ -654,6 +645,16 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
} }
private String type(WasmType type) { 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) { switch (type) {
case INT32: case INT32:
return "i32"; return "i32";

View File

@ -15,16 +15,25 @@
*/ */
package org.teavm.backend.wasm.render; package org.teavm.backend.wasm.render;
import java.util.Arrays; import java.util.List;
import org.teavm.backend.wasm.model.WasmFunction; import java.util.Objects;
import org.teavm.backend.wasm.model.WasmTag;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
final class WasmSignature { public final class WasmSignature {
WasmType[] types; private WasmType returnType;
private List<? extends WasmType> types;
WasmSignature(WasmType[] types) { public WasmSignature(WasmType returnType, WasmType... parameterTypes) {
this.types = types; this.returnType = returnType;
this.types = List.of(parameterTypes);
}
public WasmType getReturnType() {
return returnType;
}
public List<? extends WasmType> getParameterTypes() {
return types;
} }
@Override @Override
@ -36,28 +45,11 @@ final class WasmSignature {
return false; return false;
} }
WasmSignature that = (WasmSignature) o; WasmSignature that = (WasmSignature) o;
return Arrays.equals(types, that.types); return Objects.equals(types, that.types) && Objects.equals(returnType, that.returnType);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Arrays.hashCode(types); return Objects.hash(types, returnType);
}
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);
} }
} }

View File

@ -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<WasmSignature> consumer;
public WasmSignatureCollector(Consumer<WasmSignature> 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));
}
}

View File

@ -15,8 +15,6 @@
*/ */
package org.teavm.backend.wasm.render; 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.WasmType;
import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmBranch; 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.WasmLoadInt32;
import org.teavm.backend.wasm.model.expression.WasmLoadInt64; import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
import org.teavm.backend.wasm.model.expression.WasmMemoryGrow; 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.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32; 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; import org.teavm.backend.wasm.model.expression.WasmUnreachable;
public class WasmTypeInference implements WasmExpressionVisitor { public class WasmTypeInference implements WasmExpressionVisitor {
private WasmGenerationContext context;
private WasmType result; private WasmType result;
public WasmTypeInference(WasmGenerationContext context) {
this.context = context;
}
public WasmType getResult() { public WasmType getResult() {
return result; return result;
} }
@ -155,18 +149,23 @@ public class WasmTypeInference implements WasmExpressionVisitor {
@Override @Override
public void visit(WasmConversion expression) { public void visit(WasmConversion expression) {
result = expression.getTargetType(); result = WasmType.num(expression.getTargetType());
}
@Override
public void visit(WasmNullConstant expression) {
result = expression.type.getReference();
} }
@Override @Override
public void visit(WasmCall expression) { public void visit(WasmCall expression) {
WasmFunction function = context.getFunction(expression.getFunctionName()); var function = expression.getFunction();
result = function == null ? null : function.getResult(); result = function == null ? null : function.getType().getReturnType();
} }
@Override @Override
public void visit(WasmIndirectCall expression) { public void visit(WasmIndirectCall expression) {
result = expression.getReturnType(); result = expression.getType().getReturnType();
} }
@Override @Override

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.backend.wasm.transformation; 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.WasmFunction;
import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
@ -25,33 +26,32 @@ import org.teavm.backend.wasm.model.expression.WasmReplacingExpressionVisitor;
public class IndirectCallTraceTransformation { public class IndirectCallTraceTransformation {
private WasmModule module; private WasmModule module;
private WasmFunctionTypes functionTypes;
public IndirectCallTraceTransformation(WasmModule module) { public IndirectCallTraceTransformation(WasmModule module, WasmFunctionTypes functionTypes) {
this.module = module; this.module = module;
this.functionTypes = functionTypes;
} }
public void apply() { 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.setImportModule("debug");
traceFunction.setImportName("traceIndirectCall"); traceFunction.setImportName("traceIndirectCall");
traceFunction.getParameters().add(WasmType.INT32); module.functions.add(traceFunction);
traceFunction.getParameters().add(WasmType.INT32);
traceFunction.setResult(WasmType.INT32);
module.add(traceFunction);
int[] positionHolder = { 0 }; int[] positionHolder = { 0 };
WasmReplacingExpressionVisitor visitor = new WasmReplacingExpressionVisitor(expression -> { var visitor = new WasmReplacingExpressionVisitor(expression -> {
if (expression instanceof WasmIndirectCall) { if (expression instanceof WasmIndirectCall) {
WasmIndirectCall indirectCall = (WasmIndirectCall) expression; var indirectCall = (WasmIndirectCall) expression;
WasmCall call = new WasmCall(traceFunction.getName()); var call = new WasmCall(traceFunction);
call.setImported(true);
call.getArguments().add(new WasmInt32Constant(positionHolder[0]++)); call.getArguments().add(new WasmInt32Constant(positionHolder[0]++));
call.getArguments().add(indirectCall.getSelector()); call.getArguments().add(indirectCall.getSelector());
indirectCall.setSelector(call); indirectCall.setSelector(call);
} }
return expression; return expression;
}); });
for (WasmFunction function : module.getFunctions().values()) { for (var function : module.functions) {
visitor.replace(function); visitor.replace(function);
} }
} }

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.backend.wasm.transformation; 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.WasmFunction;
import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
@ -25,33 +26,32 @@ import org.teavm.backend.wasm.model.expression.WasmReplacingExpressionVisitor;
public class MemoryAccessTraceTransformation { public class MemoryAccessTraceTransformation {
private WasmModule module; private WasmModule module;
private WasmFunctionTypes functionTypes;
public MemoryAccessTraceTransformation(WasmModule module) { public MemoryAccessTraceTransformation(WasmModule module, WasmFunctionTypes functionTypes) {
this.module = module; this.module = module;
this.functionTypes = functionTypes;
} }
public void apply() { public void apply() {
WasmFunction traceFunction = new WasmFunction("traceMemoryAccess"); var traceFunction = new WasmFunction(functionTypes.of(WasmType.INT32, WasmType.INT32, WasmType.INT32));
traceFunction.setImportModule("debug"); traceFunction.setImportModule("debug");
traceFunction.setName("traceMemoryAccess");
traceFunction.setImportName("traceMemoryAccess"); traceFunction.setImportName("traceMemoryAccess");
traceFunction.getParameters().add(WasmType.INT32); module.functions.add(traceFunction);
traceFunction.getParameters().add(WasmType.INT32);
traceFunction.setResult(WasmType.INT32);
module.add(traceFunction);
int[] positionHolder = { 0 }; int[] positionHolder = { 0 };
WasmReplacingExpressionVisitor visitor = new WasmReplacingExpressionVisitor(expression -> { var visitor = new WasmReplacingExpressionVisitor(expression -> {
if (expression instanceof WasmMemoryAccess) { if (expression instanceof WasmMemoryAccess) {
WasmMemoryAccess memoryAccess = (WasmMemoryAccess) expression; WasmMemoryAccess memoryAccess = (WasmMemoryAccess) expression;
WasmCall call = new WasmCall(traceFunction.getName()); WasmCall call = new WasmCall(traceFunction);
call.setImported(true);
call.getArguments().add(new WasmInt32Constant(positionHolder[0]++)); call.getArguments().add(new WasmInt32Constant(positionHolder[0]++));
call.getArguments().add(memoryAccess.getIndex()); call.getArguments().add(memoryAccess.getIndex());
memoryAccess.setIndex(call); memoryAccess.setIndex(call);
} }
return expression; return expression;
}); });
for (WasmFunction function : module.getFunctions().values()) { for (var function : module.functions) {
visitor.replace(function); visitor.replace(function);
} }
} }

View File

@ -132,14 +132,14 @@ public class ResourceReadIntrinsic implements WasmIntrinsic {
switch (invocation.getMethod().getName()) { switch (invocation.getMethod().getName()) {
case "keys": { case "keys": {
WasmExpression map = manager.generate(invocation.getArguments().get(0)); 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); call.getArguments().add(map);
return call; return call;
} }
case "has": { case "has": {
WasmExpression map = manager.generate(invocation.getArguments().get(0)); WasmExpression map = manager.generate(invocation.getArguments().get(0));
WasmExpression key = manager.generate(invocation.getArguments().get(1)); 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(map);
call.getArguments().add(key); call.getArguments().add(key);
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.NE, call, 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 map = manager.generate(invocation.getArguments().get(0));
WasmExpression key = manager.generate(invocation.getArguments().get(1)); 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(map);
call.getArguments().add(key); call.getArguments().add(key);
WasmLocal entryVar = manager.getTemporary(WasmType.INT32); WasmLocal entryVar = manager.getTemporary(WasmType.INT32);