mirror of
https://github.com/HeyPuter/gex.git
synced 2026-11-13 20:12:43 -10:00
unity-dotnet: wasm JIT function pointers are the interpreter's (patch 0005)
gex runs Mono interpreter-only, so the wasm JIT's mono_ldftn and ldvirtfn_internal returned create_method_pointer's native entry; the delegate .ctor the JIT calls runs on the interpreter (ves_icall_mono_delegate_ctor_interp), which takes function pointers as InterpMethod* and read a NULL method there: Hollow Knight asserted in tk2dSpriteAnimator.Start with the JIT on. Under HOST_GEX both return get_interp_method (bit 0 set when the receiver must be unboxed, as MINT_LDVIRTFTN), except native-to-managed wrappers. The embed test gains T.Delegates (ldftn, ldvirtftn, a boxed struct's override, a closure, made in JIT-compiled code at threshold 1): the provider without the patch dies there (exit 139), with it every pattern holds. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_1896c5b302a54696aaa92c5f4047f20b
This commit is contained in:
1 parent
0308e2e0cf
commit
3302db70fe
4 files changed
+66
-1
No files matched your search
@@ -230,6 +230,12 @@ main (int argc, char **argv)
|
||||
P clr = mono_runtime_invoke (clears, NULL, clargs, &exc);
|
||||
check ("Clears", exc);
|
||||
printf ("%s\n", mono_string_to_utf8 (clr));
|
||||
P dels = mono_class_get_method_from_name (k, "Delegates", 1);
|
||||
int dn = 40;
|
||||
void *dargs [1] = { &dn };
|
||||
P dr = mono_runtime_invoke (dels, NULL, dargs, &exc);
|
||||
check ("Delegates", exc);
|
||||
printf ("%s\n", mono_string_to_utf8 (dr));
|
||||
/* The liveness walk Unity's UnloadUnusedAssets runs: every Leaf reachable from the root,
|
||||
* each reported once, in batches of at most 16. */
|
||||
P lvg = mono_class_get_method_from_name (k, "LvGraph", 1);
|
||||
|
||||
@@ -27,7 +27,7 @@ PATTERNS=("Compute(50) = 50002" "Hello = hello gex from t ptr=8"
|
||||
"Guest = twice 42; concat guest strings; mix 323.25; scale 2,4,6; big 11,25,52,3; caught from the guest"
|
||||
"Back = callback 806.50; unmanaged 1015" "Work(200) = 24983091"
|
||||
"held across collections: 64 of 64 intact" "worker thread: 0 bad of 640"
|
||||
"clears 643296" "liveness: 301 leaves, 0 reported twice, batched" "gc ok")
|
||||
"clears 643296" "delegates 24660 tag0tag1tag2" "liveness: 301 leaves, 0 reported twice, batched" "gc ok")
|
||||
for p in "${PATTERNS[@]}"; do grep -a -q -x -F "$p" out.txt || { echo "FAIL: no '$p'"; exit 1; }; done
|
||||
# The same under the function JIT, which must leave the replaced library's own code alone: it once
|
||||
# followed the PLT into the stub ("x86 Mono stub ... called"), as it followed Muck's into Boehm's GC.
|
||||
|
||||
@@ -38,6 +38,27 @@ public static class T {
|
||||
}
|
||||
return "clears " + sum;
|
||||
}
|
||||
// Delegates made in code the wasm JIT compiled (ldftn, ldvirtftn, a boxed struct's override, a
|
||||
// closure): their constructor runs on the interpreter, which takes the JIT's function pointers as
|
||||
// its own (Hollow Knight's tk2dSpriteAnimator.Start read a NULL method from a native entry).
|
||||
public class DShape { public int k; public virtual int Area(int x) { return x * k; } public int Plain(int x) { return x + k; } }
|
||||
public class DSquare : DShape { public override int Area(int x) { return x * x + k; } }
|
||||
public struct Tag { public int v; public override string ToString() { return "tag" + v; } }
|
||||
static int DSq(int x) { return x * x; }
|
||||
public static string Delegates(int n) {
|
||||
long sum = 0; string tags = "";
|
||||
for (int i = 0; i < n; i++) {
|
||||
Func<int, int> st = DSq;
|
||||
DShape s = i % 2 == 0 ? new DShape { k = i } : new DSquare { k = i };
|
||||
Func<int, int> pl = s.Plain, vi = s.Area;
|
||||
object o = new Tag { v = i };
|
||||
Func<string> ts = o.ToString;
|
||||
int c = i; Func<int, int> cl = x => x + c;
|
||||
sum += st(i) + pl(3) + vi(4) + cl(5);
|
||||
if (i < 3) tags += ts();
|
||||
}
|
||||
return "delegates " + sum + " " + tags;
|
||||
}
|
||||
public static object LvGraph(int n) {
|
||||
var r = new LvRoot { arr = new object[n], pairs = new Pair[n], list = new List<Leaf>(), s = "not a leaf" };
|
||||
for (int i = 0; i < n; i++) r.arr[i] = i % 2 == 0 ? new SubLeaf { id = i } : new Leaf { id = i };
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
diff --git a/src/mono/mono/mini/jit-icalls.c b/src/mono/mono/mini/jit-icalls.c
|
||||
index 2f18c3115..2c782536b 100644
|
||||
--- a/src/mono/mono/mini/jit-icalls.c
|
||||
+++ b/src/mono/mono/mini/jit-icalls.c
|
||||
@@ -157,6 +157,16 @@ mono_ldftn (MonoMethod *method)
|
||||
}
|
||||
}
|
||||
|
||||
+#ifdef HOST_GEX
|
||||
+ /* gex runs Mono interpreter-only (MONO_AOT_MODE_INTERP_ONLY, not llvm-only); the only mini-compiled code is
|
||||
+ * the wasm JIT's. A function pointer it loads goes to the interpreter (a delegate .ctor the JIT calls through
|
||||
+ * interp_entry, ves_icall_mono_delegate_ctor_interp; the JIT bails on raw calli), which takes function
|
||||
+ * pointers as InterpMethod* (its MINT_LDFTN). create_method_pointer's native entry read as an InterpMethod
|
||||
+ * gave a NULL method there (Hollow Knight, tk2dSpriteAnimator.Start). */
|
||||
+ if (mono_use_interpreter && method->wrapper_type != MONO_WRAPPER_NATIVE_TO_MANAGED)
|
||||
+ return mini_get_interp_callbacks ()->get_interp_method (method);
|
||||
+#endif
|
||||
+
|
||||
/* if we need the address of a native-to-managed wrapper, just compile it now, trampoline needs thread local
|
||||
* variables that won't be there if we run on a thread that's not attached yet. */
|
||||
if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
|
||||
@@ -211,6 +221,16 @@ ldvirtfn_internal (MonoObject *obj, MonoMethod *method, gboolean gshared)
|
||||
|
||||
/* An rgctx wrapper is added by the trampolines no need to do it here */
|
||||
gboolean need_unbox = m_class_is_valuetype (res->klass) && !m_class_is_valuetype (method->klass);
|
||||
+#ifdef HOST_GEX
|
||||
+ /* As mono_ldftn: the interpreter's function pointer (its MINT_LDVIRTFTN: the InterpMethod*, bit 0 set when
|
||||
+ * the receiver must be unboxed). */
|
||||
+ if (mono_use_interpreter) {
|
||||
+ gpointer im = mini_get_interp_callbacks ()->get_interp_method (res);
|
||||
+ addr = need_unbox ? (gpointer)((gsize)im | 1) : im;
|
||||
+ ldvirtfn_cache_write (cache, method, gshared, addr);
|
||||
+ return addr;
|
||||
+ }
|
||||
+#endif
|
||||
if (need_unbox) {
|
||||
/*
|
||||
* We can't return a jump trampoline here, because the trampoline code
|
||||
Reference in new issue
Block a user