ffi 1.15: #jni_call(void) codegen — make-green
New `.jni_msg_send` IR opcode carrying `{env, target, name, sig,
args[], is_static}`. `lowerFfiIntrinsicCall` now dispatches on
`fic.kind`: `.objc_call` keeps the existing path; `.jni_call` and
`.jni_static_call` route through `lowerJniCall`, which emits the new
opcode.
emit_llvm.zig expands `.jni_msg_send` into the JNI vtable
indirection:
%ifs = load ptr, %env ; vtable
%get_obj_class = load ptr, gep(%ifs, i32 31)
%cls = call ptr %get_obj_class(%env, %target)
%get_method_id = load ptr, gep(%ifs, i32 33)
%mid = call ptr %get_method_id(%env, %cls, %name, %sig)
%call_void_method = load ptr, gep(%ifs, i32 61)
call void %call_void_method(%env, %target, %mid, args...)
Per step 1.15's scope: only `.jni_call` (instance) + `void` return
are wired through the switch. `.jni_static_call` (1.23) and the
non-void returns (1.18–1.22) drop to a placeholder `LLVMGetUndef` so
the build doesn't fault — the next-step commits flip those arms one
shape at a time. Method-ID caching is step 1.17.
Two small helpers landed alongside:
- `loadJniFn(ifs, offset, name)` — GEP into the vtable + load.
- `extractSlicePtr(val)` — string literals lower as `{ptr, i64}`
slices in sx IR; JNI's `GetMethodID` expects raw C strings, so
this extracts field 0 when the source is a slice.
Android cross-compile now passes for `examples/ffi-jni-call-02-void.sx`
(2/2 cross targets green). Host run_examples still passes 112/112.
Chess iOS-sim + Android both compile clean.
This commit is contained in:
@@ -3790,11 +3790,8 @@ pub const Lowering = struct {
|
||||
/// fully wired. Extra arities + non-void returns will land in
|
||||
/// subsequent phase-1 steps.
|
||||
fn lowerFfiIntrinsicCall(self: *Lowering, fic: *const ast.FfiIntrinsicCall) Ref {
|
||||
if (fic.kind != .objc_call) {
|
||||
if (self.diagnostics) |d| {
|
||||
d.add(.err, "#jni_call / #jni_static_call lowering not implemented yet (Phase 1.15+)", null);
|
||||
}
|
||||
return Ref.none;
|
||||
if (fic.kind == .jni_call or fic.kind == .jni_static_call) {
|
||||
return self.lowerJniCall(fic);
|
||||
}
|
||||
|
||||
if (fic.args.len < 2) {
|
||||
@@ -3855,6 +3852,37 @@ pub const Lowering = struct {
|
||||
} }, ret_ty);
|
||||
}
|
||||
|
||||
fn lowerJniCall(self: *Lowering, fic: *const ast.FfiIntrinsicCall) Ref {
|
||||
if (fic.args.len < 4) {
|
||||
if (self.diagnostics) |d| {
|
||||
d.add(.err, "#jni_call requires env, target, method name, and signature", null);
|
||||
}
|
||||
return Ref.none;
|
||||
}
|
||||
|
||||
const ret_ty = self.resolveType(fic.return_type);
|
||||
const env_ref = self.lowerExpr(fic.args[0]);
|
||||
const target_ref = self.lowerExpr(fic.args[1]);
|
||||
const name_ref = self.lowerExpr(fic.args[2]);
|
||||
const sig_ref = self.lowerExpr(fic.args[3]);
|
||||
|
||||
var extra = std.ArrayList(Ref).empty;
|
||||
var ai: usize = 4;
|
||||
while (ai < fic.args.len) : (ai += 1) {
|
||||
extra.append(self.alloc, self.lowerExpr(fic.args[ai])) catch unreachable;
|
||||
}
|
||||
const extra_owned = extra.toOwnedSlice(self.alloc) catch unreachable;
|
||||
|
||||
return self.builder.emit(.{ .jni_msg_send = .{
|
||||
.env = env_ref,
|
||||
.target = target_ref,
|
||||
.name = name_ref,
|
||||
.sig = sig_ref,
|
||||
.args = extra_owned,
|
||||
.is_static = fic.kind == .jni_static_call,
|
||||
} }, ret_ty);
|
||||
}
|
||||
|
||||
// ── Calls ───────────────────────────────────────────────────────
|
||||
|
||||
fn lowerCall(self: *Lowering, c: *const ast.Call) Ref {
|
||||
|
||||
Reference in New Issue
Block a user