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:
agra
2026-05-20 13:53:25 +03:00
parent 013cf9f1bb
commit 6a3260ff65
26 changed files with 330 additions and 96 deletions

View File

@@ -27,6 +27,15 @@ pub fn build(b: *std.Build) void {
.file = b.path("llvm_shim.c"),
.flags = &.{b.fmt("-I{s}", .{include_dir})},
});
// FFI step 2.16c runtime — `_Thread_local`-backed JNIEnv* slot.
// Linked into sx-the-compiler so the JIT process-symbol generator
// can resolve `sx_jni_env_tl_get` / `sx_jni_env_tl_set` without the
// user importing the runtime module. AOT outputs pick up the same
// .c file via lower-side auto-injected c_import.
mod.addCSourceFile(.{
.file = b.path("library/vendors/sx_jni_runtime/sx_jni_env_tl.c"),
.flags = &.{},
});
mod.addCSourceFile(.{
.file = b.path("clang_shim.cpp"),
.flags = &.{

View File

@@ -10,14 +10,17 @@
main :: () -> s32 {
inline if false {
// Instance method: env, target, name, sig, args...
#jni_call(*void)(null, null, "getWindow", "()Landroid/view/Window;");
env : *void = null;
#jni_env(env) {
// Instance method: target, name, sig, args...
#jni_call(*void)(null, "getWindow", "()Landroid/view/Window;");
// Static method: env, class, name, sig, args...
#jni_static_call(s32)(null, null, "max", "(II)I", 3, 7);
// Static method: class, name, sig, args...
#jni_static_call(s32)(null, "max", "(II)I", 3, 7);
// Returning a Java primitive (jboolean → sx bool).
#jni_call(bool)(null, null, "isShown", "()Z");
// Returning a Java primitive (jboolean → sx bool).
#jni_call(bool)(null, "isShown", "()Z");
}
}
print("parse-only ok\n");
0;

View File

@@ -31,7 +31,9 @@ main :: () -> s32 {
// sx_android_jni.c lands.
env : *void = null;
target : *void = null;
#jni_call(void)(env, target, "noop", "()V");
#jni_env(env) {
#jni_call(void)(target, "noop", "()V");
}
}
inline if OS != .android {
print("skipped (not android)\n");

View File

@@ -17,8 +17,10 @@
g_should_call : bool = false;
unused_jni :: (env: *void, target: *void) {
#jni_call(void)(env, target, "noop", "()V");
#jni_call(void)(env, target, "noop", "()V");
#jni_env(env) {
#jni_call(void)(target, "noop", "()V");
#jni_call(void)(target, "noop", "()V");
}
}
main :: () -> s32 {

View File

@@ -14,7 +14,9 @@
g_should_call : bool = false;
read_int :: (env: *void, target: *void) -> s32 {
#jni_call(s32)(env, target, "getCount", "()I");
#jni_env(env) {
#jni_call(s32)(target, "getCount", "()I");
}
}
main :: () -> s32 {

View File

@@ -10,7 +10,9 @@
g_should_call : bool = false;
read_long :: (env: *void, target: *void) -> s64 {
#jni_call(s64)(env, target, "currentTimeMillis", "()J");
#jni_env(env) {
#jni_call(s64)(target, "currentTimeMillis", "()J");
}
}
main :: () -> s32 {

View File

@@ -7,7 +7,9 @@
g_should_call : bool = false;
read_double :: (env: *void, target: *void) -> f64 {
#jni_call(f64)(env, target, "getValue", "()D");
#jni_env(env) {
#jni_call(f64)(target, "getValue", "()D");
}
}
main :: () -> s32 {

View File

@@ -9,7 +9,9 @@
g_should_call : bool = false;
read_bool :: (env: *void, target: *void) -> bool {
#jni_call(bool)(env, target, "isShown", "()Z");
#jni_env(env) {
#jni_call(bool)(target, "isShown", "()Z");
}
}
main :: () -> s32 {

View File

@@ -14,7 +14,9 @@
g_should_call : bool = false;
get_window :: (env: *void, activity: *void) -> *void {
#jni_call(*void)(env, activity, "getWindow", "()Landroid/view/Window;");
#jni_env(env) {
#jni_call(*void)(activity, "getWindow", "()Landroid/view/Window;");
}
}
main :: () -> s32 {

View File

@@ -10,7 +10,9 @@
g_should_call : bool = false;
call_static_max :: (env: *void, cls: *void) -> s32 {
#jni_static_call(s32)(env, cls, "max", "(II)I", 3, 7);
#jni_env(env) {
#jni_static_call(s32)(cls, "max", "(II)I", 3, 7);
}
}
main :: () -> s32 {

View File

@@ -0,0 +1,30 @@
// Thread-local JNIEnv* slot for the `#jni_env(env) { body }` block and
// the `#jni_call` cross-function fallback (FFI plan step 2.16c).
//
// Lives outside the user's IR module on purpose. The natural place
// would be `@sx_jni_env_tl = internal thread_local global ptr null`
// inside the lowered IR, but LLVM ORC JIT's default platform support
// doesn't initialise TLS slots for objects added via
// `LLVMOrcLLJITAddObjectFile`. Wrapping the storage in an externally-
// linked C helper sidesteps that — JIT process-symbol resolution finds
// these `_Thread_local`-backed functions via the host's dlsym (sx
// itself is built with this .c linked in via build.zig); AOT targets
// (Android, etc.) pick it up as a regular `#import c { #source ...; }`
// auto-injected by the lowering pass.
//
// The slot is per-thread; nesting is handled at the call site via
// save → set(new) → body → set(saved) (see lower.zig). Multi-VM
// nesting needs the caller to track that themselves — the slot
// doesn't know which JVM the env belongs to.
#include <stddef.h>
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;
}

View File

@@ -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;
}

View File

@@ -534,6 +534,10 @@ pub const LLVMEmitter = struct {
c.LLVMSetLinkage(llvm_global, c.LLVMInternalLinkage);
if (global.is_thread_local) {
c.LLVMSetThreadLocal(llvm_global, 1);
}
// Evaluate comptime initializer if present
if (global.comptime_func) |func_id| {
var interp_inst = Interpreter.init(self.ir_mod, self.alloc);

View File

@@ -503,6 +503,10 @@ pub const Global = struct {
init_val: ?ConstantValue = null,
is_extern: bool = false,
is_const: bool = false,
/// Thread-local storage. `global_get` / `global_set` emit normal LLVM
/// load/store instructions; LLVM handles the per-thread access through
/// the `thread_local` attribute on the global.
is_thread_local: bool = false,
/// For comptime globals: the function to interpret to get the init value.
comptime_func: ?FuncId = null,
};

View File

@@ -98,6 +98,10 @@ pub const Lowering = struct {
current_source_file: ?[]const u8 = null, // source file of function currently being lowered
sel_register_name_fid: ?FuncId = null, // lazily-declared `sel_registerName` extern (non-literal selector fallback)
jni_env_stack: std.ArrayList(Ref) = std.ArrayList(Ref).empty, // lexical `#jni_env(env)` Ref stack — top is current scope's env for omitted-env `#jni_call`
jni_env_stack_base: usize = 0, // index above which the currently-lowering fn's `#jni_env` scopes live; outer-fn Refs aren't valid in this fn's instruction stream
jni_env_tl_get_fid: ?FuncId = null, // extern `sx_jni_env_tl_get` (from library/vendors/sx_jni_runtime/sx_jni_env_tl.c)
jni_env_tl_set_fid: ?FuncId = null, // extern `sx_jni_env_tl_set`
needs_jni_env_tl_runtime: bool = false, // set when lowering touches the JNI env TL; signals Compilation to auto-link the runtime .c
foreign_class_map: std.StringHashMap(*const ast.ForeignClassDecl) = std.StringHashMap(*const ast.ForeignClassDecl).init(std.heap.page_allocator), // sx alias → ForeignClassDecl (jni_class / objc_class / swift_class / ... — registered in scan pass)
type_bindings: ?std.StringHashMap(TypeId) = null, // generic type param bindings ($T → concrete TypeId)
current_match_tags: ?[]const u64 = null, // type tags for current match arm (for runtime dispatch)
@@ -736,6 +740,14 @@ pub const Lowering = struct {
const saved_block_terminated = self.block_terminated;
const saved_force_block_value = self.force_block_value;
const saved_source_file = self.current_source_file;
// The `#jni_env` Ref stack is lexical within ONE function's instruction
// stream — Refs from the caller don't dereference correctly in this
// callee's body. Move the visible base to the current top so
// omitted-env `#jni_call` in this fn doesn't accidentally pick up the
// caller's Refs. Defer covers all the early-return paths below.
const saved_jni_env_base = self.jni_env_stack_base;
self.jni_env_stack_base = self.jni_env_stack.items.len;
defer self.jni_env_stack_base = saved_jni_env_base;
self.func_defer_base = self.defer_stack.items.len;
self.block_terminated = false;
self.force_block_value = false;
@@ -1062,15 +1074,27 @@ pub const Lowering = struct {
.insert_expr => |ins| self.lowerInsertExpr(ins.expr),
.block => self.lowerBlock(node),
.jni_env_block => |eb| {
// Lexical-direct env resolution (2.16b): evaluate env once,
// push onto the env stack, lower body, pop. `#jni_call`
// sites inside `eb.body` with an omitted env arg pick up
// the top-of-stack value directly — no thread-local read,
// env stays register-resident across the body.
// Compile-time stack push for lexical-direct env resolution
// (2.16b — `#jni_call` in the same fn picks up env from
// jni_env_stack directly, no TL read).
//
// Runtime TL save/set/restore (2.16c) for cross-function
// helpers: callees in OTHER fns invoked from inside the
// body read the slot via `sx_jni_env_tl_get`. Storage
// lives in a separately-linked C helper (see
// library/vendors/sx_jni_runtime/sx_jni_env_tl.c) so the
// JIT doesn't need orc_rt for TLS.
const env_ref = self.lowerExpr(eb.env);
const fids = self.getJniEnvTlFids();
const ptr_ty = self.module.types.ptrTo(.void);
const saved_tl = self.builder.emit(.{ .call = .{ .callee = fids.get, .args = &.{} } }, ptr_ty);
const set_args = self.alloc.dupe(Ref, &.{env_ref}) catch unreachable;
_ = self.builder.emit(.{ .call = .{ .callee = fids.set, .args = set_args } }, .void);
self.jni_env_stack.append(self.alloc, env_ref) catch unreachable;
defer _ = self.jni_env_stack.pop();
self.lowerBlock(eb.body);
_ = self.jni_env_stack.pop();
const restore_args = self.alloc.dupe(Ref, &.{saved_tl}) catch unreachable;
_ = self.builder.emit(.{ .call = .{ .callee = fids.set, .args = restore_args } }, .void);
},
// Block-local type declarations
.struct_decl => |sd| self.registerStructDecl(&sd),
@@ -1795,6 +1819,23 @@ pub const Lowering = struct {
.spread_expr => self.emitError("spread_expr", node.span),
.chained_comparison => |cc| self.lowerChainedComparison(&cc),
// `#jni_env(env) { body }` in expression position — the block's
// value becomes the env-scope's value. Save→set→body-value→restore.
.jni_env_block => |eb| blk: {
const env_ref = self.lowerExpr(eb.env);
const fids = self.getJniEnvTlFids();
const ptr_ty = self.module.types.ptrTo(.void);
const saved_tl = self.builder.emit(.{ .call = .{ .callee = fids.get, .args = &.{} } }, ptr_ty);
const set_args = self.alloc.dupe(Ref, &.{env_ref}) catch unreachable;
_ = self.builder.emit(.{ .call = .{ .callee = fids.set, .args = set_args } }, .void);
self.jni_env_stack.append(self.alloc, env_ref) catch unreachable;
const value = self.lowerBlockValue(eb.body) orelse self.builder.constInt(0, .void);
_ = self.jni_env_stack.pop();
const restore_args = self.alloc.dupe(Ref, &.{saved_tl}) catch unreachable;
_ = self.builder.emit(.{ .call = .{ .callee = fids.set, .args = restore_args } }, .void);
break :blk value;
},
// Statements that can appear in expression position
.block => |blk| blk: {
// Create a child scope for block-level variable shadowing
@@ -3875,35 +3916,31 @@ pub const Lowering = struct {
}
fn lowerJniCall(self: *Lowering, fic: *const ast.FfiIntrinsicCall) Ref {
// env disambiguation: the method-name slot is always a string literal,
// so its position tells us whether env was omitted.
// omitted → args = target, "name", "sig", method-args... (≥3)
// explicit → args = env, target, "name", "sig", method-args... (≥4)
const env_omitted = fic.args.len >= 3 and fic.args[1].data == .string_literal;
const min_arity: usize = if (env_omitted) 3 else 4;
if (fic.args.len < min_arity) {
// env is always implicit: lexical-direct from the enclosing `#jni_env(env)`
// block (2.16b, cheap), else the thread-local slot the block populated
// at runtime (2.16c, one TL load per call). Surface form is uniform:
// #jni_call(T)(target, "name", "sig", method-args...) (≥3 args)
if (fic.args.len < 3) {
if (self.diagnostics) |d| {
d.add(.err, "#jni_call requires env (optional in #jni_env scope), target, method name, and signature", null);
d.add(.err, "#jni_call requires target, method name, and signature", null);
}
return Ref.none;
}
const ret_ty = self.resolveType(fic.return_type);
const env_ref = if (env_omitted) blk: {
if (self.jni_env_stack.items.len == 0) {
if (self.diagnostics) |d| {
d.add(.err, "#jni_call with omitted env requires an enclosing #jni_env scope", null);
}
return Ref.none;
}
break :blk self.jni_env_stack.items[self.jni_env_stack.items.len - 1];
} else self.lowerExpr(fic.args[0]);
const env_ref = if (self.jni_env_stack.items.len > self.jni_env_stack_base)
self.jni_env_stack.items[self.jni_env_stack.items.len - 1]
else blk: {
const fids = self.getJniEnvTlFids();
const ptr_ty = self.module.types.ptrTo(.void);
break :blk self.builder.emit(.{ .call = .{ .callee = fids.get, .args = &.{} } }, ptr_ty);
};
const target_idx: usize = if (env_omitted) 0 else 1;
const name_idx: usize = target_idx + 1;
const sig_idx: usize = target_idx + 2;
const first_method_arg_idx: usize = target_idx + 3;
const target_idx: usize = 0;
const name_idx: usize = 1;
const sig_idx: usize = 2;
const first_method_arg_idx: usize = 3;
const target_ref = self.lowerExpr(fic.args[target_idx]);
const name_node = fic.args[name_idx];
@@ -8130,6 +8167,37 @@ pub const Lowering = struct {
self.foreign_class_map.put(fcd.name, fcd) catch {};
}
/// Lazily declare the `sx_jni_env_tl_get` / `sx_jni_env_tl_set`
/// runtime externs (step 2.16c). The storage lives in
/// `library/vendors/sx_jni_runtime/sx_jni_env_tl.c` as a
/// `_Thread_local` slot — keeping it OUT of the user's IR module
/// is what lets the LLVM ORC JIT load the module cleanly without
/// orc_rt platform support. AOT targets get the same .c file
/// linked in via `needs_jni_env_tl_runtime`, which Compilation
/// reads to append a synthetic c_import alongside the user's.
fn getJniEnvTlFids(self: *Lowering) struct { get: FuncId, set: FuncId } {
self.needs_jni_env_tl_runtime = true;
const ptr_ty = self.module.types.ptrTo(.void);
if (self.jni_env_tl_get_fid == null) {
const name = self.module.types.internString("sx_jni_env_tl_get");
const fid = self.builder.declareExtern(name, &.{}, ptr_ty);
const func = self.module.getFunctionMut(fid);
func.call_conv = .c;
self.jni_env_tl_get_fid = fid;
}
if (self.jni_env_tl_set_fid == null) {
const name = self.module.types.internString("sx_jni_env_tl_set");
const env_param = self.module.types.internString("env");
var params = std.ArrayList(inst_mod.Function.Param).empty;
params.append(self.alloc, .{ .name = env_param, .ty = ptr_ty }) catch unreachable;
const fid = self.builder.declareExtern(name, params.toOwnedSlice(self.alloc) catch unreachable, .void);
const func = self.module.getFunctionMut(fid);
func.call_conv = .c;
self.jni_env_tl_set_fid = fid;
}
return .{ .get = self.jni_env_tl_get_fid.?, .set = self.jni_env_tl_set_fid.? };
}
/// When a namespaced import (`Ns :: #import "..."`) contains foreign-class
/// declarations, ALSO register them under their qualified name `Ns.Class`
/// so receiver types like `*Ns.Class` can find the fcd. The recursive

View File

@@ -208,6 +208,8 @@ entry:
%allocaN = alloca ptr, align 8
store ptr %1, ptr %allocaN, align 8
%load = load ptr, ptr %alloca, align 8
%call = call ptr @sx_jni_env_tl_get()
call void @sx_jni_env_tl_set(ptr %load)
%loadN = load ptr, ptr %allocaN, align 8
%jni.ifs = load ptr, ptr %load, align 8
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_noop____V, align 8
@@ -233,32 +235,32 @@ jni.cont: ; preds = %jni.miss, %entry
%5 = getelementptr inbounds ptr, ptr %jni.ifs, i32 61
%jni.callfn = load ptr, ptr %5, align 8
call void %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid)
%loadN = load ptr, ptr %alloca, align 8
%loadN = load ptr, ptr %allocaN, align 8
%jni.ifs5 = load ptr, ptr %loadN, align 8
%jni.cached.mid6 = load ptr, ptr @SX_JNI_MID_noop____V, align 8
%jni.is.cached7 = icmp ne ptr %jni.cached.mid6, null
br i1 %jni.is.cached7, label %jni.cont9, label %jni.miss8
%jni.ifs4 = load ptr, ptr %load, align 8
%jni.cached.mid5 = load ptr, ptr @SX_JNI_MID_noop____V, align 8
%jni.is.cached6 = icmp ne ptr %jni.cached.mid5, null
br i1 %jni.is.cached6, label %jni.cont8, label %jni.miss7
jni.miss8: ; preds = %jni.cont
%6 = getelementptr inbounds ptr, ptr %jni.ifs5, i32 31
%jni.GetObjectClass10 = load ptr, ptr %6, align 8
%jni.cls11 = call ptr %jni.GetObjectClass10(ptr %loadN, ptr %loadN)
%7 = getelementptr inbounds ptr, ptr %jni.ifs5, i32 21
%jni.NewGlobalRef12 = load ptr, ptr %7, align 8
%jni.global.cls13 = call ptr %jni.NewGlobalRef12(ptr %loadN, ptr %jni.cls11)
store ptr %jni.global.cls13, ptr @SX_JNI_CLS_noop____V, align 8
%8 = getelementptr inbounds ptr, ptr %jni.ifs5, i32 33
%jni.GetMethodID14 = load ptr, ptr %8, align 8
%jni.fresh.mid15 = call ptr %jni.GetMethodID14(ptr %loadN, ptr %jni.global.cls13, ptr @str.2, ptr @str.3)
store ptr %jni.fresh.mid15, ptr @SX_JNI_MID_noop____V, align 8
br label %jni.cont9
jni.miss7: ; preds = %jni.cont
%6 = getelementptr inbounds ptr, ptr %jni.ifs4, i32 31
%jni.GetObjectClass9 = load ptr, ptr %6, align 8
%jni.cls10 = call ptr %jni.GetObjectClass9(ptr %load, ptr %loadN)
%7 = getelementptr inbounds ptr, ptr %jni.ifs4, i32 21
%jni.NewGlobalRef11 = load ptr, ptr %7, align 8
%jni.global.cls12 = call ptr %jni.NewGlobalRef11(ptr %load, ptr %jni.cls10)
store ptr %jni.global.cls12, ptr @SX_JNI_CLS_noop____V, align 8
%8 = getelementptr inbounds ptr, ptr %jni.ifs4, i32 33
%jni.GetMethodID13 = load ptr, ptr %8, align 8
%jni.fresh.mid14 = call ptr %jni.GetMethodID13(ptr %load, ptr %jni.global.cls12, ptr @str.2, ptr @str.3)
store ptr %jni.fresh.mid14, ptr @SX_JNI_MID_noop____V, align 8
br label %jni.cont8
jni.cont9: ; preds = %jni.miss8, %jni.cont
%jni.mid16 = phi ptr [ %jni.cached.mid6, %jni.cont ], [ %jni.fresh.mid15, %jni.miss8 ]
%9 = getelementptr inbounds ptr, ptr %jni.ifs5, i32 61
%jni.callfn17 = load ptr, ptr %9, align 8
call void %jni.callfn17(ptr %loadN, ptr %loadN, ptr %jni.mid16)
jni.cont8: ; preds = %jni.miss7, %jni.cont
%jni.mid15 = phi ptr [ %jni.cached.mid5, %jni.cont ], [ %jni.fresh.mid14, %jni.miss7 ]
%9 = getelementptr inbounds ptr, ptr %jni.ifs4, i32 61
%jni.callfn16 = load ptr, ptr %9, align 8
call void %jni.callfn16(ptr %load, ptr %loadN, ptr %jni.mid15)
call void @sx_jni_env_tl_set(ptr %call)
ret void
}
@@ -316,6 +318,10 @@ entry:
ret void
}
; Function Attrs: nounwind
declare ptr @sx_jni_env_tl_get() #0
; Function Attrs: nounwind
declare void @sx_jni_env_tl_set(ptr) #0
declare i64 @write(i32, ptr, i64)

View File

@@ -206,6 +206,8 @@ entry:
%allocaN = alloca ptr, align 8
store ptr %1, ptr %allocaN, align 8
%load = load ptr, ptr %alloca, align 8
%call = call ptr @sx_jni_env_tl_get()
call void @sx_jni_env_tl_set(ptr %load)
%loadN = load ptr, ptr %allocaN, align 8
%jni.ifs = load ptr, ptr %load, align 8
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_getCount____I, align 8
@@ -231,6 +233,7 @@ jni.cont: ; preds = %jni.miss, %entry
%5 = getelementptr inbounds ptr, ptr %jni.ifs, i32 49
%jni.callfn = load ptr, ptr %5, align 8
%jni.ret = call i32 %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid)
call void @sx_jni_env_tl_set(ptr %call)
ret i32 %jni.ret
}
@@ -290,6 +293,10 @@ entry:
ret void
}
; Function Attrs: nounwind
declare ptr @sx_jni_env_tl_get() #0
; Function Attrs: nounwind
declare void @sx_jni_env_tl_set(ptr) #0
declare i64 @write(i32, ptr, i64)

View File

@@ -206,6 +206,8 @@ entry:
%allocaN = alloca ptr, align 8
store ptr %1, ptr %allocaN, align 8
%load = load ptr, ptr %alloca, align 8
%call = call ptr @sx_jni_env_tl_get()
call void @sx_jni_env_tl_set(ptr %load)
%loadN = load ptr, ptr %allocaN, align 8
%jni.ifs = load ptr, ptr %load, align 8
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_currentTimeMillis____J, align 8
@@ -231,6 +233,7 @@ jni.cont: ; preds = %jni.miss, %entry
%5 = getelementptr inbounds ptr, ptr %jni.ifs, i32 52
%jni.callfn = load ptr, ptr %5, align 8
%jni.ret = call i64 %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid)
call void @sx_jni_env_tl_set(ptr %call)
ret i64 %jni.ret
}
@@ -290,6 +293,10 @@ entry:
ret void
}
; Function Attrs: nounwind
declare ptr @sx_jni_env_tl_get() #0
; Function Attrs: nounwind
declare void @sx_jni_env_tl_set(ptr) #0
declare i64 @write(i32, ptr, i64)

View File

@@ -206,6 +206,8 @@ entry:
%allocaN = alloca ptr, align 8
store ptr %1, ptr %allocaN, align 8
%load = load ptr, ptr %alloca, align 8
%call = call ptr @sx_jni_env_tl_get()
call void @sx_jni_env_tl_set(ptr %load)
%loadN = load ptr, ptr %allocaN, align 8
%jni.ifs = load ptr, ptr %load, align 8
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_getValue____D, align 8
@@ -231,6 +233,7 @@ jni.cont: ; preds = %jni.miss, %entry
%5 = getelementptr inbounds ptr, ptr %jni.ifs, i32 58
%jni.callfn = load ptr, ptr %5, align 8
%jni.ret = call double %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid)
call void @sx_jni_env_tl_set(ptr %call)
ret double %jni.ret
}
@@ -290,6 +293,10 @@ entry:
ret void
}
; Function Attrs: nounwind
declare ptr @sx_jni_env_tl_get() #0
; Function Attrs: nounwind
declare void @sx_jni_env_tl_set(ptr) #0
declare i64 @write(i32, ptr, i64)

