Wasm backend: fix

This commit is contained in:
Alexey Andreev 2019-04-26 17:10:28 +03:00
parent 20866637e7
commit da68fa0083
10 changed files with 191 additions and 14 deletions

View File

@ -49,6 +49,7 @@ import org.teavm.backend.c.generators.ArrayGenerator;
import org.teavm.backend.c.generators.Generator; import org.teavm.backend.c.generators.Generator;
import org.teavm.backend.c.intrinsic.AddressIntrinsic; import org.teavm.backend.c.intrinsic.AddressIntrinsic;
import org.teavm.backend.c.intrinsic.AllocatorIntrinsic; import org.teavm.backend.c.intrinsic.AllocatorIntrinsic;
import org.teavm.backend.c.intrinsic.ConsoleIntrinsic;
import org.teavm.backend.c.intrinsic.ExceptionHandlingIntrinsic; import org.teavm.backend.c.intrinsic.ExceptionHandlingIntrinsic;
import org.teavm.backend.c.intrinsic.FunctionIntrinsic; import org.teavm.backend.c.intrinsic.FunctionIntrinsic;
import org.teavm.backend.c.intrinsic.GCIntrinsic; import org.teavm.backend.c.intrinsic.GCIntrinsic;
@ -293,6 +294,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
intrinsics.add(new LongIntrinsic()); intrinsics.add(new LongIntrinsic());
intrinsics.add(new IntegerIntrinsic()); intrinsics.add(new IntegerIntrinsic());
intrinsics.add(new StringsIntrinsic()); intrinsics.add(new StringsIntrinsic());
intrinsics.add(new ConsoleIntrinsic());
List<Generator> generators = new ArrayList<>(); List<Generator> generators = new ArrayList<>();
generators.add(new ArrayGenerator()); generators.add(new ArrayGenerator());

View File

