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

View File

@ -20,6 +20,7 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.util.function.Consumer;
import org.teavm.backend.wasm.model.WasmNumType;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmFloatType;
@ -103,7 +104,8 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
private String typeToString(WasmType type) {
if (type != null) {
switch (type) {
if (type instanceof WasmType.Number) {
switch (((WasmType.Number) type).number) {
case INT32:
return "i32";
case INT64:
@ -115,6 +117,9 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
default:
break;
}
} else if (type instanceof WasmType.Reference) {
return "ref";
}
}
return "unknown";
}
@ -615,7 +620,7 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
}
@Override
public void convert(WasmType sourceType, WasmType targetType, boolean signed, boolean reinterpret) {
public void convert(WasmNumType sourceType, WasmNumType targetType, boolean signed, boolean reinterpret) {
switch (targetType) {
case INT32:
writer.write("i32.");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -133,7 +133,7 @@ public class ArrayGenerator implements WasmMethodGenerator {
new WasmGetLocal(arrayVar));
int baseAddr = BinaryWriter.align(base, 1 << shift[i]);
WasmCall call = new WasmCall(context.getNames().forMethod(methodRef));
WasmCall call = new WasmCall(context.getFunctions().forStaticMethod(methodRef));
switch (primitiveTypes[i].getKind()) {
case BOOLEAN:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,11 +18,11 @@ package org.teavm.backend.wasm.intrinsics;
import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.generate.WasmClassGenerator;
import org.teavm.backend.wasm.generate.WasmGeneratorUtil;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmIndirectCall;
import org.teavm.interop.Function;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
public class FunctionIntrinsic implements WasmIntrinsic {
private WasmClassGenerator classGenerator;
@ -42,15 +42,16 @@ public class FunctionIntrinsic implements WasmIntrinsic {
@Override
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
WasmExpression selector = manager.generate(invocation.getArguments().get(0));
WasmIndirectCall call = new WasmIndirectCall(selector);
var parameterTypes = new WasmType[invocation.getMethod().parameterCount()];
for (var i = 0; i < parameterTypes.length; ++i) {
parameterTypes[i] = WasmGeneratorUtil.mapType(invocation.getMethod().parameterType(i));
}
var returnType = WasmGeneratorUtil.mapType(invocation.getMethod().getReturnType());
var functionType = manager.getFunctionTypes().of(returnType, parameterTypes);
var selector = manager.generate(invocation.getArguments().get(0));
var call = new WasmIndirectCall(selector, functionType);
for (ValueType type : invocation.getMethod().getParameterTypes()) {
call.getParameterTypes().add(WasmGeneratorUtil.mapType(type));
}
if (invocation.getMethod().getReturnType() != ValueType.VOID) {
call.setReturnType(WasmGeneratorUtil.mapType(invocation.getMethod().getReturnType()));
}
for (int i = 1; i < invocation.getArguments().size(); ++i) {
call.getArguments().add(manager.generate(invocation.getArguments().get(i)));
}

View File

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

View File

@ -56,7 +56,7 @@ public class IntegerIntrinsic implements WasmIntrinsic {
manager.generate(invocation.getArguments().get(0)),
manager.generate(invocation.getArguments().get(1)));
case "compareUnsigned":
return new WasmCall(manager.getNames().forMethod(COMPARE_UNSIGNED),
return new WasmCall(manager.getFunctions().forStaticMethod(COMPARE_UNSIGNED),
manager.generate(invocation.getArguments().get(0)),
manager.generate(invocation.getArguments().get(1)));
default:

View File

@ -56,7 +56,7 @@ public class LongIntrinsic implements WasmIntrinsic {
manager.generate(invocation.getArguments().get(0)),
manager.generate(invocation.getArguments().get(1)));
case "compareUnsigned":
return new WasmCall(manager.getNames().forMethod(COMPARE_UNSIGNED),
return new WasmCall(manager.getFunctions().forStaticMethod(COMPARE_UNSIGNED),
manager.generate(invocation.getArguments().get(0)),
manager.generate(invocation.getArguments().get(1)));
default:

View File

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

View File

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

View File

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

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.model.MethodReference;
public class WasmFunction {
WasmModule module;
public class WasmFunction extends WasmEntity {
private String name;
private String exportName;
private String importName;
private String importModule;
private List<WasmType> parameters = new ArrayList<>();
private WasmType result;
private WasmFunctionType type;
private List<WasmLocal> localVariables = new ArrayList<>();
private List<WasmLocal> readonlyLocalVariables = Collections.unmodifiableList(localVariables);
private List<WasmExpression> body = new ArrayList<>();
private MethodReference javaMethod;
public WasmFunction(String name) {
Objects.requireNonNull(name);
this.name = name;
}
public WasmModule getModule() {
return module;
public WasmFunction(WasmFunctionType type) {
this.type = Objects.requireNonNull(type);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getExportName() {
return exportName;
}
@ -62,6 +59,7 @@ public class WasmFunction {
public void setImportName(String importName) {
this.importName = importName;
collection.invalidateIndexes();
}
public String getImportModule() {
@ -72,16 +70,17 @@ public class WasmFunction {
this.importModule = importModule;
}
public WasmType getResult() {
return result;
@Override
boolean isImported() {
return importName != null;
}
public void setResult(WasmType result) {
this.result = result;
public WasmFunctionType getType() {
return type;
}
public List<WasmType> getParameters() {
return parameters;
public void setType(WasmFunctionType type) {
this.type = Objects.requireNonNull(type);
}
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 maxMemorySize;
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 WasmFunction startFunction;
private Map<String, WasmCustomSection> customSections = new LinkedHashMap<>();
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) {
if (functions.containsKey(function.getName())) {
throw new IllegalArgumentException("Function " + function.getName() + " already defined in this module");
}
if (function.module != null) {
throw new IllegalArgumentException("Given function is already registered in another module");
}
functions.put(function.getName(), function);
function.module = this;
}
public void remove(WasmFunction function) {
if (function.getModule() != this) {
return;
}
function.module = null;
functions.remove(function.getName());
}
public Map<String, WasmFunction> getFunctions() {
return readonlyFunctions;
}
public final WasmCollection<WasmFunction> functions = new WasmCollection<>();
public final WasmCollection<WasmCompositeType> types = new WasmCollection<>();
public final WasmCollection<WasmTag> tags = new WasmCollection<>();
public void add(WasmCustomSection customSection) {
if (customSections.containsKey(customSection.getName())) {
@ -112,17 +89,4 @@ public class WasmModule {
public void setStartFunction(WasmFunction startFunction) {
this.startFunction = startFunction;
}
public void addTag(WasmTag tag) {
if (tag.module != null) {
throw new IllegalArgumentException("Given tag already belongs to some module");
}
tags.add(tag);
tag.module = this;
tag.index = tags.size() - 1;
}
public List<? 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;
import java.util.ArrayList;
import java.util.List;
public class WasmTag {
private List<WasmType> values = new ArrayList<>();
public class WasmTag extends WasmEntity {
private WasmFunctionType type;
WasmModule module;
int index;
public List<WasmType> getValues() {
return values;
public WasmTag(WasmFunctionType type) {
this.type = type;
}
public WasmFunctionType getType() {
return type;
}
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");
* you may not use this file except in compliance with the License.
@ -15,9 +15,52 @@
*/
package org.teavm.backend.wasm.model;
public enum WasmType {
INT32,
INT64,
FLOAT32,
FLOAT64
public abstract class WasmType {
public static final WasmType.Number INT32 = new Number(WasmNumType.INT32);
public static final WasmType.Number INT64 = new Number(WasmNumType.INT64);
public static final WasmType.Number FLOAT32 = new Number(WasmNumType.FLOAT32);
public static final WasmType.Number FLOAT64 = new Number(WasmNumType.FLOAT64);
private WasmStorageType.Regular storageType;
private WasmType() {
}
public WasmStorageType.Regular asStorage() {
if (storageType == null) {
storageType = new WasmStorageType.Regular(this);
}
return storageType;
}
public static WasmType.Number num(WasmNumType number) {
switch (number) {
case INT32:
return INT32;
case INT64:
return INT64;
case FLOAT32:
return FLOAT32;
case FLOAT64:
return FLOAT64;
default:
throw new IllegalArgumentException();
}
}
public static final class Number extends WasmType {
public final WasmNumType number;
private Number(WasmNumType number) {
this.number = number;
}
}
public static final class Reference extends WasmType {
public final WasmCompositeType composite;
Reference(WasmCompositeType composite) {
this.composite = composite;
}
}
}

View File

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

View File

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

View File

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

View File

@ -42,7 +42,8 @@ public abstract class WasmExpression {
if (type == null) {
return null;
}
switch (type) {
if (type instanceof WasmType.Number) {
switch (((WasmType.Number) type).number) {
case INT32:
return new WasmInt32Constant(0);
case INT64:
@ -54,5 +55,10 @@ public abstract class WasmExpression {
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(WasmNullConstant expression);
void visit(WasmGetLocal 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.List;
import java.util.Objects;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.WasmFunctionType;
public class WasmIndirectCall extends WasmExpression {
private List<WasmType> parameterTypes = new ArrayList<>();
private WasmType returnType;
private WasmFunctionType type;
private WasmExpression selector;
private List<WasmExpression> arguments = new ArrayList<>();
public WasmIndirectCall(WasmExpression selector) {
Objects.requireNonNull(selector);
this.selector = selector;
public WasmIndirectCall(WasmExpression selector, WasmFunctionType type) {
this.selector = Objects.requireNonNull(selector);
this.type = Objects.requireNonNull(type);
}
public WasmExpression getSelector() {
@ -36,26 +35,21 @@ public class WasmIndirectCall extends WasmExpression {
}
public void setSelector(WasmExpression selector) {
Objects.requireNonNull(selector);
this.selector = selector;
}
public List<WasmType> getParameterTypes() {
return parameterTypes;
}
public WasmType getReturnType() {
return returnType;
}
public void setReturnType(WasmType returnType) {
this.returnType = returnType;
this.selector = Objects.requireNonNull(selector);
}
public List<WasmExpression> getArguments() {
return arguments;
}
public WasmFunctionType getType() {
return type;
}
public void setType(WasmFunctionType type) {
this.type = Objects.requireNonNull(type);
}
@Override
public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this);

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) {
}
@Override
public void visit(WasmNullConstant expression) {
}
@Override
public void visit(WasmGetLocal expression) {
}

View File

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

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;
import org.teavm.backend.wasm.model.WasmNumType;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmFloatType;
@ -97,7 +98,7 @@ public interface CodeListener {
default void storeFloat64(int align, int offset) {
}
default void convert(WasmType sourceType, WasmType targetType, boolean signed, boolean reinterpret) {
default void convert(WasmNumType sourceType, WasmNumType targetType, boolean signed, boolean reinterpret) {
}
default void memoryGrow() {

View File

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

View File

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

View File

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

View File

@ -16,6 +16,8 @@
package org.teavm.backend.wasm.render;
import java.util.Arrays;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmNumType;
import org.teavm.backend.wasm.model.WasmType;
public class WasmBinaryWriter {
@ -27,11 +29,19 @@ public class WasmBinaryWriter {
data[pointer++] = (byte) v;
}
public void writeType(WasmType type, WasmBinaryVersion version) {
public void writeType(WasmType type, WasmModule module) {
if (type == null) {
writeByte(0x40);
return;
}
if (type instanceof WasmType.Number) {
writeType(((WasmType.Number) type).number);
} else if (type instanceof WasmType.Reference) {
writeSignedLEB(module.types.indexOf(((WasmType.Reference) type).composite));
}
}
public void writeType(WasmNumType type) {
switch (type) {
case INT32:
writeByte(0x7F);

View File

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

View File

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

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;
public class WasmRenderer {
private WasmRenderingVisitor visitor = new WasmRenderingVisitor();
private WasmRenderingVisitor visitor;
private WasmModule module;
public WasmRenderer(WasmModule module) {
visitor = new WasmRenderingVisitor(module);
this.module = module;
}
public WasmRenderer append(String text) {
visitor.append(text);
@ -58,12 +64,12 @@ public class WasmRenderer {
renderTypes(module);
int functionIndex = 0;
for (WasmFunction function : module.getFunctions().values()) {
for (WasmFunction function : module.functions) {
if (function.getImportName() != null) {
lf().append(";; function #" + functionIndex++).lf().render(function);
}
}
for (WasmFunction function : module.getFunctions().values()) {
for (WasmFunction function : module.functions) {
if (function.getImportName() == null) {
lf().append(";; function #" + functionIndex++).lf().render(function);
}
@ -127,7 +133,7 @@ public class WasmRenderer {
renderSignature(function);
int firstLocalVariable = function.getParameters().size();
int firstLocalVariable = function.getType().getParameterTypes().size();
if (firstLocalVariable < function.getLocalVariables().size()) {
visitor.lf().open().append("local");
List<WasmLocal> locals = function.getLocalVariables().subList(firstLocalVariable,
@ -148,44 +154,10 @@ public class WasmRenderer {
}
private void renderSignature(WasmFunction function) {
WasmSignature signature = WasmSignature.fromFunction(function);
visitor.append(" ").open().append("type $type" + visitor.getSignatureIndex(signature)).close();
visitor.append(" ").open().append("type $type" + module.types.indexOf(function.getType())).close();
}
private void renderTypes(WasmModule module) {
WasmSignatureCollector signatureCollector = new WasmSignatureCollector(visitor::getSignatureIndex);
for (WasmFunction function : module.getFunctions().values()) {
visitor.getSignatureIndex(WasmSignature.fromFunction(function));
for (WasmExpression part : function.getBody()) {
part.acceptVisitor(signatureCollector);
}
}
if (visitor.signatureList.isEmpty()) {
return;
}
visitor.lf();
int index = 0;
for (WasmSignature signature : visitor.signatureList) {
visitor.open().append("type $type" + index++ + " ");
visitor.open().append("func");
if (signature.types.length > 1) {
visitor.append(" ").open().append("param");
for (int i = 1; i < signature.types.length; ++i) {
visitor.append(" ").append(signature.types[i]);
}
visitor.close();
}
if (signature.types[0] != null) {
visitor.append(" ").open().append("result ");
visitor.append(signature.types[0]);
visitor.close();
}
visitor.close();
visitor.close();
visitor.lf();
}
}
private void renderTable(WasmModule module) {

View File

@ -15,11 +15,11 @@
*/
package org.teavm.backend.wasm.render;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmNumType;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmBranch;
@ -54,6 +54,7 @@ import org.teavm.backend.wasm.model.expression.WasmLoadFloat64;
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
import org.teavm.backend.wasm.model.expression.WasmMemoryGrow;
import org.teavm.backend.wasm.model.expression.WasmNullConstant;
import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32;
@ -71,8 +72,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
private int indentLevel;
private boolean lfDeferred;
boolean lineNumbersEmitted;
List<WasmSignature> signatureList = new ArrayList<>();
private Map<WasmSignature, Integer> signatureMap = new HashMap<>();
WasmModule module;
WasmRenderingVisitor(WasmModule module) {
this.module = module;
}
void preprocess(WasmExpression expression) {
expression.acceptVisitor(new WasmDefaultExpressionVisitor() {
@ -269,6 +273,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
open().append("f64.const " + Double.toHexString(expression.getValue())).close();
}
@Override
public void visit(WasmNullConstant expression) {
open().append("ref.null " + module.types.indexOf(expression.getType())).close();
}
@Override
public void visit(WasmGetLocal expression) {
open().append("get_local " + asString(expression.getLocal())).close();
@ -398,7 +407,7 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
@Override
public void visit(WasmCall expression) {
open().append("call").append(" $" + expression.getFunctionName());
open().append("call").append(" $" + module.functions.indexOf(expression.getFunction()));
for (WasmExpression argument : expression.getArguments()) {
line(argument);
}
@ -407,25 +416,7 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
@Override
public void visit(WasmIndirectCall expression) {
WasmType[] types = new WasmType[expression.getParameterTypes().size() + 1];
types[0] = expression.getReturnType();
for (int i = 0; i < expression.getParameterTypes().size(); ++i) {
types[i + 1] = expression.getParameterTypes().get(i);
}
open().append("call_indirect").append(" $type" + getSignatureIndex(new WasmSignature(types)));
line(expression.getSelector());
for (WasmExpression argument : expression.getArguments()) {
line(argument);
}
close();
}
int getSignatureIndex(WasmSignature signature) {
return signatureMap.computeIfAbsent(signature, key -> {
signatureList.add(key);
return signatureMap.size();
});
}
@Override
@ -654,6 +645,16 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
}
private String type(WasmType type) {
if (type instanceof WasmType.Number) {
return type(((WasmType.Number) type).number);
} else if (type instanceof WasmType.Reference) {
return "(ref " + module.types.indexOf(((WasmType.Reference) type).composite) + ")";
} else {
throw new IllegalArgumentException();
}
}
private String type(WasmNumType type) {
switch (type) {
case INT32:
return "i32";

View File

@ -15,16 +15,25 @@
*/
package org.teavm.backend.wasm.render;
import java.util.Arrays;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmTag;
import java.util.List;
import java.util.Objects;
import org.teavm.backend.wasm.model.WasmType;
final class WasmSignature {
WasmType[] types;
public final class WasmSignature {
private WasmType returnType;
private List<? extends WasmType> types;
WasmSignature(WasmType[] types) {
this.types = types;
public WasmSignature(WasmType returnType, WasmType... parameterTypes) {
this.returnType = returnType;
this.types = List.of(parameterTypes);
}
public WasmType getReturnType() {
return returnType;
}
public List<? extends WasmType> getParameterTypes() {
return types;
}
@Override
@ -36,28 +45,11 @@ final class WasmSignature {
return false;
}
WasmSignature that = (WasmSignature) o;
return Arrays.equals(types, that.types);
return Objects.equals(types, that.types) && Objects.equals(returnType, that.returnType);
}
@Override
public int hashCode() {
return Arrays.hashCode(types);
}
static WasmSignature fromFunction(WasmFunction function) {
WasmType[] types = new WasmType[function.getParameters().size() + 1];
types[0] = function.getResult();
for (int i = 0; i < function.getParameters().size(); ++i) {
types[i + 1] = function.getParameters().get(i);
}
return new WasmSignature(types);
}
static WasmSignature fromTag(WasmTag tag) {
var types = new WasmType[tag.getValues().size() + 1];
for (var i = 0; i < tag.getValues().size(); i++) {
types[i + 1] = tag.getValues().get(i);
}
return new WasmSignature(types);
return Objects.hash(types, returnType);
}
}

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

View File

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

View File

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

View File

@ -132,14 +132,14 @@ public class ResourceReadIntrinsic implements WasmIntrinsic {
switch (invocation.getMethod().getName()) {
case "keys": {
WasmExpression map = manager.generate(invocation.getArguments().get(0));
WasmCall call = new WasmCall(manager.getNames().forMethod(KEYS_METHOD));
var call = new WasmCall(manager.getFunctions().forStaticMethod(KEYS_METHOD));
call.getArguments().add(map);
return call;
}
case "has": {
WasmExpression map = manager.generate(invocation.getArguments().get(0));
WasmExpression key = manager.generate(invocation.getArguments().get(1));
WasmCall call = new WasmCall(manager.getNames().forMethod(LOOKUP_METHOD));
WasmCall call = new WasmCall(manager.getFunctions().forStaticMethod(LOOKUP_METHOD));
call.getArguments().add(map);
call.getArguments().add(key);
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.NE, call,
@ -151,7 +151,7 @@ public class ResourceReadIntrinsic implements WasmIntrinsic {
WasmExpression map = manager.generate(invocation.getArguments().get(0));
WasmExpression key = manager.generate(invocation.getArguments().get(1));
WasmCall call = new WasmCall(manager.getNames().forMethod(LOOKUP_METHOD));
WasmCall call = new WasmCall(manager.getFunctions().forStaticMethod(LOOKUP_METHOD));
call.getArguments().add(map);
call.getArguments().add(key);
WasmLocal entryVar = manager.getTemporary(WasmType.INT32);