refactor(backend): extract JNI slot cache into ffi_ctors.zig (A7.3 slice 2a)

Move getOrCreateJniSlots (the cls/methodid slot-cache builder) out of
emit_llvm.zig into the FfiCtors backend *LLVMEmitter facade. Behavior-preserving
— self.* -> self.e.* only.

- FfiCtors gains getOrCreateJniSlots (pub). The jni_slots cache + mangleJniKey
  stay on LLVMEmitter; mangleJniKey is widened to pub (the facade calls it back,
  like lazyDeclareCRuntime/emitPrivateCString), and JniSlotPair is widened to pub
  (the facade returns it; the call site consumes it). 1 call site routed via
  ffiCtors().
- emitJniConstructor intentionally NOT moved in this slice: it is emission-heavy
  (resolveRef/mapRef/coerceArg/getRefIRType/extractSlicePtr/loadJniFn/
  emitCStringGlobal — 100+ internal callers for the first two), so relocating it
  would pub-expose the emitter's core value-emission machinery. Consistent with
  A7.2 keeping emitFieldValueGet in emit_llvm.zig. Pending an explicit decision.

Gate: zig build, zig build test, bash tests/run_examples.sh -> 361/0
(JNI anchors 1402/1408/1418/1425 green, no churn).
This commit is contained in:
agra
2026-06-03 10:42:58 +03:00
parent e8c33bfc00
commit 2f7c99fd11
2 changed files with 36 additions and 34 deletions

View File

@@ -221,7 +221,7 @@ pub const LLVMEmitter = struct {
param_index: u32,
};
const JniSlotPair = struct {
pub const JniSlotPair = struct {
cls_slot: c.LLVMValueRef, // @SX_JNI_CLS_<key>: ptr (GlobalRef to jclass)
mid_slot: c.LLVMValueRef, // @SX_JNI_MID_<key>: ptr (jmethodID)
};
@@ -623,43 +623,12 @@ pub const LLVMEmitter = struct {
}
}
/// Return `{cls_slot, mid_slot}` global pair for the
/// `(name, sig)` literal — created on first lookup, shared across
/// later `#jni_call` sites with the same literal pair. Both
/// slots are zero-initialized `ptr`; the call-site lowering does
/// lazy population on first dispatch.
fn getOrCreateJniSlots(self: *LLVMEmitter, name: []const u8, sig: []const u8) JniSlotPair {
// Compose the key from name + a separator + sig. The separator
// is a byte that can't appear in a JNI method name or signature
// (NUL), so the same key never collides across distinct pairs.
const key = std.fmt.allocPrint(self.alloc, "{s}\x00{s}", .{ name, sig }) catch unreachable;
if (self.jni_slots.get(key)) |existing| {
self.alloc.free(key);
return existing;
}
const mangled = self.mangleJniKey(name, sig);
defer self.alloc.free(mangled);
const cls_name = std.fmt.allocPrintSentinel(self.alloc, "SX_JNI_CLS_{s}", .{mangled}, 0) catch unreachable;
defer self.alloc.free(cls_name);
const mid_name = std.fmt.allocPrintSentinel(self.alloc, "SX_JNI_MID_{s}", .{mangled}, 0) catch unreachable;
defer self.alloc.free(mid_name);
const cls_slot = c.LLVMAddGlobal(self.llvm_module, self.cached_ptr, cls_name.ptr);
c.LLVMSetLinkage(cls_slot, c.LLVMInternalLinkage);
c.LLVMSetInitializer(cls_slot, c.LLVMConstNull(self.cached_ptr));
const mid_slot = c.LLVMAddGlobal(self.llvm_module, self.cached_ptr, mid_name.ptr);
c.LLVMSetLinkage(mid_slot, c.LLVMInternalLinkage);
c.LLVMSetInitializer(mid_slot, c.LLVMConstNull(self.cached_ptr));
const pair = JniSlotPair{ .cls_slot = cls_slot, .mid_slot = mid_slot };
self.jni_slots.put(key, pair) catch unreachable;
return pair;
}
/// Build an LLVM-friendly identifier suffix from a JNI
/// `(method_name, signature)` pair. Non-identifier characters are
/// rewritten to `_`; the resulting string is unique per pair (the
/// caller guarantees uniqueness on `(name, sig)`, which we
/// preserve through the separator between mangled name and sig).
fn mangleJniKey(self: *LLVMEmitter, name: []const u8, sig: []const u8) []u8 {
pub fn mangleJniKey(self: *LLVMEmitter, name: []const u8, sig: []const u8) []u8 {
var buf = std.ArrayList(u8).empty;
for (name) |b| buf.append(self.alloc, if (isIdentByte(b)) b else '_') catch unreachable;
buf.appendSlice(self.alloc, "__") catch unreachable;
@@ -1894,7 +1863,7 @@ pub const LLVMEmitter = struct {
// back to the per-call `GetObjectClass + GetMethodID`
// sequence (1.15 shape).
const mid = if (msg.cache_key) |ck| blk: {
const pair = self.getOrCreateJniSlots(ck.name_str, ck.sig_str);
const pair = self.ffiCtors().getOrCreateJniSlots(ck.name_str, ck.sig_str);
const cached_mid = c.LLVMBuildLoad2(self.builder, self.cached_ptr, pair.mid_slot, "jni.cached.mid");
const is_cached = c.LLVMBuildICmp(self.builder, c.LLVMIntNE, cached_mid, c.LLVMConstNull(self.cached_ptr), "jni.is.cached");