Wasm: trying to revive GC debugger in wasm2c generator and implement emulation for WASI

This commit is contained in:
Alexey Andreev 2022-11-11 14:04:56 +01:00
parent 8285799b2b
commit 4a025f2605
9 changed files with 129 additions and 27 deletions

View File

@ -1,19 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CheckStyle-IDEA"> <component name="CheckStyle-IDEA" serialisationVersion="2">
<option name="configuration"> <checkstyleVersion>8.41.1</checkstyleVersion>
<scanScope>JavaOnlyWithTests</scanScope>
<option name="thirdPartyClasspath" />
<option name="activeLocationIds">
<option value="c65b2ab0-cb40-4423-befb-a37d514deee5" />
</option>
<option name="locations">
<list>
<ConfigurationLocation id="bundled-sun-checks" type="BUNDLED" scope="All" description="Sun Checks">(bundled)</ConfigurationLocation>
<ConfigurationLocation id="bundled-google-checks" type="BUNDLED" scope="All" description="Google Checks">(bundled)</ConfigurationLocation>
<ConfigurationLocation id="c65b2ab0-cb40-4423-befb-a37d514deee5" type="LOCAL_FILE" scope="All" description="TeaVM">
$PROJECT_DIR$/checkstyle.xml
<option name="properties">
<map> <map>
<entry key="active-configuration" value="LOCAL_FILE:$PROJECT_DIR$/checkstyle.xml:TeaVM" /> <entry key="config_loc" value="." />
<entry key="checkstyle-version" value="8.41" />
<entry key="copy-libs" value="false" />
<entry key="location-0" value="BUNDLED:(bundled):Sun Checks" />
<entry key="location-1" value="BUNDLED:(bundled):Google Checks" />
<entry key="location-2" value="LOCAL_FILE:$PROJECT_DIR$/checkstyle.xml:TeaVM" />
<entry key="property-2.config_loc" value="." />
<entry key="scan-before-checkin" value="false" />
<entry key="scanscope" value="JavaOnlyWithTests" />
<entry key="suppress-errors" value="false" />
</map> </map>
</option> </option>
</ConfigurationLocation>
</list>
</option>
</component> </component>
</project> </project>

View File

@ -348,8 +348,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
String.class, void.class)).use(); String.class, void.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "printInt", dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "printInt",
int.class, void.class)).use(); int.class, void.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "printOutOfMemory", dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "printOutOfMemory", void.class)).use();
void.class)).use();
dependencyAnalyzer.linkMethod(INIT_HEAP_REF).use(); dependencyAnalyzer.linkMethod(INIT_HEAP_REF).use();
dependencyAnalyzer.linkMethod(RESIZE_HEAP_REF).use(); dependencyAnalyzer.linkMethod(RESIZE_HEAP_REF).use();
@ -471,10 +470,11 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
context.addIntrinsic(new ObjectIntrinsic()); context.addIntrinsic(new ObjectIntrinsic());
context.addIntrinsic(new ConsoleIntrinsic()); context.addIntrinsic(new ConsoleIntrinsic());
context.addGenerator(new ArrayGenerator()); context.addGenerator(new ArrayGenerator());
if (!Boolean.parseBoolean(System.getProperty("teavm.wasm.vmAssertions", "false"))) { boolean vmAssertions = Boolean.parseBoolean(System.getProperty("teavm.wasm.vmAssertions", "false"));
if (!vmAssertions) {
context.addIntrinsic(new MemoryTraceIntrinsic()); context.addIntrinsic(new MemoryTraceIntrinsic());
} }
context.addIntrinsic(new WasmHeapIntrinsic()); context.addIntrinsic(new WasmHeapIntrinsic(vmAssertions));
context.addIntrinsic(new FiberIntrinsic()); context.addIntrinsic(new FiberIntrinsic());
IntrinsicFactoryContext intrinsicFactoryContext = new IntrinsicFactoryContext(); IntrinsicFactoryContext intrinsicFactoryContext = new IntrinsicFactoryContext();

View File