@ -0,0 +1,63 @@
/*
* Copyright 2019 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.c.intrinsic;
import org.teavm.ast.ConstantExpr;
import org.teavm.ast.Expr;
import org.teavm.ast.InvocationExpr;
import org.teavm.backend.c.generate.StringPoolGenerator;
import org.teavm.model.MethodReference;
import org.teavm.runtime.Console;
public class ConsoleIntrinsic implements Intrinsic {
@Override
public boolean canHandle(MethodReference method) {
if (!method.getClassName().equals(Console.class.getName())) {
return false;
}
return method.getName().equals("printString");
}
@Override
public void apply(IntrinsicContext context, InvocationExpr invocation) {
switch (invocation.getMethod().getName()) {
case "printString": {
context.writer().print("fprintf(stderr, \"%s\", ");
Expr arg = invocation.getArguments().get(0);
String literal = extractStringConstant(arg);
if (literal != null) {
StringPoolGenerator.generateSimpleStringLiteral(context.writer(), literal);
} else {
context.writer().print("teavm_stringToC(");
context.emit(arg);
context.writer().print(")");
}
context.writer().print(")");
break;
}
}
}
private String extractStringConstant(Expr expr) {
if (!(expr instanceof ConstantExpr)) {
return null;
}
Object value = ((ConstantExpr) expr).getValue();
return value instanceof String ? (String) value : null;
}
}

View File

@ -83,6 +83,15 @@ public final class WasmRuntime {
@Import(name = "print", module = "spectest") @Import(name = "print", module = "spectest")
public static native void print(int a); public static native void print(int a);
@Import(name = "logString", module = "teavm")
public static native void printString(String s);
@Import(name = "logInt", module = "teavm")
public static native void printInt(int i);
@Import(name = "logOutOfMemory", module = "teavm")
public static native void printOutOfMemory();
public static void fillZero(Address address, int count) { public static void fillZero(Address address, int count) {
int start = address.toInt(); int start = address.toInt();

View File

@ -44,6 +44,7 @@ import org.teavm.backend.wasm.generators.WasmMethodGeneratorContext;
import org.teavm.backend.wasm.intrinsics.AddressIntrinsic; import org.teavm.backend.wasm.intrinsics.AddressIntrinsic;
import org.teavm.backend.wasm.intrinsics.AllocatorIntrinsic; import org.teavm.backend.wasm.intrinsics.AllocatorIntrinsic;
import org.teavm.backend.wasm.intrinsics.ClassIntrinsic; import org.teavm.backend.wasm.intrinsics.ClassIntrinsic;
import org.teavm.backend.wasm.intrinsics.ConsoleIntrinsic;
import org.teavm.backend.wasm.intrinsics.DoubleIntrinsic; import org.teavm.backend.wasm.intrinsics.DoubleIntrinsic;
import org.teavm.backend.wasm.intrinsics.ExceptionHandlingIntrinsic; import org.teavm.backend.wasm.intrinsics.ExceptionHandlingIntrinsic;
import org.teavm.backend.wasm.intrinsics.FloatIntrinsic; import org.teavm.backend.wasm.intrinsics.FloatIntrinsic;
@ -269,6 +270,12 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
String[].class)).use(); String[].class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "lookupResource", Address.class, dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "lookupResource", Address.class,
String.class, Address.class)).use(); String.class, Address.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "printString",
String.class, void.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "printInt",
int.class, void.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "printOutOfMemory",
void.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocate", dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocate",
RuntimeClass.class, Address.class)).use(); RuntimeClass.class, Address.class)).use();
@ -344,6 +351,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
context.addIntrinsic(new LongIntrinsic()); context.addIntrinsic(new LongIntrinsic());
context.addIntrinsic(new IntegerIntrinsic()); context.addIntrinsic(new IntegerIntrinsic());
context.addIntrinsic(new ObjectIntrinsic()); context.addIntrinsic(new ObjectIntrinsic());
context.addIntrinsic(new ConsoleIntrinsic());
context.addGenerator(new ArrayGenerator()); context.addGenerator(new ArrayGenerator());
IntrinsicFactoryContext intrinsicFactoryContext = new IntrinsicFactoryContext(); IntrinsicFactoryContext intrinsicFactoryContext = new IntrinsicFactoryContext();

View File

@ -0,0 +1,66 @@
/*
* Copyright 2019 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.intrinsics;
import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.WasmRuntime;
import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
import org.teavm.model.MethodReference;
import org.teavm.runtime.Console;
public class ConsoleIntrinsic implements WasmIntrinsic {
private static final MethodReference PRINT_STRING = new MethodReference(WasmRuntime.class,
"printString", String.class, void.class);
private static final MethodReference PRINT_INT = new MethodReference(WasmRuntime.class,
"printInt", int.class, void.class);
@Override
public boolean isApplicable(MethodReference methodReference) {
if (!methodReference.getClassName().equals(Console.class.getName())) {
return false;
}
switch (methodReference.getName()) {
case "printString":
case "printInt":
return true;
default:
return false;
}
}
@Override
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, true);
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, true);
call.getArguments().add(manager.generate(invocation.getArguments().get(0)));
return call;
}
default:
return new WasmUnreachable();
}
}
}

View File

@ -18,13 +18,19 @@ package org.teavm.backend.wasm.intrinsics;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.WasmRuntime;
import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant; import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
import org.teavm.backend.wasm.model.expression.WasmInt64Constant; import org.teavm.backend.wasm.model.expression.WasmInt64Constant;
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.runtime.GC; import org.teavm.runtime.GC;
public class GCIntrinsic implements WasmIntrinsic { public class GCIntrinsic implements WasmIntrinsic {
private static final MethodReference PRINT_OUT_OF_MEMORY = new MethodReference(
WasmRuntime.class, "printOutOfMemory", void.class);
private List<WasmInt32Constant> heapAddressExpressions = new ArrayList<>(); private List<WasmInt32Constant> heapAddressExpressions = new ArrayList<>();
private List<WasmInt64Constant> availableBytesExpressions = new ArrayList<>(); private List<WasmInt64Constant> availableBytesExpressions = new ArrayList<>();
private List<WasmInt32Constant> gcStorageAddressExpressions = new ArrayList<>(); private List<WasmInt32Constant> gcStorageAddressExpressions = new ArrayList<>();
@ -89,6 +95,7 @@ public class GCIntrinsic implements WasmIntrinsic {
case "regionsAddress": case "regionsAddress":
case "regionMaxCount": case "regionMaxCount":
case "regionSize": case "regionSize":
case "outOfMemory":
return true; return true;
default: default:
return false; return false;
@ -122,6 +129,13 @@ public class GCIntrinsic implements WasmIntrinsic {
availableBytesExpressions.add(constant); availableBytesExpressions.add(constant);
return constant; return constant;
} }
case "outOfMemory": {
WasmBlock block = new WasmBlock(false);
WasmCall call = new WasmCall(manager.getNames().forMethod(PRINT_OUT_OF_MEMORY), true);
block.getBody().add(call);
block.getBody().add(new WasmUnreachable());
return block;
}
default: default:
throw new IllegalArgumentException(invocation.getMethod().toString()); throw new IllegalArgumentException(invocation.getMethod().toString());
} }

View File

@ -337,6 +337,9 @@ public class WasmBinaryRenderer {
functionsSubsection.writeLEB(functions.size()); functionsSubsection.writeLEB(functions.size());
for (WasmFunction function : functions) { for (WasmFunction function : functions) {
if (function.getImportName() != null) {
continue;
}
functionsSubsection.writeLEB(functionIndexes.get(function.getName())); functionsSubsection.writeLEB(functionIndexes.get(function.getName()));
functionsSubsection.writeAsciiString(function.getName()); functionsSubsection.writeAsciiString(function.getName());
} }

View File

@ -15,7 +15,6 @@
*/ */
package org.teavm.runtime; package org.teavm.runtime;
import org.teavm.interop.Address;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.interop.StaticInit; import org.teavm.interop.StaticInit;
import org.teavm.interop.Unmanaged; import org.teavm.interop.Unmanaged;
@ -26,8 +25,7 @@ public final class Console {
private Console() { private Console() {
} }
@Import(name = "teavm_printString") public static native void printString(String s);
public static native void printString(Address ptr);
@Import(name = "teavm_printInt") @Import(name = "teavm_printInt")
public static native void printInt(int n); public static native void printInt(int n);

View File

@ -18,7 +18,6 @@ package org.teavm.runtime;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.interop.Export; import org.teavm.interop.Export;
import org.teavm.interop.StaticInit; import org.teavm.interop.StaticInit;
import org.teavm.interop.Strings;
import org.teavm.interop.Structure; import org.teavm.interop.Structure;
import org.teavm.interop.Unmanaged; import org.teavm.interop.Unmanaged;
@ -36,21 +35,21 @@ public final class ExceptionHandling {
CallSite callSite = findCallSiteById(callSiteId); CallSite callSite = findCallSiteById(callSiteId);
CallSiteLocation location = callSite.location; CallSiteLocation location = callSite.location;
Console.printString(Strings.toC(" at ")); Console.printString(" at ");
if (location.className == null || location.methodName == null) { if (location.className == null || location.methodName == null) {
Console.printString(Strings.toC("(Unknown method)")); Console.printString("(Unknown method)");
} else { } else {
Console.printString(Strings.toC(location.className)); Console.printString(location.className);
Console.printString(Strings.toC(".")); Console.printString(".");
Console.printString(Strings.toC(location.methodName)); Console.printString(location.methodName);
} }
Console.printString(Strings.toC("(")); Console.printString("(");
if (location.fileName != null && location.lineNumber >= 0) { if (location.fileName != null && location.lineNumber >= 0) {
Console.printString(Strings.toC(location.fileName)); Console.printString(location.fileName);
Console.printString(Strings.toC(":")); Console.printString(":");
Console.printInt(location.lineNumber); Console.printInt(location.lineNumber);
} }
Console.printString(Strings.toC(")\n")); Console.printString(")\n");
stackFrame = ShadowStack.getNextStackFrame(stackFrame); stackFrame = ShadowStack.getNextStackFrame(stackFrame);
} }
} }