View File

@@ -206,6 +206,8 @@ entry:
%allocaN = alloca ptr, align 8
store ptr %1, ptr %allocaN, align 8
%load = load ptr, ptr %alloca, align 8
%call = call ptr @sx_jni_env_tl_get()
call void @sx_jni_env_tl_set(ptr %load)
%loadN = load ptr, ptr %allocaN, align 8
%jni.ifs = load ptr, ptr %load, align 8
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_isShown____Z, align 8
@@ -231,6 +233,7 @@ jni.cont: ; preds = %jni.miss, %entry
%5 = getelementptr inbounds ptr, ptr %jni.ifs, i32 37
%jni.callfn = load ptr, ptr %5, align 8
%jni.ret = call i1 %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid)
call void @sx_jni_env_tl_set(ptr %call)
ret i1 %jni.ret
}
@@ -290,6 +293,10 @@ entry:
ret void
}
; Function Attrs: nounwind
declare ptr @sx_jni_env_tl_get() #0
; Function Attrs: nounwind
declare void @sx_jni_env_tl_set(ptr) #0
declare i64 @write(i32, ptr, i64)

View File

@@ -206,6 +206,8 @@ entry:
%allocaN = alloca ptr, align 8
store ptr %1, ptr %allocaN, align 8
%load = load ptr, ptr %alloca, align 8
%call = call ptr @sx_jni_env_tl_get()
call void @sx_jni_env_tl_set(ptr %load)
%loadN = load ptr, ptr %allocaN, align 8
%jni.ifs = load ptr, ptr %load, align 8
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_getWindow____Landroid_view_Window_, align 8
@@ -231,6 +233,7 @@ jni.cont: ; preds = %jni.miss, %entry
%5 = getelementptr inbounds ptr, ptr %jni.ifs, i32 34
%jni.callfn = load ptr, ptr %5, align 8
%jni.ret = call ptr %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid)
call void @sx_jni_env_tl_set(ptr %call)
ret ptr %jni.ret
}
@@ -290,6 +293,10 @@ entry:
ret void
}
; Function Attrs: nounwind
declare ptr @sx_jni_env_tl_get() #0
; Function Attrs: nounwind
declare void @sx_jni_env_tl_set(ptr) #0
declare i64 @write(i32, ptr, i64)

