ffi 2.16c green: TL fallback via C-helper runtime + always-omit env in #jni_call
`#jni_call` collapses to a single surface — env is *always* implicit:
either picked up from the lexically-enclosing `#jni_env(env) { ... }`
block's Ref (cheap, register-resident, no TL touch) or from the
runtime's thread-local slot via `sx_jni_env_tl_get()` (one fn call
per dispatch). The explicit-env shape is gone — chess and the
existing tests migrate cleanly by wrapping their helper-fn bodies
in `#jni_env(env) { ... }`.
The TL slot lives outside the user's IR module so the LLVM ORC JIT
can load object files cleanly without `orc_rt` for TLS support:
library/vendors/sx_jni_runtime/sx_jni_env_tl.c:
static _Thread_local void *sx_jni_env_tl_slot;
void *sx_jni_env_tl_get(void) { return sx_jni_env_tl_slot; }
void sx_jni_env_tl_set(void *env) { sx_jni_env_tl_slot = env; }
Linkage:
- sx-the-compiler links the .c file via build.zig so the JIT
process-symbol generator resolves `sx_jni_env_tl_get`/`_set`.
- AOT targets get the same .c file auto-linked via the lowering
pass: when lower touches the TL externs, it sets
`needs_jni_env_tl_runtime`, and `Compilation.lowerToIR` appends a
synthetic `CImportInfo` to `lowering_extra_c_sources` that
`collectCImportSources` merges with user-written ones.
Lowering-side changes:
- `getJniEnvTlFids` lazily declares the two externs (parallel
to `getSelRegisterNameFid`) and flips `needs_jni_env_tl_runtime`.
- `#jni_env(env) { body }` emits save→set→body→restore via three
`call` ops to the externs; the inner body sees env via the
lexical-direct stack.
- `lowerJniCall` resolves env from `jni_env_stack` (top) or the TL
fallback. The explicit-env branch is gone.
- `jni_env_stack_base` tracks per-fn lexical scope so lazy-lowering
a callee doesn't accidentally see the caller's Ref (Refs are only
valid inside one fn's instruction stream).
Test migration (mechanical):
- ffi-jni-call-{01..09}: each helper fn wraps `#jni_call(...)`
bodies in `#jni_env(env) { ... }`. Returning values pass through
the block as an expression — `#jni_env` now also lowers in
expression position.
Verified:
- zig build test + tests/run_examples.sh: 130/130 green.
- tests/cross_compile.sh: 3/3 green.
- Chess APK rebuilt + reinstalled on Pixel. Board renders with
status-bar clearance + info panel intact; no crashes in logcat.
Safe-insets dispatch through `#jni_env` + lexical-direct now
fully exercised end-to-end on real hardware.
This commit is contained in:
@@ -208,6 +208,8 @@ entry:
|
||||
%allocaN = alloca ptr, align 8
|
||||
store ptr %1, ptr %allocaN, align 8
|
||||
%load = load ptr, ptr %alloca, align 8
|
||||
%call = call ptr @sx_jni_env_tl_get()
|
||||
call void @sx_jni_env_tl_set(ptr %load)
|
||||
%loadN = load ptr, ptr %allocaN, align 8
|
||||
%jni.ifs = load ptr, ptr %load, align 8
|
||||
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_noop____V, align 8
|
||||
@@ -233,32 +235,32 @@ jni.cont: ; preds = %jni.miss, %entry
|
||||
%5 = getelementptr inbounds ptr, ptr %jni.ifs, i32 61
|
||||
%jni.callfn = load ptr, ptr %5, align 8
|
||||
call void %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid)
|
||||
%loadN = load ptr, ptr %alloca, align 8
|
||||
%loadN = load ptr, ptr %allocaN, align 8
|
||||
%jni.ifs5 = load ptr, ptr %loadN, align 8
|
||||
%jni.cached.mid6 = load ptr, ptr @SX_JNI_MID_noop____V, align 8
|
||||
%jni.is.cached7 = icmp ne ptr %jni.cached.mid6, null
|
||||
br i1 %jni.is.cached7, label %jni.cont9, label %jni.miss8
|
||||
%jni.ifs4 = load ptr, ptr %load, align 8
|
||||
%jni.cached.mid5 = load ptr, ptr @SX_JNI_MID_noop____V, align 8
|
||||
%jni.is.cached6 = icmp ne ptr %jni.cached.mid5, null
|
||||
br i1 %jni.is.cached6, label %jni.cont8, label %jni.miss7
|
||||
|
||||
jni.miss8: ; preds = %jni.cont
|
||||
%6 = getelementptr inbounds ptr, ptr %jni.ifs5, i32 31
|
||||
%jni.GetObjectClass10 = load ptr, ptr %6, align 8
|
||||
%jni.cls11 = call ptr %jni.GetObjectClass10(ptr %loadN, ptr %loadN)
|
||||
%7 = getelementptr inbounds ptr, ptr %jni.ifs5, i32 21
|
||||
%jni.NewGlobalRef12 = load ptr, ptr %7, align 8
|
||||
%jni.global.cls13 = call ptr %jni.NewGlobalRef12(ptr %loadN, ptr %jni.cls11)
|
||||
store ptr %jni.global.cls13, ptr @SX_JNI_CLS_noop____V, align 8
|
||||
%8 = getelementptr inbounds ptr, ptr %jni.ifs5, i32 33
|
||||
%jni.GetMethodID14 = load ptr, ptr %8, align 8
|
||||
%jni.fresh.mid15 = call ptr %jni.GetMethodID14(ptr %loadN, ptr %jni.global.cls13, ptr @str.2, ptr @str.3)
|
||||
store ptr %jni.fresh.mid15, ptr @SX_JNI_MID_noop____V, align 8
|
||||
br label %jni.cont9
|
||||
jni.miss7: ; preds = %jni.cont
|
||||
%6 = getelementptr inbounds ptr, ptr %jni.ifs4, i32 31
|
||||
%jni.GetObjectClass9 = load ptr, ptr %6, align 8
|
||||
%jni.cls10 = call ptr %jni.GetObjectClass9(ptr %load, ptr %loadN)
|
||||
%7 = getelementptr inbounds ptr, ptr %jni.ifs4, i32 21
|
||||
%jni.NewGlobalRef11 = load ptr, ptr %7, align 8
|
||||
%jni.global.cls12 = call ptr %jni.NewGlobalRef11(ptr %load, ptr %jni.cls10)
|
||||
store ptr %jni.global.cls12, ptr @SX_JNI_CLS_noop____V, align 8
|
||||
%8 = getelementptr inbounds ptr, ptr %jni.ifs4, i32 33
|
||||
%jni.GetMethodID13 = load ptr, ptr %8, align 8
|
||||
%jni.fresh.mid14 = call ptr %jni.GetMethodID13(ptr %load, ptr %jni.global.cls12, ptr @str.2, ptr @str.3)
|
||||
store ptr %jni.fresh.mid14, ptr @SX_JNI_MID_noop____V, align 8
|
||||
br label %jni.cont8
|
||||
|
||||
jni.cont9: ; preds = %jni.miss8, %jni.cont
|
||||
%jni.mid16 = phi ptr [ %jni.cached.mid6, %jni.cont ], [ %jni.fresh.mid15, %jni.miss8 ]
|
||||
%9 = getelementptr inbounds ptr, ptr %jni.ifs5, i32 61
|
||||
%jni.callfn17 = load ptr, ptr %9, align 8
|
||||
call void %jni.callfn17(ptr %loadN, ptr %loadN, ptr %jni.mid16)
|
||||
jni.cont8: ; preds = %jni.miss7, %jni.cont
|
||||
%jni.mid15 = phi ptr [ %jni.cached.mid5, %jni.cont ], [ %jni.fresh.mid14, %jni.miss7 ]
|
||||
%9 = getelementptr inbounds ptr, ptr %jni.ifs4, i32 61
|
||||
%jni.callfn16 = load ptr, ptr %9, align 8
|
||||
call void %jni.callfn16(ptr %load, ptr %loadN, ptr %jni.mid15)
|
||||
call void @sx_jni_env_tl_set(ptr %call)
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -316,6 +318,10 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @sx_jni_env_tl_get() #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare void @sx_jni_env_tl_set(ptr) #0
|
||||
|
||||
declare i64 @write(i32, ptr, i64)
|
||||
|
||||
|
||||
|
||||
@@ -206,6 +206,8 @@ entry:
|
||||
%allocaN = alloca ptr, align 8
|
||||
store ptr %1, ptr %allocaN, align 8
|
||||
%load = load ptr, ptr %alloca, align 8
|
||||
%call = call ptr @sx_jni_env_tl_get()
|
||||
call void @sx_jni_env_tl_set(ptr %load)
|
||||
%loadN = load ptr, ptr %allocaN, align 8
|
||||
%jni.ifs = load ptr, ptr %load, align 8
|
||||
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_getCount____I, align 8
|
||||
@@ -231,6 +233,7 @@ jni.cont: ; preds = %jni.miss, %entry
|
||||
%5 = getelementptr inbounds ptr, ptr %jni.ifs, i32 49
|
||||
%jni.callfn = load ptr, ptr %5, align 8
|
||||
%jni.ret = call i32 %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid)
|
||||
call void @sx_jni_env_tl_set(ptr %call)
|
||||
ret i32 %jni.ret
|
||||
}
|
||||
|
||||
@@ -290,6 +293,10 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @sx_jni_env_tl_get() #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare void @sx_jni_env_tl_set(ptr) #0
|
||||
|
||||
declare i64 @write(i32, ptr, i64)
|
||||
|
||||
|
||||
|
||||
@@ -206,6 +206,8 @@ entry:
|
||||
%allocaN = alloca ptr, align 8
|
||||
store ptr %1, ptr %allocaN, align 8
|
||||
%load = load ptr, ptr %alloca, align 8
|
||||
%call = call ptr @sx_jni_env_tl_get()
|
||||
call void @sx_jni_env_tl_set(ptr %load)
|
||||
%loadN = load ptr, ptr %allocaN, align 8
|
||||
%jni.ifs = load ptr, ptr %load, align 8
|
||||
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_currentTimeMillis____J, align 8
|
||||
@@ -231,6 +233,7 @@ jni.cont: ; preds = %jni.miss, %entry
|
||||
%5 = getelementptr inbounds ptr, ptr %jni.ifs, i32 52
|
||||
%jni.callfn = load ptr, ptr %5, align 8
|
||||
%jni.ret = call i64 %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid)
|
||||
call void @sx_jni_env_tl_set(ptr %call)
|
||||
ret i64 %jni.ret
|
||||
}
|
||||
|
||||
@@ -290,6 +293,10 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @sx_jni_env_tl_get() #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare void @sx_jni_env_tl_set(ptr) #0
|
||||
|
||||
declare i64 @write(i32, ptr, i64)
|
||||
|
||||
|
||||
|
||||
@@ -206,6 +206,8 @@ entry:
|
||||
%allocaN = alloca ptr, align 8
|
||||
store ptr %1, ptr %allocaN, align 8
|
||||
%load = load ptr, ptr %alloca, align 8
|
||||
%call = call ptr @sx_jni_env_tl_get()
|
||||
call void @sx_jni_env_tl_set(ptr %load)
|
||||
%loadN = load ptr, ptr %allocaN, align 8
|
||||
%jni.ifs = load ptr, ptr %load, align 8
|
||||
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_getValue____D, align 8
|
||||
@@ -231,6 +233,7 @@ jni.cont: ; preds = %jni.miss, %entry
|
||||
%5 = getelementptr inbounds ptr, ptr %jni.ifs, i32 58
|
||||
%jni.callfn = load ptr, ptr %5, align 8
|
||||
%jni.ret = call double %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid)
|
||||
call void @sx_jni_env_tl_set(ptr %call)
|
||||
ret double %jni.ret
|
||||
}
|
||||
|
||||
@@ -290,6 +293,10 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @sx_jni_env_tl_get() #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare void @sx_jni_env_tl_set(ptr) #0
|
||||
|
||||
declare i64 @write(i32, ptr, i64)
|
||||
|
||||
|
||||
|
||||
@@ -206,6 +206,8 @@ entry:
|
||||
%allocaN = alloca ptr, align 8
|
||||
store ptr %1, ptr %allocaN, align 8
|
||||
%load = load ptr, ptr %alloca, align 8
|
||||
%call = call ptr @sx_jni_env_tl_get()
|
||||
call void @sx_jni_env_tl_set(ptr %load)
|
||||
%loadN = load ptr, ptr %allocaN, align 8
|
||||
%jni.ifs = load ptr, ptr %load, align 8
|
||||
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_isShown____Z, align 8
|
||||
@@ -231,6 +233,7 @@ jni.cont: ; preds = %jni.miss, %entry
|
||||
%5 = getelementptr inbounds ptr, ptr %jni.ifs, i32 37
|
||||
%jni.callfn = load ptr, ptr %5, align 8
|
||||
%jni.ret = call i1 %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid)
|
||||
call void @sx_jni_env_tl_set(ptr %call)
|
||||
ret i1 %jni.ret
|
||||
}
|
||||
|
||||
@@ -290,6 +293,10 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @sx_jni_env_tl_get() #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare void @sx_jni_env_tl_set(ptr) #0
|
||||
|
||||
declare i64 @write(i32, ptr, i64)
|
||||
|
||||
|
||||
|
||||
@@ -206,6 +206,8 @@ entry:
|
||||
%allocaN = alloca ptr, align 8
|
||||
store ptr %1, ptr %allocaN, align 8
|
||||
%load = load ptr, ptr %alloca, align 8
|
||||
%call = call ptr @sx_jni_env_tl_get()
|
||||
call void @sx_jni_env_tl_set(ptr %load)
|
||||
%loadN = load ptr, ptr %allocaN, align 8
|
||||
%jni.ifs = load ptr, ptr %load, align 8
|
||||
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_getWindow____Landroid_view_Window_, align 8
|
||||
@@ -231,6 +233,7 @@ jni.cont: ; preds = %jni.miss, %entry
|
||||
%5 = getelementptr inbounds ptr, ptr %jni.ifs, i32 34
|
||||
%jni.callfn = load ptr, ptr %5, align 8
|
||||
%jni.ret = call ptr %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid)
|
||||
call void @sx_jni_env_tl_set(ptr %call)
|
||||
ret ptr %jni.ret
|
||||
}
|
||||
|
||||
@@ -290,6 +293,10 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @sx_jni_env_tl_get() #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare void @sx_jni_env_tl_set(ptr) #0
|
||||
|
||||
declare i64 @write(i32, ptr, i64)
|
||||
|
||||
|
||||
|
||||
@@ -206,6 +206,8 @@ entry:
|
||||
%allocaN = alloca ptr, align 8
|
||||
store ptr %1, ptr %allocaN, align 8
|
||||
%load = load ptr, ptr %alloca, align 8
|
||||
%call = call ptr @sx_jni_env_tl_get()
|
||||
call void @sx_jni_env_tl_set(ptr %load)
|
||||
%loadN = load ptr, ptr %allocaN, align 8
|
||||
%jni.ifs = load ptr, ptr %load, align 8
|
||||
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_max___II_I, align 8
|
||||
@@ -228,6 +230,7 @@ jni.cont: ; preds = %jni.miss, %entry
|
||||
%4 = getelementptr inbounds ptr, ptr %jni.ifs, i32 129
|
||||
%jni.callfn = load ptr, ptr %4, align 8
|
||||
%jni.ret = call i32 %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid, i32 3, i32 7)
|
||||
call void @sx_jni_env_tl_set(ptr %call)
|
||||
ret i32 %jni.ret
|
||||
}
|
||||
|
||||
@@ -287,6 +290,10 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @sx_jni_env_tl_get() #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare void @sx_jni_env_tl_set(ptr) #0
|
||||
|
||||
declare i64 @write(i32, ptr, i64)
|
||||
|
||||
|
||||
|
||||
@@ -206,6 +206,8 @@ entry:
|
||||
%allocaN = alloca ptr, align 8
|
||||
store ptr %1, ptr %allocaN, align 8
|
||||
%load = load ptr, ptr %alloca, align 8
|
||||
%call = call ptr @sx_jni_env_tl_get()
|
||||
call void @sx_jni_env_tl_set(ptr %load)
|
||||
%loadN = load ptr, ptr %allocaN, align 8
|
||||
%jni.ifs = load ptr, ptr %load, align 8
|
||||
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_getWindow____Ljava_lang_Object_, align 8
|
||||
@@ -233,6 +235,7 @@ jni.cont: ; preds = %jni.miss, %entry
|
||||
%jni.ret = call ptr %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid)
|
||||
%allocaN = alloca ptr, align 8
|
||||
store ptr %jni.ret, ptr %allocaN, align 8
|
||||
call void @sx_jni_env_tl_set(ptr %call)
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -290,4 +293,10 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @sx_jni_env_tl_get() #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare void @sx_jni_env_tl_set(ptr) #0
|
||||
|
||||
declare i64 @write(i32, ptr, i64)
|
||||
|
||||
@@ -206,6 +206,8 @@ entry:
|
||||
%allocaN = alloca ptr, align 8
|
||||
store ptr %1, ptr %allocaN, align 8
|
||||
%load = load ptr, ptr %alloca, align 8
|
||||
%call = call ptr @sx_jni_env_tl_get()
|
||||
call void @sx_jni_env_tl_set(ptr %load)
|
||||
%loadN = load ptr, ptr %allocaN, align 8
|
||||
%jni.ifs = load ptr, ptr %load, align 8
|
||||
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_noop____V, align 8
|
||||
@@ -231,6 +233,7 @@ jni.cont: ; preds = %jni.miss, %entry
|
||||
%5 = getelementptr inbounds ptr, ptr %jni.ifs, i32 61
|
||||
%jni.callfn = load ptr, ptr %5, align 8
|
||||
call void %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid)
|
||||
call void @sx_jni_env_tl_set(ptr %call)
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -288,4 +291,10 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @sx_jni_env_tl_get() #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare void @sx_jni_env_tl_set(ptr) #0
|
||||
|
||||
declare i64 @write(i32, ptr, i64)
|
||||
|
||||
@@ -1 +1 @@
|
||||
1
|
||||
0
|
||||
|
||||
@@ -1,14 +1 @@
|
||||
LLVM verification failed: Load operand must be a pointer.
|
||||
%jni.ifs = load ptr, i64 undef, align 8
|
||||
Call parameter type does not match function signature!
|
||||
i64 undef
|
||||
ptr %jni.cls = call ptr %jni.GetObjectClass(i64 undef, ptr %load)
|
||||
Call parameter type does not match function signature!
|
||||
i64 undef
|
||||
ptr %jni.global.cls = call ptr %jni.NewGlobalRef(i64 undef, ptr %jni.cls)
|
||||
Call parameter type does not match function signature!
|
||||
i64 undef
|
||||
ptr %jni.fresh.mid = call ptr %jni.GetMethodID(i64 undef, ptr %jni.global.cls, ptr @str, ptr @str.1)
|
||||
Call parameter type does not match function signature!
|
||||
i64 undef
|
||||
ptr call void %jni.callfn(i64 undef, ptr %load, ptr %jni.mid)
|
||||
ok
|
||||
|
||||
Reference in New Issue
Block a user