mirror of
https://github.com/HeyPuter/gex.git
synced 2026-11-13 20:12:43 -10:00
unity-dotnet wasm JIT: a negative 32-bit constant is sign-extended as a native int (patch 0004; Hollow Knight's fault in SpanHelpers.ClearWithReferences); embed checks Array.Clear/Span.Clear under the JIT
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_d2453757b8a84fdbb4ee7c9e093141df
This commit is contained in:
1 parent
5c16a1400b
commit
a1b2f22f95
4 files changed
+64
-1
No files matched your search
@@ -224,6 +224,12 @@ main (int argc, char **argv)
|
||||
}
|
||||
pthread_join (t, NULL);
|
||||
printf ("worker thread: %d bad of 640\n", worker_bad);
|
||||
P clears = mono_class_get_method_from_name (k, "Clears", 1);
|
||||
int cn = 300;
|
||||
void *clargs [1] = { &cn };
|
||||
P clr = mono_runtime_invoke (clears, NULL, clargs, &exc);
|
||||
check ("Clears", exc);
|
||||
printf ("%s\n", mono_string_to_utf8 (clr));
|
||||
/* 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);
|
||||
|
||||
@@ -26,7 +26,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"
|
||||
"liveness: 301 leaves, 0 reported twice, batched" "gc ok")
|
||||
"clears 643296" "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.
|
||||
|
||||
@@ -16,6 +16,28 @@ public class LvRoot { public object[] arr; public Pair p; public Pair[] pairs; p
|
||||
public static class T {
|
||||
// 3n+1 distinct Leafs: n in arr (half SubLeaf, each .next the following one, a cycle), one in
|
||||
// p.b, n in pairs[i].b (pairs[i].a repeats arr[i]), n in list.
|
||||
// Array.Clear and Span.Clear over reference, value and mixed element types, many offsets
|
||||
// (Hollow Knight's first wasm-JIT fault was in SpanHelpers.ClearWithReferences under Array.Clear).
|
||||
public struct Mixed { public object o; public long v; }
|
||||
public static string Clears(int n) {
|
||||
long sum = 0;
|
||||
for (int len = 1; len <= n; len += 7) {
|
||||
var a = new object[len]; for (int i = 0; i < len; i++) a[i] = i;
|
||||
int from = len / 3, cnt = len - from - len / 4;
|
||||
Array.Clear(a, from, cnt);
|
||||
for (int i = 0; i < len; i++) if (a[i] == null) sum += i + 1;
|
||||
var b = new int[len]; for (int i = 0; i < len; i++) b[i] = i + 1;
|
||||
Array.Clear(b, from, cnt);
|
||||
for (int i = 0; i < len; i++) sum += b[i];
|
||||
var c = new Mixed[len]; for (int i = 0; i < len; i++) c[i] = new Mixed { o = "s", v = i };
|
||||
Array.Clear(c, from, cnt);
|
||||
for (int i = 0; i < len; i++) if (c[i].o == null) sum += 3 * c[i].v + 1;
|
||||
var l = new List<string>(); for (int i = 0; i < len; i++) l.Add("x"); l.Clear(); sum += l.Count;
|
||||
new Span<object>(a).Slice(len / 2).Clear();
|
||||
for (int i = 0; i < len; i++) if (a[i] == null) sum += 1;
|
||||
}
|
||||
return "clears " + sum;
|
||||
}
|
||||
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,35 @@
|
||||
diff --git a/src/mono/mono/mini/mini-wasm-emitter.inc b/src/mono/mono/mini/mini-wasm-emitter.inc
|
||||
index 9919357ca..11bfda8b6 100644
|
||||
--- a/src/mono/mono/mini/mini-wasm-emitter.inc
|
||||
+++ b/src/mono/mono/mini/mini-wasm-emitter.inc
|
||||
@@ -635,6 +635,15 @@ mono_wasm_emit_method (MonoCompile *cfg)
|
||||
if (wj_ins_is_move (ins->opcode))
|
||||
continue;
|
||||
rt = wasm_valtype_of_opcode (ins->opcode);
|
||||
+#if WJ_W64
|
||||
+ /* A negative 32-bit constant is sign-extended in its 64-bit register (inst_c0 is the 64-bit value,
|
||||
+ * and mini-amd64 moves it as one), and mono relies on it: conv.i of a constant is folded away, so
|
||||
+ * `Unsafe.Add (ref p, -1)` reads the ICONST as a native int. As an i32 local, read by an i64 op,
|
||||
+ * it would be zero-extended (0xffffffff): SpanHelpers.ClearWithReferences wrote 32 GiB past its
|
||||
+ * span (Hollow Knight). Such a constant lives in an i64 local; 32-bit readers wrap it. */
|
||||
+ if (ins->opcode == OP_ICONST && (gint64) ins->inst_c0 < 0 && !(ins->type == STACK_OBJ && ins->inst_p0))
|
||||
+ rt = WASM_I64;
|
||||
+#endif
|
||||
if (!rt && (ins->opcode == OP_CALL || ins->opcode == OP_FCALL || ins->opcode == OP_LCALL || ins->opcode == OP_RCALL
|
||||
|| ins->opcode == OP_CALL_REG || ins->opcode == OP_FCALL_REG || ins->opcode == OP_LCALL_REG || ins->opcode == OP_RCALL_REG
|
||||
|| ins->opcode == OP_CALL_MEMBASE || ins->opcode == OP_FCALL_MEMBASE || ins->opcode == OP_LCALL_MEMBASE || ins->opcode == OP_RCALL_MEMBASE)) {
|
||||
@@ -3704,6 +3713,14 @@ mono_wasm_emit_method (MonoCompile *cfg)
|
||||
if (!wasm_st_from (&body, &lc, ins->dreg, WASM_PTR)) { fail = "iconst"; goto done; }
|
||||
break;
|
||||
} else {
|
||||
+#if WJ_W64
|
||||
+ /* the 64-bit register's value (see "negative 32-bit constant" in the valtype pass) */
|
||||
+ if (ins->dreg >= 0 && ins->dreg < lc.nvreg && lc.vt [ins->dreg] == WASM_I64) {
|
||||
+ wasm_i64_const (&body, (gint64) ins->inst_c0);
|
||||
+ if (!wasm_st (&body, &lc, ins->dreg)) { fail = "iconst"; goto done; }
|
||||
+ break;
|
||||
+ }
|
||||
+#endif
|
||||
wasm_i32_const (&body, (gint32) ins->inst_c0);
|
||||
}
|
||||
if (!wasm_st_from (&body, &lc, ins->dreg, WASM_I32)) { fail = "iconst"; goto done; }
|
||||
Reference in new issue
Block a user