View File

@@ -206,6 +206,8 @@ entry:
%allocaN = alloca ptr, align 8
store ptr %1, ptr %allocaN, align 8
%load = load ptr, ptr %alloca, align 8
%call = call ptr @sx_jni_env_tl_get()
call void @sx_jni_env_tl_set(ptr %load)
%loadN = load ptr, ptr %allocaN, align 8
%jni.ifs = load ptr, ptr %load, align 8
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_max___II_I, align 8
@@ -228,6 +230,7 @@ jni.cont: ; preds = %jni.miss, %entry
%4 = getelementptr inbounds ptr, ptr %jni.ifs, i32 129
%jni.callfn = load ptr, ptr %4, align 8
%jni.ret = call i32 %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid, i32 3, i32 7)
call void @sx_jni_env_tl_set(ptr %call)
ret i32 %jni.ret
}
@@ -287,6 +290,10 @@ entry:
ret void
}
; Function Attrs: nounwind
declare ptr @sx_jni_env_tl_get() #0
; Function Attrs: nounwind
declare void @sx_jni_env_tl_set(ptr) #0
declare i64 @write(i32, ptr, i64)

View File

@@ -206,6 +206,8 @@ entry:
%allocaN = alloca ptr, align 8
store ptr %1, ptr %allocaN, align 8
%load = load ptr, ptr %alloca, align 8
%call = call ptr @sx_jni_env_tl_get()
call void @sx_jni_env_tl_set(ptr %load)
%loadN = load ptr, ptr %allocaN, align 8
%jni.ifs = load ptr, ptr %load, align 8
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_getWindow____Ljava_lang_Object_, align 8
@@ -233,6 +235,7 @@ jni.cont: ; preds = %jni.miss, %entry
%jni.ret = call ptr %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid)
%allocaN = alloca ptr, align 8
store ptr %jni.ret, ptr %allocaN, align 8
call void @sx_jni_env_tl_set(ptr %call)
ret void
}
@@ -290,4 +293,10 @@ entry:
ret void
}
; Function Attrs: nounwind
declare ptr @sx_jni_env_tl_get() #0
; Function Attrs: nounwind
declare void @sx_jni_env_tl_set(ptr) #0
declare i64 @write(i32, ptr, i64)

