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

@@ -4,6 +4,7 @@ const c = llvm.c;
const emit = @import("../../ir/emit_llvm.zig");
const LLVMEmitter = emit.LLVMEmitter;
const JniSlotPair = LLVMEmitter.JniSlotPair;
/// Obj-C / JNI runtime-constructor emission (architecture phase A7.3), extracted
/// from `LLVMEmitter`. A backend `*LLVMEmitter` facade (field `e`): it builds the
@@ -473,4 +474,36 @@ pub const FfiCtors = struct {
_ = i32_ty;
}
/// 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. The cache (`jni_slots`) +
/// `mangleJniKey` stay on `LLVMEmitter`.
pub fn getOrCreateJniSlots(self: FfiCtors, 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.e.alloc, "{s}\x00{s}", .{ name, sig }) catch unreachable;
if (self.e.jni_slots.get(key)) |existing| {
self.e.alloc.free(key);
return existing;
}
const mangled = self.e.mangleJniKey(name, sig);
defer self.e.alloc.free(mangled);
const cls_name = std.fmt.allocPrintSentinel(self.e.alloc, "SX_JNI_CLS_{s}", .{mangled}, 0) catch unreachable;
defer self.e.alloc.free(cls_name);
const mid_name = std.fmt.allocPrintSentinel(self.e.alloc, "SX_JNI_MID_{s}", .{mangled}, 0) catch unreachable;
defer self.e.alloc.free(mid_name);
const cls_slot = c.LLVMAddGlobal(self.e.llvm_module, self.e.cached_ptr, cls_name.ptr);
c.LLVMSetLinkage(cls_slot, c.LLVMInternalLinkage);
c.LLVMSetInitializer(cls_slot, c.LLVMConstNull(self.e.cached_ptr));
const mid_slot = c.LLVMAddGlobal(self.e.llvm_module, self.e.cached_ptr, mid_name.ptr);
c.LLVMSetLinkage(mid_slot, c.LLVMInternalLinkage);
c.LLVMSetInitializer(mid_slot, c.LLVMConstNull(self.e.cached_ptr));
const pair = JniSlotPair{ .cls_slot = cls_slot, .mid_slot = mid_slot };
self.e.jni_slots.put(key, pair) catch unreachable;
return pair;
}
};