@ -49,13 +49,13 @@ public class ConsoleIntrinsic implements WasmIntrinsic {
switch (invocation.getMethod().getName()) { switch (invocation.getMethod().getName()) {
case "printString": { case "printString": {
String name = manager.getNames().forMethod(PRINT_STRING); String name = manager.getNames().forMethod(PRINT_STRING);
WasmCall call = new WasmCall(name, true); WasmCall call = new WasmCall(name);
call.getArguments().add(manager.generate(invocation.getArguments().get(0))); call.getArguments().add(manager.generate(invocation.getArguments().get(0)));
return call; return call;
} }
case "printInt": { case "printInt": {
String name = manager.getNames().forMethod(PRINT_INT); String name = manager.getNames().forMethod(PRINT_INT);
WasmCall call = new WasmCall(name, true); WasmCall call = new WasmCall(name);
call.getArguments().add(manager.generate(invocation.getArguments().get(0))); call.getArguments().add(manager.generate(invocation.getArguments().get(0)));
return call; return call;
} }

View File

@ -111,7 +111,7 @@ public class GCIntrinsic implements WasmIntrinsic {
return intToLong(getStaticField(manager, "heapSize")); return intToLong(getStaticField(manager, "heapSize"));
case "outOfMemory": { case "outOfMemory": {
WasmBlock block = new WasmBlock(false); WasmBlock block = new WasmBlock(false);
WasmCall call = new WasmCall(manager.getNames().forMethod(PRINT_OUT_OF_MEMORY), true); WasmCall call = new WasmCall(manager.getNames().forMethod(PRINT_OUT_OF_MEMORY));
block.getBody().add(call); block.getBody().add(call);
block.getBody().add(new WasmUnreachable()); block.getBody().add(new WasmUnreachable());
return block; return block;

View File

@ -17,17 +17,27 @@ package org.teavm.backend.wasm.intrinsics;
import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.WasmHeap; import org.teavm.backend.wasm.WasmHeap;
import org.teavm.backend.wasm.model.expression.WasmDrop;
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.WasmMemoryGrow; import org.teavm.backend.wasm.model.expression.WasmMemoryGrow;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
public class WasmHeapIntrinsic implements WasmIntrinsic { public class WasmHeapIntrinsic implements WasmIntrinsic {
private boolean trace;
public WasmHeapIntrinsic(boolean trace) {
this.trace = trace;
}
@Override @Override
public boolean isApplicable(MethodReference methodReference) { public boolean isApplicable(MethodReference methodReference) {
if (!methodReference.getClassName().equals(WasmHeap.class.getName())) { if (!methodReference.getClassName().equals(WasmHeap.class.getName())) {
return false; return false;
} }
switch (methodReference.getName()) { switch (methodReference.getName()) {
case "initHeapTrace":
return !trace;
case "growMemory": case "growMemory":
return true; return true;
default: default:
@ -38,6 +48,8 @@ public class WasmHeapIntrinsic implements WasmIntrinsic {
@Override @Override
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
switch (invocation.getMethod().getName()) { switch (invocation.getMethod().getName()) {
case "initHeapTrace":
return new WasmDrop(new WasmInt32Constant(0));
case "growMemory": case "growMemory":
return new WasmMemoryGrow(manager.generate(invocation.getArguments().get(0))); return new WasmMemoryGrow(manager.generate(invocation.getArguments().get(0)));
default: default:

View File

@ -20,6 +20,7 @@ import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmMemorySegment; import org.teavm.backend.wasm.model.WasmMemorySegment;
@ -80,9 +81,11 @@ public class WasmCRenderer {
} }
} }
line("void main() {"); line("void main(int argc, char** argv) {");
indent(); indent();
line("wasm_args = argc;");
line("wasm_argv = argv;");
renderHeap(module); renderHeap(module);
if (module.getStartFunction() != null) { if (module.getStartFunction() != null) {
line(module.getStartFunction().getName() + "();"); line(module.getStartFunction().getName() + "();");
@ -94,6 +97,12 @@ public class WasmCRenderer {
} }
} }
for (WasmFunction function : module.getFunctions().values()) {
if (Objects.equals(function.getExportName(), "_start")) {
line(function.getName() + "();");
}
}
outdent(); outdent();
line("}"); line("}");
} }

View File

@ -102,10 +102,6 @@ public class WasiSupport {
printString("Out of memory"); printString("Out of memory");
} }
@Unmanaged
public static void initHeapTrace(int maxHeap) {
}
public static String[] getArgs() { public static String[] getArgs() {
Address buffer = WasiBuffer.getBuffer(); Address buffer = WasiBuffer.getBuffer();
IntResult sizesReceiver = buffer.toStructure(); IntResult sizesReceiver = buffer.toStructure();
@ -143,6 +139,7 @@ public class WasiSupport {
private static int nextRandom(int bits) { private static int nextRandom(int bits) {
if (!randomInitialized) { if (!randomInitialized) {
randomInitialized = true;
short errno = Wasi.randomGet(WasiBuffer.getBuffer(), 8); short errno = Wasi.randomGet(WasiBuffer.getBuffer(), 8);
if (errno != ERRNO_SUCCESS) { if (errno != ERRNO_SUCCESS) {

View File

@ -17,6 +17,7 @@ package org.teavm.backend.wasm.transformation;
import org.teavm.backend.wasm.runtime.WasiSupport; import org.teavm.backend.wasm.runtime.WasiSupport;
import org.teavm.backend.wasm.runtime.WasmSupport; import org.teavm.backend.wasm.runtime.WasmSupport;
import org.teavm.interop.Import;
import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHierarchy;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassHolderTransformer;
@ -45,6 +46,7 @@ public class WasiSupportClassTransformer implements ClassHolderTransformer {
if (method.hasModifier(ElementModifier.NATIVE)) { if (method.hasModifier(ElementModifier.NATIVE)) {
method.getModifiers().remove(ElementModifier.NATIVE); method.getModifiers().remove(ElementModifier.NATIVE);
} }
method.getAnnotations().remove(Import.class.getName());
ProgramEmitter pe = ProgramEmitter.create(method, classHierarchy); ProgramEmitter pe = ProgramEmitter.create(method, classHierarchy);
ValueEmitter[] args = new ValueEmitter[method.parameterCount()]; ValueEmitter[] args = new ValueEmitter[method.parameterCount()];
for (int i = 0; i < args.length; ++i) { for (int i = 0; i < args.length; ++i) {

View File

@ -8,9 +8,12 @@
#include <wctype.h> #include <wctype.h>
#include <time.h> #include <time.h>
#include <uchar.h> #include <uchar.h>
#include <unistd.h>
static int8_t *wasm_heap; static int8_t *wasm_heap;
static int32_t wasm_heap_size; static int32_t wasm_heap_size;
static int wasm_args;
static char** wasm_argv;
float teavm_teavm_getNaN() { float teavm_teavm_getNaN() {
return NAN; return NAN;
@ -22,7 +25,7 @@ float teavm_teavm_getNaN() {
#define teavmMath_ceil ceil #define teavmMath_ceil ceil
#define teavmMath_floor floor #define teavmMath_floor floor
double teavm_currentTimeMillis() { int64_t teavm_currentTimeMillis() {
struct timespec time; struct timespec time;
clock_gettime(CLOCK_REALTIME, &time); clock_gettime(CLOCK_REALTIME, &time);
@ -75,3 +78,76 @@ void teavm_logString(int32_t string) {
void teavm_logInt(int32_t v) { void teavm_logInt(int32_t v) {
wprintf(L"%" PRId32, v); wprintf(L"%" PRId32, v);
} }
int32_t wasi_snapshot_preview1_clock_time_get(int32_t clock_id, int64_t precision, int32_t result_ptr) {
int64_t* resultAddr = (int64_t*) (wasm_heap + result_ptr);
*resultAddr = teavm_currentTimeMillis();
return 0;
}
int32_t wasi_snapshot_preview1_args_sizes_get(int32_t argv_size, int32_t argv_buffer_size) {
int32_t* argvSizePtr = (int32_t*) (wasm_heap + argv_size);
int32_t* argvBufferSizePtr = (int32_t*) (wasm_heap + argv_buffer_size);
*argvSizePtr = (int32_t) wasm_args;
int32_t bufferSize = 0;
for (int i = 0; i < wasm_args; ++i) {
bufferSize += (int32_t) strlen(wasm_argv[i]);
}
*argvBufferSizePtr = bufferSize;
return 0;
}
int32_t wasi_snapshot_preview1_args_get(int32_t sizes_ptr, int32_t args_ptr) {
int32_t* sizesPtr = (int32_t*) (wasm_heap + sizes_ptr);
char* argsPtr = (char*) (wasm_heap + args_ptr);
int offset = 0;
for (int i = 0; i < wasm_args; ++i) {
sizesPtr[i] = (int32_t) offset;
int len = strlen(wasm_argv[i]);
memcpy(argsPtr + offset, wasm_argv[i], len);
offset += len;
}
return 0;
}
typedef struct {
int32_t tag;
union {
struct {
int32_t name_length;
} dir;
} data;
} WasiPrestat;
int32_t wasi_snapshot_preview1_fd_prestat_get(int32_t fd, int32_t prestat_ptr) {
if (fd != 3) {
return 8;
}
WasiPrestat* prestat = (WasiPrestat*) (wasm_heap + prestat_ptr);
prestat->tag = 0;
prestat->data.dir.name_length = 1;
return 0;
}
int32_t wasi_snapshot_preview1_fd_prestat_dir_name(int32_t fd, int32_t path, int32_t path_length) {
char* pathPtr = (char*) (wasm_heap + path);
*pathPtr = '/';
return 0;
}
typedef struct {
int32_t buf;
int32_t buf_len;
} WasiIOVec;
int32_t wasi_snapshot_preview1_fd_write(int32_t fd, int32_t iovs, int32_t count, int32_t result) {
WasiIOVec* vec = (WasiIOVec*) (wasm_heap + iovs);
int32_t written = 0;
for (int32_t i = 0; i < count; ++i) {
written += write((int) fd, (char*) (wasm_heap + vec->buf), vec->buf_len);
}
int32_t* resultPtr = (int32_t*) (wasm_heap + result);
*resultPtr = written;
return 0;
}