View File

@@ -206,6 +206,8 @@ entry:
%allocaN = alloca ptr, align 8
store ptr %1, ptr %allocaN, align 8
%load = load ptr, ptr %alloca, align 8
%call = call ptr @sx_jni_env_tl_get()
call void @sx_jni_env_tl_set(ptr %load)
%loadN = load ptr, ptr %allocaN, align 8
%jni.ifs = load ptr, ptr %load, align 8
%jni.cached.mid = load ptr, ptr @SX_JNI_MID_noop____V, align 8
@@ -231,6 +233,7 @@ jni.cont: ; preds = %jni.miss, %entry
%5 = getelementptr inbounds ptr, ptr %jni.ifs, i32 61
%jni.callfn = load ptr, ptr %5, align 8
call void %jni.callfn(ptr %load, ptr %loadN, ptr %jni.mid)
call void @sx_jni_env_tl_set(ptr %call)
ret void
}
@@ -288,4 +291,10 @@ entry:
ret void
}
; Function Attrs: nounwind
declare ptr @sx_jni_env_tl_get() #0
; Function Attrs: nounwind
declare void @sx_jni_env_tl_set(ptr) #0
declare i64 @write(i32, ptr, i64)

View File

@@ -1 +1 @@
1
0

View File

@@ -1,14 +1 @@
LLVM verification failed: Load operand must be a pointer.
%jni.ifs = load ptr, i64 undef, align 8
Call parameter type does not match function signature!
i64 undef
ptr %jni.cls = call ptr %jni.GetObjectClass(i64 undef, ptr %load)
Call parameter type does not match function signature!
i64 undef
ptr %jni.global.cls = call ptr %jni.NewGlobalRef(i64 undef, ptr %jni.cls)
Call parameter type does not match function signature!
i64 undef
ptr %jni.fresh.mid = call ptr %jni.GetMethodID(i64 undef, ptr %jni.global.cls, ptr @str, ptr @str.1)
Call parameter type does not match function signature!
i64 undef
ptr call void %jni.callfn(i64 undef, ptr %load, ptr %jni.mid)
ok