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:
51
src/core.zig
51
src/core.zig
@@ -28,6 +28,10 @@ pub const Compilation = struct {
|
||||
import_graph: std.StringHashMap(std.StringHashMap(void)),
|
||||
sema_result: ?sema.SemaResult = null,
|
||||
ir_emitter: ?ir.LLVMEmitter = null,
|
||||
/// C sources requested by the lowering pass (not in the user's AST).
|
||||
/// E.g. the JNI env TL runtime when `#jni_env` is used. Merged with
|
||||
/// AST sources in `collectCImportSources`.
|
||||
lowering_extra_c_sources: std.ArrayList(c_import.CImportInfo) = .empty,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, io: std.Io, file_path: []const u8, source: [:0]const u8, target_config: TargetConfig, stdlib_paths: []const []const u8) Compilation {
|
||||
return .{
|
||||
@@ -145,10 +149,36 @@ pub const Compilation = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Collect C import source info from the resolved AST.
|
||||
/// Collect C import source info — both from user-written `#import c { ... }`
|
||||
/// blocks in the AST AND from lowering-time auto-injections (currently:
|
||||
/// the JNI env TL runtime when `#jni_env` / `#jni_call`-with-omitted-env
|
||||
/// is used). The lower-side auto-injections live in
|
||||
/// `lowering_extra_c_sources` and are populated by `lowerToIR` based on
|
||||
/// `Lowering.needs_jni_env_tl_runtime` etc.
|
||||
pub fn collectCImportSources(self: *Compilation) ![]c_import.CImportInfo {
|
||||
const root = self.resolved_root orelse self.root orelse return &.{};
|
||||
return c_import.collectCImportSources(self.allocator, root);
|
||||
const ast_sources = try c_import.collectCImportSources(self.allocator, root);
|
||||
if (self.lowering_extra_c_sources.items.len == 0) return ast_sources;
|
||||
var merged = std.ArrayList(c_import.CImportInfo).empty;
|
||||
try merged.appendSlice(self.allocator, ast_sources);
|
||||
try merged.appendSlice(self.allocator, self.lowering_extra_c_sources.items);
|
||||
return merged.toOwnedSlice(self.allocator);
|
||||
}
|
||||
|
||||
/// Resolve a stdlib-relative path through the configured `stdlib_paths`.
|
||||
/// Returns the first candidate whose absolute path resolves to an
|
||||
/// existing file. Used by lower-side auto-injected C sources.
|
||||
fn resolveStdlibPath(self: *Compilation, rel: []const u8) !?[]const u8 {
|
||||
for (self.stdlib_paths) |root_path| {
|
||||
const candidate = try std.fmt.allocPrint(self.allocator, "{s}/{s}", .{ root_path, rel });
|
||||
if (std.Io.Dir.readFileAlloc(.cwd(), self.io, candidate, self.allocator, .limited(1024 * 1024))) |buf| {
|
||||
self.allocator.free(buf);
|
||||
return candidate;
|
||||
} else |_| {
|
||||
self.allocator.free(candidate);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Lower the parsed AST to the sx IR module (shadow pipeline).
|
||||
@@ -168,6 +198,23 @@ pub const Compilation = struct {
|
||||
lowering.import_graph = &self.import_graph;
|
||||
lowering.lowerRoot(root);
|
||||
if (self.diagnostics.hasErrors()) return error.CompileError;
|
||||
|
||||
// Auto-link the JNI env TL runtime when lowering used it. The .c file
|
||||
// ships with the sx library; we resolve it through stdlib_paths so
|
||||
// consumers don't need to vendor a copy.
|
||||
if (lowering.needs_jni_env_tl_runtime) {
|
||||
if (try self.resolveStdlibPath("vendors/sx_jni_runtime/sx_jni_env_tl.c")) |abs_path| {
|
||||
var sources = std.ArrayList([]const u8).empty;
|
||||
try sources.append(self.allocator, abs_path);
|
||||
try self.lowering_extra_c_sources.append(self.allocator, .{
|
||||
.sources = try sources.toOwnedSlice(self.allocator),
|
||||
.includes = &.{},
|
||||
.defines = &.{},
|
||||
.flags = &.{},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user