View File

@ -37,6 +37,17 @@ TeaVM.wasm = function() {
function getNativeOffset(instant) { function getNativeOffset(instant) {
return new Date(instant).getTimezoneOffset(); return new Date(instant).getTimezoneOffset();
} }
function logString(string) {
var memory = new DataView(logString.memory.buffer);
var arrayPtr = memory.getUint32(string + 8, true);
var length = memory.getUint32(arrayPtr + 8, true);
for (var i = 0; i < length; ++i) {
putwchar(memory.getUint16(i * 2 + arrayPtr + 12, true));
}
}
function logInt(i) {
lineBuffer += i.toString();
}
function importDefaults(obj) { function importDefaults(obj) {
obj.teavm = { obj.teavm = {
@ -48,7 +59,10 @@ TeaVM.wasm = function() {
putwchar: putwchar, putwchar: putwchar,
towlower: towlower, towlower: towlower,
towupper: towupper, towupper: towupper,
getNativeOffset: getNativeOffset getNativeOffset: getNativeOffset,
logString: logString,
logInt: logInt,
logOutOfMemory: function() { console.log("Out of memory") }
}; };
obj.teavmMath = Math; obj.teavmMath = Math;
@ -79,6 +93,7 @@ TeaVM.wasm = function() {
} }
WebAssembly.instantiate(response, importObj).then(function(resultObject) { WebAssembly.instantiate(response, importObj).then(function(resultObject) {
importObj.teavm.logString.memory = resultObject.instance.exports.memory;
resultObject.instance.exports.main(); resultObject.instance.exports.main();
callback(resultObject); callback(resultObject);
}).catch(function(error) { }).catch(function(error) {