Commit Graph

2 Commits

Author SHA1 Message Date
agra
0d883b412d ffi 1.17: #jni_call(name, sig) literal-keyed slot interning
Two `#jni_call` sites with the same string-literal `(name, sig)` pair
now share a single `jclass` GlobalRef slot and a single `jmethodID`
slot, populated lazily on the first call to any matching site.
Non-literal sites keep the per-call `GetObjectClass` + `GetMethodID`
sequence from step 1.15.

Per-call-site lowering for literal sites:

  %cached_mid = load ptr, @SX_JNI_MID_<key>
  %is_cached  = icmp ne ptr %cached_mid, null
  br i1 %is_cached, cont, miss
miss:
  %local_cls  = GetObjectClass(env, target)
  %global_cls = NewGlobalRef(env, local_cls)     ; vtable slot 21
  store ptr %global_cls, @SX_JNI_CLS_<key>
  %fresh_mid  = GetMethodID(env, global_cls, name, sig)
  store ptr %fresh_mid, @SX_JNI_MID_<key>
  br cont
cont:
  %mid = phi ptr [%cached_mid, before], [%fresh_mid, miss]
  call <Type>Method(env, target, %mid, args...)

Wiring:
- `JniMsgSend.cache_key: ?CacheKey` (new) carries `(name_str,
  sig_str)` when both `name` and `sig` are string-literal AST nodes;
  empty for non-literal call sites.
- `lower.zig` populates `cache_key` from the AST.
- `emit_llvm.zig` `getOrCreateJniSlots(name, sig)` returns the
  `{cls_slot, mid_slot}` pair, creating and caching them on first
  lookup. Key is `name\x00sig` so the separator can't collide with
  any JNI identifier byte.
- `mangleJniKey` builds an LLVM-identifier suffix from the pair, used
  in the `@SX_JNI_{CLS,MID}_<suffix>` global names.

IR snapshot at `tests/expected/ffi-jni-call-03-methodid-sharing.ir`
updated: two call sites against literal `("noop", "()V")` now share
`@SX_JNI_CLS_noop____V` and `@SX_JNI_MID_noop____V`. Pre-1.17 snapshot
had two independent `GetMethodID` calls; post-1.17 has one global
slot pair plus per-call lazy-init branches.

Note: an unrelated regression in `examples/ffi-objc-call-12-rect-u64-returns.sx`
exists in the working tree (parse error from an in-progress C-import
block) and is left untouched.
2026-05-19 22:22:55 +03:00
agra
13018ef3b4 ffi 1.16: lock in pre-caching #jni_call IR shape
Adds `examples/ffi-jni-call-03-methodid-sharing.sx` with two
`#jni_call` sites against the same (class, method, sig). Today each
site emits its own `GetObjectClass` + `GetMethodID` + `Call<Type>Method`
sequence (8 vtable indirections total for the two-call test); 1.17
will collapse the two `GetMethodID` calls into a single cached
`jmethodID` static slot populated at module init, mirroring the
`OBJC_SELECTOR_REFERENCES_*` shape that 1.5 introduced for `#objc_call`.

Runtime is a no-op — `unused_jni` is reachable through a
runtime-readable `g_should_call` global that stays false, so the JNI
dereferences never execute. A plain `if false` would get
constant-folded, taking the function definition out of the IR
entirely; the global keeps both the function and its body present
for the IR-snapshot harness.

IR snapshot at `tests/expected/ffi-jni-call-03-methodid-sharing.ir`
locks the pre-caching shape. The next commit (1.17) updates it to the
collapsed shape.

113/113 host tests pass.
2026-05-19 21:41:26 +03:00