ffi M3.1 + M1.2 A.3 refactor: self=Obj-C id, self.field via ivar; SxAppDelegate migrated
Two coupled changes that unblock the uikit_register_classes
migration:
1) M1.2 A.3 — body's 'self' is the Obj-C id (opaque), NOT the
state struct. Matches Apple's ObjC semantics where 'self' IS
the object. Cocoa idiom 'xx self → id' works at runtime calls
(addObserver:, etc.); previously the trampoline replaced
'self' with the state-struct pointer, breaking any runtime
call that expected an id.
'*Self' substitution in resolveTypeWithBindings now points at
foreignClassStructType(fcd) — the opaque class stub — instead
of objcDefinedStateStructType(fcd).
'self.field' access on a sx-defined class instance field is
rewritten by lowerFieldAccess to go through the __sx_state
ivar:
state = object_getIvar(self, load(__<Cls>_state_ivar))
val = struct_gep(state, field_idx) → load
Both read (lowerFieldAccess) and write (lowerAssignment) take
this path. Compound ops (+=, -=, etc.) are supported via
storeOrCompound. The lookup is filtered: skip property fields
(those still go through the M2.2 msgSend getter/setter
dispatch) and foreign classes (no state).
New helpers in lower.zig:
- lookupObjcDefinedStateFieldOnPointer — match check.
- lowerObjcDefinedStateForObj — emit the object_getIvar +
ivar-global-load idiom (shared between read + write paths).
- lowerObjcDefinedStateFieldRead — the load path.
Also moved the @llvm.global_ctors registration out of the
sx-defined class-pair init constructor — global_ctors fires
DURING dyld's framework load, before UIKit registers its Obj-C
classes. objc_getClass("UIResponder") returned null, super
was null, objc_registerClassPair crashed. main's entry block
is post-framework-load but pre-user-code — exactly the right
window. New helper injectCtorIntoMain.
2) M3.1 — SxAppDelegate migrated to declarative #objc_class.
uikit_register_classes' hand-rolled objc_allocateClassPair +
class_addMethod for SxAppDelegate is gone; the compiler
synthesises the class at module init. The method bodies
forward to the existing legacy IMP free functions
(uikit_did_finish_launching, uikit_keyboard_will_change_frame)
so we don't have to inline 70+ lines of keyboard-frame logic
right now.
Also adds UIResponder foreign-class declaration and chains
UIView / UITextField to it via #extends UIResponder so the
methods that previously lived on UITextField directly
(becomeFirstResponder etc.) move to their proper home.
Chess on iOS-sim: board renders, full state intact. 183 example
tests + zig build test green.
This commit is contained in:
@@ -140,7 +140,17 @@ UIScreen :: #foreign #objc_class("UIScreen") {
|
||||
bounds :: (self: *Self) -> CGRect;
|
||||
}
|
||||
|
||||
// UIResponder is the root for keyboard / touch / focus dispatch.
|
||||
// Most UIKit classes inherit from it; sx-defined classes that
|
||||
// participate in lifecycle callbacks (delegates, scene delegates)
|
||||
// extend it so the runtime picks up the responder-chain behavior.
|
||||
UIResponder :: #foreign #objc_class("UIResponder") {
|
||||
becomeFirstResponder :: (self: *Self) -> s8;
|
||||
resignFirstResponder :: (self: *Self) -> s8;
|
||||
}
|
||||
|
||||
UIView :: #foreign #objc_class("UIView") {
|
||||
#extends UIResponder;
|
||||
safeAreaInsets :: (self: *Self) -> UIEdgeInsets;
|
||||
addSubview :: (self: *Self, view: *void);
|
||||
layer :: (self: *Self) -> *CALayer;
|
||||
@@ -162,12 +172,29 @@ UIViewController :: #foreign #objc_class("UIViewController") {
|
||||
}
|
||||
|
||||
UITextField :: #foreign #objc_class("UITextField") {
|
||||
alloc :: () -> *UITextField;
|
||||
init :: (self: *Self) -> *UITextField;
|
||||
// Inherited from UIResponder via the runtime; declared here directly
|
||||
// until `#extends UIResponder` lands (Phase 3.4).
|
||||
becomeFirstResponder :: (self: *Self) -> s8;
|
||||
resignFirstResponder :: (self: *Self) -> s8;
|
||||
#extends UIResponder;
|
||||
alloc :: () -> *UITextField;
|
||||
init :: (self: *Self) -> *UITextField;
|
||||
}
|
||||
|
||||
// SxAppDelegate — UIApplicationMain's principal class. Replaces the
|
||||
// M3.1 hand-rolled objc_allocateClassPair + class_addMethod sequence
|
||||
// in uikit_register_classes. The method bodies forward to the
|
||||
// existing legacy IMP free functions so we don't have to inline 70+
|
||||
// lines of keyboard-frame logic here.
|
||||
SxAppDelegate :: #objc_class("SxAppDelegate") {
|
||||
#extends UIResponder;
|
||||
|
||||
application_didFinishLaunchingWithOptions :: (self: *Self, app: *void, opts: *void) -> BOOL {
|
||||
return xx uikit_did_finish_launching(xx self, xx 0, app, opts);
|
||||
}
|
||||
|
||||
sxKeyboardWillChangeFrame :: (self: *Self, notification: *void) {
|
||||
uikit_keyboard_will_change_frame(xx self, xx 0, notification);
|
||||
}
|
||||
|
||||
alloc :: () -> *SxAppDelegate;
|
||||
init :: (self: *SxAppDelegate) -> *SxAppDelegate;
|
||||
}
|
||||
|
||||
// GLenum constants for renderbuffer/framebuffer setup that aren't in opengl.sx's
|
||||
@@ -401,24 +428,19 @@ uikit_chdir_to_bundle :: () {
|
||||
|
||||
uikit_register_classes :: () {
|
||||
inline if OS == .ios {
|
||||
UIResponder := objc_getClass("UIResponder".ptr);
|
||||
SxAppDelegate := objc_allocateClassPair(UIResponder, "SxAppDelegate".ptr, 0);
|
||||
// SxAppDelegate is now declared as `#objc_class("SxAppDelegate")`
|
||||
// (M3.1) — the compiler synthesises its IMPs and class-pair
|
||||
// registration at module init. The old hand-rolled
|
||||
// objc_allocateClassPair + class_addMethod sequence is gone.
|
||||
|
||||
class_addMethod(SxAppDelegate,
|
||||
sel_registerName("application:didFinishLaunchingWithOptions:".ptr),
|
||||
xx uikit_did_finish_launching, "c@:@@".ptr);
|
||||
class_addMethod(SxAppDelegate,
|
||||
sel_registerName("sxKeyboardWillChangeFrame:".ptr),
|
||||
xx uikit_keyboard_will_change_frame, "v@:@".ptr);
|
||||
|
||||
objc_registerClassPair(SxAppDelegate);
|
||||
UIResponder_cls := objc_getClass("UIResponder".ptr);
|
||||
|
||||
// SxSceneDelegate handles the per-scene UI setup. iOS 13+ scene-based
|
||||
// lifecycle: didFinishLaunching is too early for the window — the
|
||||
// UIWindowScene doesn't connect until `scene:willConnectTo:options:`.
|
||||
// The class is named in Info.plist's UIApplicationSceneManifest →
|
||||
// UISceneDelegateClassName.
|
||||
SxSceneDelegate := objc_allocateClassPair(UIResponder, "SxSceneDelegate".ptr, 0);
|
||||
SxSceneDelegate := objc_allocateClassPair(UIResponder_cls, "SxSceneDelegate".ptr, 0);
|
||||
|
||||
class_addMethod(SxSceneDelegate,
|
||||
sel_registerName("scene:willConnectToSession:options:".ptr),
|
||||
|
||||
@@ -735,8 +735,17 @@ pub const LLVMEmitter = struct {
|
||||
}
|
||||
_ = c.LLVMBuildRetVoid(self.builder);
|
||||
|
||||
// Register in @llvm.global_ctors + inject into main for ORC JIT.
|
||||
self.appendModuleCtor(ctor, ctor_ty);
|
||||
// Inject the call into main's entry block ONLY — skip
|
||||
// @llvm.global_ctors. Apple's frameworks (UIKit on iOS,
|
||||
// AppKit on macOS) register their Obj-C classes during
|
||||
// dyld's image-init phase, which overlaps global_ctors. If
|
||||
// we ran there too, `objc_getClass(\"UIResponder\")` would
|
||||
// return null and `objc_allocateClassPair(null, ...)` would
|
||||
// crash inside objc_registerClassPair. main's entry runs
|
||||
// AFTER dyld's framework init is complete but BEFORE user
|
||||
// code (UIApplicationMain), so the runtime sees the parent
|
||||
// class properly.
|
||||
self.injectCtorIntoMain(ctor, ctor_ty);
|
||||
|
||||
_ = i32_ty;
|
||||
}
|
||||
@@ -778,6 +787,30 @@ pub const LLVMEmitter = struct {
|
||||
/// global if not present, extending the array if so) AND inject a
|
||||
/// direct call from `main`'s entry block so the ORC JIT path runs
|
||||
/// the constructor too.
|
||||
/// Inject a call to `ctor()` at the start of `main`'s entry block
|
||||
/// (past any existing init calls). Used by class-pair init etc.
|
||||
/// that need to run BEFORE user code but AFTER dyld's framework
|
||||
/// load — global_ctors is too early because Apple frameworks
|
||||
/// (UIKit etc.) register their Obj-C classes during their own
|
||||
/// init phase that overlaps ours.
|
||||
fn injectCtorIntoMain(self: *LLVMEmitter, ctor: c.LLVMValueRef, ctor_ty: c.LLVMTypeRef) void {
|
||||
const main_z = "main";
|
||||
const main_fn = c.LLVMGetNamedFunction(self.llvm_module, main_z);
|
||||
if (main_fn == null) return;
|
||||
const entry_bb = c.LLVMGetEntryBasicBlock(main_fn);
|
||||
var insert_before = c.LLVMGetFirstInstruction(entry_bb);
|
||||
while (insert_before != null) : (insert_before = c.LLVMGetNextInstruction(insert_before)) {
|
||||
if (c.LLVMGetInstructionOpcode(insert_before) != c.LLVMCall) break;
|
||||
}
|
||||
if (insert_before != null) {
|
||||
c.LLVMPositionBuilderBefore(self.builder, insert_before);
|
||||
} else {
|
||||
c.LLVMPositionBuilderAtEnd(self.builder, entry_bb);
|
||||
}
|
||||
var no_args: [0]c.LLVMValueRef = .{};
|
||||
_ = c.LLVMBuildCall2(self.builder, ctor_ty, ctor, &no_args, 0, "");
|
||||
}
|
||||
|
||||
fn appendModuleCtor(self: *LLVMEmitter, ctor: c.LLVMValueRef, ctor_ty: c.LLVMTypeRef) void {
|
||||
const i32_ty = self.cached_i32;
|
||||
const ptr_ty = self.cached_ptr;
|
||||
|
||||
177
src/ir/lower.zig
177
src/ir/lower.zig
@@ -1657,16 +1657,33 @@ pub const Lowering = struct {
|
||||
}
|
||||
},
|
||||
.field_access => |fa| {
|
||||
// M2.2 — `obj.field = val` for an Obj-C `#property`
|
||||
// field dispatches as `[obj setField:val]` through
|
||||
// objc_msgSend. Skip the struct-pointer / GEP path
|
||||
// entirely; receivers are opaque Obj-C ids.
|
||||
// M2.2 — `obj.field = val` for an Obj-C `#property` field
|
||||
// dispatches via objc_msgSend `setField:`. Skip struct-
|
||||
// pointer / GEP entirely; receivers are opaque Obj-C ids.
|
||||
// Compound ops on properties are deferred (need load-via-
|
||||
// getter + op + store-via-setter — Month 4 ARC territory).
|
||||
if (asgn.op == .assign) {
|
||||
if (self.lookupObjcPropertyOnPointer(fa.object, fa.field)) |prop| {
|
||||
self.lowerObjcPropertySetter(fa.object, prop, val);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// M1.2 A.3 — `self.field [op]= val` on a sx-defined Obj-C
|
||||
// class instance field (NOT a #property): write through
|
||||
// the __sx_state ivar. Handles plain assignment AND
|
||||
// compound ops (+=, -=, etc.) via storeOrCompound.
|
||||
if (self.lookupObjcDefinedStateFieldOnPointer(fa.object, fa.field)) |info| {
|
||||
const obj_ref = self.lowerExpr(fa.object);
|
||||
const state_ptr = self.lowerObjcDefinedStateForObj(obj_ref, info.fcd) orelse return;
|
||||
const ptr_void = self.module.types.ptrTo(.void);
|
||||
const field_addr = self.builder.emit(.{ .struct_gep = .{
|
||||
.base = state_ptr,
|
||||
.field_index = info.field_idx,
|
||||
.base_type = info.state_ty,
|
||||
} }, ptr_void);
|
||||
self.storeOrCompound(field_addr, val, asgn.op, info.field_ty);
|
||||
return;
|
||||
}
|
||||
|
||||
var obj_ptr = self.lowerExprAsPtr(fa.object);
|
||||
var obj_ty = self.inferExprType(fa.object);
|
||||
@@ -3610,6 +3627,16 @@ pub const Lowering = struct {
|
||||
return self.lowerObjcPropertyGetter(fa.object, prop, fa.field, span);
|
||||
}
|
||||
|
||||
// M1.2 A.3 — `self.field` (or `obj.field`) on a *sx-defined-class
|
||||
// pointer for a plain instance field (NOT a #property) lowers as
|
||||
// `object_getIvar(obj, load(__<Cls>_state_ivar))` + struct_gep on
|
||||
// the state struct + load. The receiver is the opaque Obj-C id
|
||||
// (matching Apple's `self` semantics); the state lives in the
|
||||
// hidden `__sx_state` ivar.
|
||||
if (self.lookupObjcDefinedStateFieldOnPointer(fa.object, fa.field)) |info| {
|
||||
return self.lowerObjcDefinedStateFieldRead(fa.object, info);
|
||||
}
|
||||
|
||||
var obj = self.lowerExpr(fa.object);
|
||||
var obj_ty = self.inferExprType(fa.object);
|
||||
|
||||
@@ -8858,17 +8885,17 @@ pub const Lowering = struct {
|
||||
|
||||
/// Resolve a type node, checking type_bindings first for generic type params.
|
||||
fn resolveTypeWithBindings(self: *Lowering, node: *const Node) TypeId {
|
||||
// *Self substitution for sx-defined `#objc_class` method bodies
|
||||
// (M1.2 A.2b). `current_foreign_class` is set by lowerFunction
|
||||
// and registerObjcDefinedClassMethods when the surrounding
|
||||
// function is a class method. `Self` inside the body resolves
|
||||
// to the class's hidden state-struct type — `self.field` then
|
||||
// works as a plain struct field access (M1.2 A.3 "free if
|
||||
// types align").
|
||||
// `*Self` substitution inside foreign-class member declarations
|
||||
// — both foreign and sx-defined — resolves to the class's own
|
||||
// 0-field stub struct (i.e. the opaque Obj-C pointer type).
|
||||
// This matches the Obj-C idiom where `self` IS the object.
|
||||
// `self.field` access on sx-defined classes is rewritten by
|
||||
// lowerFieldAccess to go through the `__sx_state` ivar
|
||||
// (object_getIvar + struct_gep) when needed — see M1.2 A.3.
|
||||
if (node.data == .type_expr and std.mem.eql(u8, node.data.type_expr.name, "Self")) {
|
||||
if (self.current_foreign_class) |fcd| {
|
||||
if (!fcd.is_foreign and fcd.runtime == .objc_class) {
|
||||
return self.objcDefinedStateStructType(fcd);
|
||||
if (fcd.runtime == .objc_class or fcd.runtime == .objc_protocol) {
|
||||
return self.foreignClassStructType(fcd);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10617,6 +10644,10 @@ pub const Lowering = struct {
|
||||
if (self.lookupObjcPropertyOnPointer(fa.object, fa.field)) |prop| {
|
||||
return self.resolveType(prop.field_type);
|
||||
}
|
||||
// M1.2 A.3 — sx-defined class state field returns the field's type.
|
||||
if (self.lookupObjcDefinedStateFieldOnPointer(fa.object, fa.field)) |info| {
|
||||
return info.field_ty;
|
||||
}
|
||||
|
||||
var obj_ty = self.inferExprType(fa.object);
|
||||
// Auto-deref: if object is a pointer, resolve through it (matches lowerFieldAccess behavior)
|
||||
@@ -11681,6 +11712,93 @@ pub const Lowering = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
const ObjcDefinedStateField = struct {
|
||||
field_ty: TypeId,
|
||||
state_ty: TypeId,
|
||||
field_idx: u32,
|
||||
fcd: *const ast.ForeignClassDecl,
|
||||
};
|
||||
|
||||
/// State-field-access info: if obj_expr is *<sx-defined-class>
|
||||
/// and `field_name` is in the state struct (not a property),
|
||||
/// returns the field's TypeId, the state struct's TypeId, and
|
||||
/// the field's index. M1.2 A.3 supports.
|
||||
fn lookupObjcDefinedStateFieldOnPointer(self: *Lowering, obj_expr: *const ast.Node, field_name: []const u8) ?ObjcDefinedStateField {
|
||||
const obj_ty = self.inferExprType(obj_expr);
|
||||
if (obj_ty.isBuiltin()) return null;
|
||||
const ptr_info = self.module.types.get(obj_ty);
|
||||
if (ptr_info != .pointer) return null;
|
||||
const pointee_info = self.module.types.get(ptr_info.pointer.pointee);
|
||||
if (pointee_info != .@"struct") return null;
|
||||
const struct_name = self.module.types.getString(pointee_info.@"struct".name);
|
||||
const fcd = self.foreign_class_map.get(struct_name) orelse return null;
|
||||
// Only sx-defined Obj-C classes have a state struct. Foreign
|
||||
// classes' fields are purely declaration metadata (no state).
|
||||
if (fcd.is_foreign or fcd.runtime != .objc_class) return null;
|
||||
// Skip property fields — those dispatch via the M2.2 getter/setter
|
||||
// path. Plain instance fields take the ivar+gep path.
|
||||
for (fcd.members) |m| switch (m) {
|
||||
.field => |f| {
|
||||
if (std.mem.eql(u8, f.name, field_name)) {
|
||||
if (f.is_property) return null;
|
||||
const state_ty = self.objcDefinedStateStructType(fcd);
|
||||
const state_info = self.module.types.get(state_ty);
|
||||
if (state_info != .@"struct") return null;
|
||||
const fname_id = self.module.types.internString(f.name);
|
||||
for (state_info.@"struct".fields, 0..) |sf, idx| {
|
||||
if (sf.name == fname_id) {
|
||||
return .{
|
||||
.field_ty = sf.ty,
|
||||
.state_ty = state_ty,
|
||||
.field_idx = @intCast(idx),
|
||||
.fcd = fcd,
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
};
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Lower a read of `self.field` (or `obj.field`) on a sx-defined
|
||||
/// Obj-C class: `state = object_getIvar(self, load(ivar_global))`
|
||||
/// then `struct_gep(state, idx)` + load. M1.2 A.3 — the runtime
|
||||
/// hop through the hidden ivar.
|
||||
fn lowerObjcDefinedStateFieldRead(
|
||||
self: *Lowering,
|
||||
obj_expr: *const ast.Node,
|
||||
info: ObjcDefinedStateField,
|
||||
) Ref {
|
||||
const obj_ref = self.lowerExpr(obj_expr);
|
||||
const state_ptr = self.lowerObjcDefinedStateForObj(obj_ref, info.fcd) orelse return Ref.none;
|
||||
const ptr_void = self.module.types.ptrTo(.void);
|
||||
const field_addr = self.builder.emit(.{ .struct_gep = .{
|
||||
.base = state_ptr,
|
||||
.field_index = info.field_idx,
|
||||
.base_type = info.state_ty,
|
||||
} }, ptr_void);
|
||||
return self.builder.load(field_addr, info.field_ty);
|
||||
}
|
||||
|
||||
/// `state = object_getIvar(obj, load(__<Cls>_state_ivar))`. Shared
|
||||
/// helper for state-field read + write (M1.2 A.3).
|
||||
fn lowerObjcDefinedStateForObj(self: *Lowering, obj_ref: Ref, fcd: *const ast.ForeignClassDecl) ?Ref {
|
||||
const ptr_void = self.module.types.ptrTo(.void);
|
||||
const ivar_global_name = std.fmt.allocPrint(self.alloc, "__{s}_state_ivar", .{fcd.name}) catch return null;
|
||||
defer self.alloc.free(ivar_global_name);
|
||||
const ivar_global_id = self.lookupGlobalIdByName(ivar_global_name) orelse return null;
|
||||
const ivar_addr = self.builder.emit(.{ .global_addr = ivar_global_id }, ptr_void);
|
||||
const ivar_handle = self.builder.load(ivar_addr, ptr_void);
|
||||
const get_ivar_fid = self.ensureCRuntimeDecl("object_getIvar", &.{ ptr_void, ptr_void }, ptr_void);
|
||||
const args = self.alloc.alloc(Ref, 2) catch return null;
|
||||
args[0] = obj_ref;
|
||||
args[1] = ivar_handle;
|
||||
return self.builder.emit(.{ .call = .{ .callee = get_ivar_fid, .args = args } }, ptr_void);
|
||||
}
|
||||
|
||||
/// Lower `obj.field` for an Obj-C `#property` field as
|
||||
/// `objc_msg_send(obj, sel_<fieldName>)`. M2.2 — getter side.
|
||||
/// The setter side lives in the assignment-statement lowering.
|
||||
@@ -12082,39 +12200,27 @@ pub const Lowering = struct {
|
||||
const entry = self.builder.appendBlock(entry_name, &.{});
|
||||
self.builder.switchToBlock(entry);
|
||||
|
||||
// (1) Load ivar handle from per-class global.
|
||||
const ivar_global_name = std.fmt.allocPrint(self.alloc, "__{s}_state_ivar", .{fcd.name}) catch return;
|
||||
defer self.alloc.free(ivar_global_name);
|
||||
const ivar_global_id = self.lookupGlobalIdByName(ivar_global_name) orelse return;
|
||||
const ivar_addr = self.builder.emit(.{ .global_addr = ivar_global_id }, ptr_void);
|
||||
const ivar_handle = self.builder.load(ivar_addr, ptr_void);
|
||||
|
||||
// (2) state = object_getIvar(obj, ivar_handle).
|
||||
const get_ivar_fid = self.ensureCRuntimeDecl("object_getIvar", &.{ ptr_void, ptr_void }, ptr_void);
|
||||
// Pass the Obj-C receiver pointer through to the sx body as
|
||||
// `self`. The body's `self: *Self` type resolves to the
|
||||
// foreign-class stub (the opaque Obj-C type), matching Apple's
|
||||
// Obj-C semantics where `self` IS the object. `self.field`
|
||||
// access on a sx-defined class is rewritten by lowerFieldAccess
|
||||
// to go through `object_getIvar(self, __sx_state_ivar)` and
|
||||
// a struct_gep on the state struct — see M1.2 A.3.
|
||||
const obj_ref = Ref.fromIndex(0);
|
||||
const get_ivar_args = self.alloc.alloc(Ref, 2) catch return;
|
||||
get_ivar_args[0] = obj_ref;
|
||||
get_ivar_args[1] = ivar_handle;
|
||||
const state_ptr = self.builder.emit(.{ .call = .{
|
||||
.callee = get_ivar_fid,
|
||||
.args = get_ivar_args,
|
||||
} }, ptr_void);
|
||||
|
||||
// (3) Call sx body `@<Cls>.<method>(default_ctx, state, ...user_args)`.
|
||||
// Call sx body `@<Cls>.<method>(default_ctx, self, ...user_args)`.
|
||||
const body_name = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ fcd.name, md.name }) catch return;
|
||||
defer self.alloc.free(body_name);
|
||||
const body_fid = self.resolveFuncByName(body_name) orelse return;
|
||||
|
||||
// Locate __sx_default_context global. When implicit_ctx is off
|
||||
// (no std.sx imported), the body has no __sx_ctx param either —
|
||||
// skip the ctx prepend.
|
||||
const ctx_ref: ?Ref = blk: {
|
||||
if (!self.implicit_ctx_enabled) break :blk null;
|
||||
const dctx_gi = self.global_names.get("__sx_default_context") orelse break :blk null;
|
||||
break :blk self.builder.emit(.{ .global_addr = dctx_gi.id }, ptr_void);
|
||||
};
|
||||
|
||||
// Build arg list: [ctx?] + state + user_args.
|
||||
// Build arg list: [ctx?] + self + user_args.
|
||||
const num_user_args = params_slice.len - 2; // minus obj + _cmd
|
||||
const num_call_args = (if (ctx_ref != null) @as(usize, 1) else 0) + 1 + num_user_args;
|
||||
const call_args = self.alloc.alloc(Ref, num_call_args) catch return;
|
||||
@@ -12123,9 +12229,8 @@ pub const Lowering = struct {
|
||||
call_args[idx] = c_ref;
|
||||
idx += 1;
|
||||
}
|
||||
call_args[idx] = state_ptr;
|
||||
call_args[idx] = obj_ref;
|
||||
idx += 1;
|
||||
// User args come from imp params slots 2..N.
|
||||
var ip: usize = 2;
|
||||
while (ip < params_slice.len) : (ip += 1) {
|
||||
call_args[idx] = Ref.fromIndex(@intCast(ip));
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
@OBJC_METH_VAR_TYPE_.21 = private unnamed_addr constant [4 x i8] c"v@:\00"
|
||||
@OBJC_METH_VAR_NAME_.22 = private unnamed_addr constant [6 x i8] c"alloc\00"
|
||||
@OBJC_METH_VAR_TYPE_.23 = private unnamed_addr constant [4 x i8] c"@@:\00"
|
||||
@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @__sx_objc_defined_class_init, ptr null }]
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare void @out(ptr) #0
|
||||
@@ -739,7 +738,9 @@ entry:
|
||||
%alloca = alloca ptr, align 8
|
||||
store ptr %1, ptr %alloca, align 8
|
||||
%load = load ptr, ptr %alloca, align 8
|
||||
%gep = getelementptr inbounds { i32 }, ptr %load, i32 0, i32 0
|
||||
%loadN = load ptr, ptr @__SxFoo_state_ivar, align 8
|
||||
%call = call ptr @object_getIvar(ptr %load, ptr %loadN)
|
||||
%gep = getelementptr inbounds { i32 }, ptr %call, i32 0, i32 0
|
||||
%loadN = load i32, ptr %gep, align 4
|
||||
%add = add i32 %loadN, 1
|
||||
store i32 %add, ptr %gep, align 4
|
||||
@@ -792,6 +793,9 @@ entry:
|
||||
ret { ptr, i64 } %call
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @object_getIvar(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define ptr @__SxFoo_alloc_imp(ptr %0, ptr %1) #0 {
|
||||
entry:
|
||||
@@ -827,9 +831,6 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @object_getIvar(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @sel_registerName(ptr) #0
|
||||
|
||||
@@ -839,9 +840,7 @@ declare void @objc_msgSendSuper2(ptr, ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
define void @__SxFoo_bump_imp(ptr %0, ptr %1) #0 {
|
||||
entry:
|
||||
%load = load ptr, ptr @__SxFoo_state_ivar, align 8
|
||||
%call = call ptr @object_getIvar(ptr %0, ptr %load)
|
||||
call void @SxFoo.bump(ptr @__sx_default_context, ptr %call)
|
||||
call void @SxFoo.bump(ptr @__sx_default_context, ptr %0)
|
||||
ret void
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user