From 10c3731c4305649f1cc6547feeb6ebf7d5d20c86 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 27 Aug 2024 13:28:49 +0200 Subject: [PATCH] wasm gc: fix some issues and implement intrinsics * properly cast exception type on catch blocks * generate virtual table structures lazily * fix calculation of types of a.field values --- .../org/teavm/classlib/java/lang/TMath.java | 5 ++ .../ast/decompilation/StatementGenerator.java | 1 + .../wasm/generate/WasmGenerationVisitor.java | 3 +- .../methods/BaseWasmGenerationVisitor.java | 5 +- .../gc/classes/WasmGCClassGenerator.java | 61 ++++++++++++------- .../gc/methods/WasmGCGenerationVisitor.java | 13 +++- .../gc/SystemArrayCopyIntrinsic.java | 1 + .../gc/BaseClassesTransformation.java | 9 +++ .../org/teavm/backend/wasm/wasm-gc-runtime.js | 4 ++ 9 files changed, 76 insertions(+), 26 deletions(-) diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java index 587237586..eb705a331 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java @@ -299,6 +299,8 @@ public final class TMath extends TObject { return randomC(); } else if (PlatformDetector.isWebAssembly()) { return WasmSupport.random(); + } else if (PlatformDetector.isWebAssemblyGC()) { + return randomWasmGC(); } else { return randomImpl(); } @@ -310,6 +312,9 @@ public final class TMath extends TObject { @GeneratedBy(MathNativeGenerator.class) private static native double randomImpl(); + @Import(module = "teavmMath", name = "random") + private static native double randomWasmGC(); + public static int min(int a, int b) { return a < b ? a : b; } diff --git a/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java b/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java index da209c4b2..c8eb01d33 100644 --- a/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java +++ b/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java @@ -401,6 +401,7 @@ class StatementGenerator implements InstructionVisitor { if (insn.getInstance() != null) { AssignmentStatement stmt = Statement.assign(Expr.var(insn.getReceiver().getIndex()), Expr.qualify(Expr.var(insn.getInstance().getIndex()), insn.getField())); + stmt.getRightValue().setVariableIndex(insn.getReceiver().getIndex()); stmt.setLocation(currentLocation); statements.add(stmt); } else { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index 90a2eaa0d..3c8fbc15a 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -175,7 +175,8 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor { } @Override - protected void catchException(TextLocation location, List target, WasmLocal local) { + protected void catchException(TextLocation location, List target, WasmLocal local, + String exceptionClass) { var call = new WasmCall(context.functions().forStaticMethod(CATCH_METHOD)); if (local != null) { var save = new WasmSetLocal(local, call); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java index 5143170d3..d116bef26 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java @@ -1300,7 +1300,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp var catchLocal = tryCatch.getExceptionVariable() != null ? localVar(tryCatch.getExceptionVariable()) : null; - catchException(null, catchBlock.getBody(), catchLocal); + catchException(null, catchBlock.getBody(), catchLocal, tryCatch.getExceptionType()); visitMany(tryCatch.getHandler(), catchBlock.getBody()); if (!catchBlock.isTerminating() && catchBlock != outerCatchBlock) { catchBlock.getBody().add(new WasmBreak(outerCatchBlock)); @@ -1313,7 +1313,8 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp protected abstract WasmExpression peekException(); - protected abstract void catchException(TextLocation location, List target, WasmLocal local); + protected abstract void catchException(TextLocation location, List target, WasmLocal local, + String exceptionClass); private void visitMany(List statements, List target) { var oldTarget = resultConsumer; diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java index f31390d12..046bf1f1b 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java @@ -129,6 +129,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private WasmFunction arrayLengthObjectFunction; private WasmFunctionType arrayGetType; private WasmFunctionType arrayLengthType; + private List nonInitializedStructures = new ArrayList<>(); public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource, WasmFunctionTypes functionTypes, TagRegistry tagRegistry, @@ -163,10 +164,22 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit var classInfo = classInfoQueue.remove(); classInfo.initializer.accept(initializerFunctionStatements); classInfo.initializer = null; + initStructures(); } return true; } + private void initStructures() { + if (nonInitializedStructures.isEmpty()) { + return; + } + var copy = List.copyOf(nonInitializedStructures); + nonInitializedStructures.clear(); + for (var structure : copy) { + structure.init(); + } + } + @Override public void contributeToInitializerDefinitions(WasmFunction function) { fillVirtualTableSupertypes(); @@ -269,6 +282,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit classInfo.structure = new WasmStructure(name != null ? names.forClass(name) : null, fields -> fillFields(finalClassInfo, fields, type)); module.types.add(classInfo.structure); + nonInitializedStructures.add(classInfo.structure); } } if (name != null) { @@ -698,12 +712,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private WasmStructure initRegularClassStructure(String className) { var virtualTable = virtualTables.lookup(className); - var structure = new WasmStructure(names.forClassClass(className)); + var structure = new WasmStructure(names.forClassClass(className), fields -> { + addSystemFields(fields); + fillSimpleClassFields(fields, "java.lang.Class"); + addVirtualTableFields(fields, virtualTable); + }); + nonInitializedStructures.add(structure); structure.setSupertype(standardClasses.classClass().getStructure()); module.types.add(structure); - addSystemFields(structure.getFields()); - fillSimpleClassFields(structure.getFields(), "java.lang.Class"); - addVirtualTableFields(structure, virtualTable); return structure; } @@ -716,46 +732,49 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit fields.add(monitorField); } - private void addVirtualTableFields(WasmStructure structure, WasmGCVirtualTable virtualTable) { + private void addVirtualTableFields(List fields, WasmGCVirtualTable virtualTable) { for (var entry : virtualTable.getEntries()) { var functionType = typeMapper.getFunctionType(entry.getOrigin().getClassName(), entry.getMethod(), false); var field = new WasmField(functionType.getReference().asStorage()); field.setName(names.forVirtualMethod(entry.getMethod())); - structure.getFields().add(field); + fields.add(field); } } @Override public WasmStructure getArrayVirtualTableStructure() { if (arrayVirtualTableStruct == null) { - arrayVirtualTableStruct = new WasmStructure(null); + arrayVirtualTableStruct = new WasmStructure(null, fields -> { + addSystemFields(fields); + fillSimpleClassFields(fields, "java.lang.Class"); + addVirtualTableFields(fields, virtualTables.lookup("java.lang.Object")); + if (metadataRequirements.hasArrayLength()) { + arrayLengthOffset = fields.size(); + var arrayLengthType = getArrayLengthType(); + fields.add(new WasmField(arrayLengthType.getReference().asStorage())); + } + if (metadataRequirements.hasArrayGet()) { + arrayGetOffset = fields.size(); + var arrayGetType = getArrayGetType(); + fields.add(new WasmField(arrayGetType.getReference().asStorage())); + } + }); arrayVirtualTableStruct.setSupertype(standardClasses.objectClass().getVirtualTableStructure()); module.types.add(arrayVirtualTableStruct); - addSystemFields(arrayVirtualTableStruct.getFields()); - fillSimpleClassFields(arrayVirtualTableStruct.getFields(), "java.lang.Class"); - addVirtualTableFields(arrayVirtualTableStruct, virtualTables.lookup("java.lang.Object")); - - if (metadataRequirements.hasArrayLength()) { - arrayLengthOffset = arrayVirtualTableStruct.getFields().size(); - var arrayLengthType = getArrayLengthType(); - arrayVirtualTableStruct.getFields().add(new WasmField(arrayLengthType.getReference().asStorage())); - } - if (metadataRequirements.hasArrayGet()) { - arrayGetOffset = arrayVirtualTableStruct.getFields().size(); - var arrayGetType = getArrayGetType(); - arrayVirtualTableStruct.getFields().add(new WasmField(arrayGetType.getReference().asStorage())); - } + nonInitializedStructures.add(arrayVirtualTableStruct); } return arrayVirtualTableStruct; } @Override public int getArrayLengthOffset() { + initStructures(); return arrayLengthOffset; } @Override public int getArrayGetOffset() { + initStructures(); return arrayGetOffset; } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java index 1b4019f4c..0f4ee4df9 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java @@ -372,10 +372,16 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { } @Override - protected void catchException(TextLocation location, List target, WasmLocal local) { + protected void catchException(TextLocation location, List target, WasmLocal local, + String exceptionClass) { var type = context.classInfoProvider().getClassInfo("java.lang.Throwable").getType(); if (local != null) { - var save = new WasmSetLocal(local, new WasmGetGlobal(context.exceptionGlobal())); + WasmExpression exception = new WasmGetGlobal(context.exceptionGlobal()); + if (exceptionClass != null && !exceptionClass.equals("java.lang.Throwable")) { + exception = new WasmCast(exception, context.classInfoProvider().getClassInfo(exceptionClass) + .getStructure().getReference()); + } + var save = new WasmSetLocal(local, exception); save.setLocation(location); target.add(save); } @@ -511,6 +517,9 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { var fieldType = field.getType(); if (fieldType instanceof ValueType.Primitive) { switch (((ValueType.Primitive) fieldType).getKind()) { + case BOOLEAN: + structGet.setSignedType(WasmSignedType.UNSIGNED); + break; case BYTE: structGet.setSignedType(WasmSignedType.SIGNED); break; diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/SystemArrayCopyIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/SystemArrayCopyIntrinsic.java index 1e9700c90..a8d0e2cb9 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/SystemArrayCopyIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/SystemArrayCopyIntrinsic.java @@ -35,6 +35,7 @@ public class SystemArrayCopyIntrinsic implements WasmGCIntrinsic { public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) { var result = tryGenerateSpecialCase(invocation, context); if (result == null) { + tryGenerateSpecialCase(invocation, context); var function = getDefaultFunction(context); result = new WasmCall(function, context.generate(invocation.getArguments().get(0)), context.generate(invocation.getArguments().get(1)), diff --git a/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java b/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java index 484f3ff94..2d53cf891 100644 --- a/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java +++ b/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java @@ -16,7 +16,10 @@ package org.teavm.backend.wasm.transformation.gc; import org.teavm.backend.wasm.runtime.WasmGCSupport; +import org.teavm.interop.Import; import org.teavm.model.AccessLevel; +import org.teavm.model.AnnotationHolder; +import org.teavm.model.AnnotationValue; import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassHolderTransformerContext; @@ -86,6 +89,12 @@ public class BaseClassesTransformation implements ClassHolderTransformer { method.setProgram(null); method.getModifiers().add(ElementModifier.NATIVE); break; + case "currentTimeMillis": { + var annotation = new AnnotationHolder(Import.class.getName()); + annotation.getValues().put("module", new AnnotationValue("teavm")); + annotation.getValues().put("name", new AnnotationValue("currentTimeMillis")); + method.getAnnotations().add(annotation); + } } } } diff --git a/core/src/main/resources/org/teavm/backend/wasm/wasm-gc-runtime.js b/core/src/main/resources/org/teavm/backend/wasm/wasm-gc-runtime.js index 06eae4dcb..aa5abcb22 100644 --- a/core/src/main/resources/org/teavm/backend/wasm/wasm-gc-runtime.js +++ b/core/src/main/resources/org/teavm/backend/wasm/wasm-gc-runtime.js @@ -35,8 +35,12 @@ TeaVM.wasm = function() { } else { stdout += String.fromCharCode(c); } + }, + currentTimeMillis() { + return new Date().getTime(); } }; + imports.teavmMath = Math; } function load(path, options) {