From d4a342d0c1049bd5fcdd62cac014746f9d7a36f7 Mon Sep 17 00:00:00 2001 From: agra Date: Mon, 25 May 2026 09:35:15 +0300 Subject: [PATCH] =?UTF-8?q?mem:=20implicit-Context=20platform=20fixes=20?= =?UTF-8?q?=E2=80=94=20chess=20green=20on=20macOS/iOS/Android?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verify-step uncovered three categories of regressions where sx code calls into the platform's C ABI through fn-pointer types or as a registered callback. Every site now declares the right convention. C-side calls INTO sx → callconv(.c) on the sx function: - platform/android.sx: sx_android_render_thread_entry is the start routine pthread_create invokes — pthread treats it as a C function. Also annotate the pthread_create signature so the start-routine fn- pointer field rejects mismatching sx fns at compile time. sx code calling typed fn-pointers cast from C symbols → callconv(.c) on the fn-pointer type: - opengl.sx: 55 GL fn-ptr globals + load_gl's proc-loader param. GL trampolines are macOS/iOS/Android system code. - std/objc.sx: the two typed `objc_msgSend` casts. - gpu/metal.sx: ~40 typed `objc_msgSend` casts across Metal command encoder / device / pipeline construction. The block invoke trampolines (objc_block.sx) call back INTO sx (the closure trampoline). The typed fn-ptr there stays default-conv so ctx prepends correctly. Compiler change: a callconv(.c) sx function now binds `current_ctx_ref` to `&__sx_default_context` at entry (used to be gated by `isExportedEntryName`). C-callable sx callbacks like the block invokes don't get their own __sx_ctx param but their bodies still need a real Context to forward to the closure they delegate to. Tests: 152/152 example suite + chess green on all 3 platforms. Screenshots at /tmp/sx-game-{macos,iossim,android}.png. --- library/modules/gpu/metal.sx | 74 +++++++++---------- library/modules/opengl.sx | 108 ++++++++++++++-------------- library/modules/platform/android.sx | 4 +- library/modules/std/objc.sx | 4 +- library/modules/std/objc_block.sx | 4 ++ src/ir/lower.zig | 15 ++-- 6 files changed, 109 insertions(+), 100 deletions(-) diff --git a/library/modules/gpu/metal.sx b/library/modules/gpu/metal.sx index 8e9d1ea..f3d7304 100644 --- a/library/modules/gpu/metal.sx +++ b/library/modules/gpu/metal.sx @@ -250,11 +250,11 @@ metal_init_ios :: (self: *MetalGPU) -> bool { if self.device == null { return false; } } - msg_oo : (*void, *void, *void) -> void = xx objc_msgSend; - msg_ou : (*void, *void, u64) -> void = xx objc_msgSend; - msg_ob : (*void, *void, u8) -> void = xx objc_msgSend; - msg_osize : (*void, *void, CGSize) -> void = xx objc_msgSend; - msg_o : (*void, *void) -> *void = xx objc_msgSend; + msg_oo : (*void, *void, *void) -> void callconv(.c) = xx objc_msgSend; + msg_ou : (*void, *void, u64) -> void callconv(.c) = xx objc_msgSend; + msg_ob : (*void, *void, u8) -> void callconv(.c) = xx objc_msgSend; + msg_osize : (*void, *void, CGSize) -> void callconv(.c) = xx objc_msgSend; + msg_o : (*void, *void) -> *void callconv(.c) = xx objc_msgSend; if self.queue == null { self.queue = msg_o(self.device, sel_registerName("newCommandQueue".ptr)); @@ -283,7 +283,7 @@ metal_resize_ios :: (self: *MetalGPU) { inline if OS != .ios { return; } if self.layer == null { return; } - msg_osize : (*void, *void, CGSize) -> void = xx objc_msgSend; + msg_osize : (*void, *void, CGSize) -> void callconv(.c) = xx objc_msgSend; size := CGSize.{ width = xx self.pixel_w, height = xx self.pixel_h }; msg_osize(self.layer, sel_registerName("setDrawableSize:".ptr), size); } @@ -294,12 +294,12 @@ metal_begin_frame_ios :: (self: *MetalGPU, clear: ClearColor) -> bool { if self.queue == null { return false; } if self.pixel_w <= 0 or self.pixel_h <= 0 { return false; } - msg_o : (*void, *void) -> *void = xx objc_msgSend; - msg_oo : (*void, *void, *void) -> void = xx objc_msgSend; - msg_oo_ret : (*void, *void, *void) -> *void = xx objc_msgSend; - msg_ou : (*void, *void, u64) -> void = xx objc_msgSend; - msg_ouret : (*void, *void, u64) -> *void = xx objc_msgSend; - msg_oclear : (*void, *void, MTLClearColor) -> void = xx objc_msgSend; + msg_o : (*void, *void) -> *void callconv(.c) = xx objc_msgSend; + msg_oo : (*void, *void, *void) -> void callconv(.c) = xx objc_msgSend; + msg_oo_ret : (*void, *void, *void) -> *void callconv(.c) = xx objc_msgSend; + msg_ou : (*void, *void, u64) -> void callconv(.c) = xx objc_msgSend; + msg_ouret : (*void, *void, u64) -> *void callconv(.c) = xx objc_msgSend; + msg_oclear : (*void, *void, MTLClearColor) -> void callconv(.c) = xx objc_msgSend; // drawable = [layer nextDrawable] self.drawable = msg_o(self.layer, sel_registerName("nextDrawable".ptr)); @@ -346,9 +346,9 @@ metal_end_frame_ios :: (self: *MetalGPU, target_time: f64) { if self.cmd_buffer == null { return; } if self.drawable == null { return; } - msg_v : (*void, *void) -> void = xx objc_msgSend; - msg_oo : (*void, *void, *void) -> void = xx objc_msgSend; - msg_ood : (*void, *void, *void, f64) -> void = xx objc_msgSend; + msg_v : (*void, *void) -> void callconv(.c) = xx objc_msgSend; + msg_oo : (*void, *void, *void) -> void callconv(.c) = xx objc_msgSend; + msg_ood : (*void, *void, *void, f64) -> void callconv(.c) = xx objc_msgSend; msg_v(self.encoder, sel_registerName("endEncoding".ptr)); @@ -379,15 +379,15 @@ metal_create_shader_ios :: (self: *MetalGPU, src: string) -> u32 { inline if OS != .ios { return 0; } if self.device == null { return 0; } - msg_o : (*void, *void) -> *void = xx objc_msgSend; - msg_oo : (*void, *void, *void) -> void = xx objc_msgSend; - msg_oo_r : (*void, *void, *void) -> *void = xx objc_msgSend; - msg_ou : (*void, *void, u64) -> void = xx objc_msgSend; - msg_ouret: (*void, *void, u64) -> *void = xx objc_msgSend; - msg_ob : (*void, *void, u8) -> void = xx objc_msgSend; + msg_o : (*void, *void) -> *void callconv(.c) = xx objc_msgSend; + msg_oo : (*void, *void, *void) -> void callconv(.c) = xx objc_msgSend; + msg_oo_r : (*void, *void, *void) -> *void callconv(.c) = xx objc_msgSend; + msg_ou : (*void, *void, u64) -> void callconv(.c) = xx objc_msgSend; + msg_ouret: (*void, *void, u64) -> *void callconv(.c) = xx objc_msgSend; + msg_ob : (*void, *void, u8) -> void callconv(.c) = xx objc_msgSend; // [device newLibraryWithSource:src options:nil error:&err] - msg_lib : (*void, *void, *void, *void, **void) -> *void = xx objc_msgSend; + msg_lib : (*void, *void, *void, *void, **void) -> *void callconv(.c) = xx objc_msgSend; src_ns := ns_string(src.ptr); err : *void = null; library := msg_lib(self.device, @@ -422,7 +422,7 @@ metal_create_shader_ios :: (self: *MetalGPU, src: string) -> u32 { msg_ou(att0, sel_registerName("setSourceAlphaBlendFactor:".ptr), MTL_BLEND_FACTOR_SRC_ALPHA); msg_ou(att0, sel_registerName("setDestinationAlphaBlendFactor:".ptr), MTL_BLEND_FACTOR_ONE_MINUS_SRC_A); - msg_pipe : (*void, *void, *void, **void) -> *void = xx objc_msgSend; + msg_pipe : (*void, *void, *void, **void) -> *void callconv(.c) = xx objc_msgSend; err2 : *void = null; state := msg_pipe(self.device, sel_registerName("newRenderPipelineStateWithDescriptor:error:".ptr), @@ -446,7 +446,7 @@ metal_create_buffer_ios :: (self: *MetalGPU, size_bytes: s64) -> u32 { if size_bytes <= 0 { return 0; } // MTLResourceStorageModeShared is the default (option value 0). - msg_buf : (*void, *void, u64, u64) -> *void = xx objc_msgSend; + msg_buf : (*void, *void, u64, u64) -> *void callconv(.c) = xx objc_msgSend; buf := msg_buf(self.device, sel_registerName("newBufferWithLength:options:".ptr), xx size_bytes, 0); @@ -463,7 +463,7 @@ metal_update_buffer_ios :: (self: *MetalGPU, handle: u32, data: *void, size_byte if data == null { return; } if size_bytes <= 0 { return; } - msg_o : (*void, *void) -> *void = xx objc_msgSend; + msg_o : (*void, *void) -> *void callconv(.c) = xx objc_msgSend; dst := msg_o(buf, sel_registerName("contents".ptr)); if dst == null { return; } memcpy(dst, data, size_bytes); @@ -477,7 +477,7 @@ metal_update_buffer_at_ios :: (self: *MetalGPU, handle: u32, data: *void, size_b if size_bytes <= 0 { return; } if byte_offset < 0 { return; } - msg_o : (*void, *void) -> *void = xx objc_msgSend; + msg_o : (*void, *void) -> *void callconv(.c) = xx objc_msgSend; base := msg_o(buf, sel_registerName("contents".ptr)); if base == null { return; } // Add byte_offset via integer arithmetic — `@dst[i]` on `[*]u8` @@ -524,7 +524,7 @@ metal_create_texture_ios :: (self: *MetalGPU, w: s32, h: s32, format: TextureFor // [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:width:height:mipmapped:] MTLTextureDescriptor := objc_getClass("MTLTextureDescriptor".ptr); - msg_desc : (*void, *void, u64, u64, u64, u8) -> *void = xx objc_msgSend; + msg_desc : (*void, *void, u64, u64, u64, u8) -> *void callconv(.c) = xx objc_msgSend; desc := msg_desc(MTLTextureDescriptor, sel_registerName("texture2DDescriptorWithPixelFormat:width:height:mipmapped:".ptr), pixel_format, xx w, xx h, 0); @@ -533,10 +533,10 @@ metal_create_texture_ios :: (self: *MetalGPU, w: s32, h: s32, format: TextureFor // Force shared storage so the CPU can keep writing pixels (atlas updates, // sprite uploads). On iOS-sim under Apple Silicon the convenience class // method's default storage isn't reliably shared for every format. - msg_ou_void : (*void, *void, u64) -> void = xx objc_msgSend; + msg_ou_void : (*void, *void, u64) -> void callconv(.c) = xx objc_msgSend; msg_ou_void(desc, sel_registerName("setStorageMode:".ptr), MTL_STORAGE_MODE_SHARED); - msg_oo : (*void, *void, *void) -> *void = xx objc_msgSend; + msg_oo : (*void, *void, *void) -> *void callconv(.c) = xx objc_msgSend; tex := msg_oo(self.device, sel_registerName("newTextureWithDescriptor:".ptr), desc); if tex == null { return 0; } @@ -590,7 +590,7 @@ metal_destroy_shader_ios :: (self: *MetalGPU, handle: u32) { if h64 > self.shaders.len { return; } obj := self.shaders.items[handle - 1]; if obj == null { return; } - msg : (*void, *void) -> void = xx objc_msgSend; + msg : (*void, *void) -> void callconv(.c) = xx objc_msgSend; msg(obj, sel_registerName("release".ptr)); self.shaders.items[handle - 1] = null; } @@ -602,7 +602,7 @@ metal_destroy_buffer_ios :: (self: *MetalGPU, handle: u32) { if h64 > self.buffers.len { return; } obj := self.buffers.items[handle - 1]; if obj == null { return; } - msg : (*void, *void) -> void = xx objc_msgSend; + msg : (*void, *void) -> void callconv(.c) = xx objc_msgSend; msg(obj, sel_registerName("release".ptr)); self.buffers.items[handle - 1] = null; } @@ -614,7 +614,7 @@ metal_destroy_texture_ios :: (self: *MetalGPU, handle: u32) { if h64 > self.textures.len { return; } obj := self.textures.items[handle - 1].tex; if obj == null { return; } - msg : (*void, *void) -> void = xx objc_msgSend; + msg : (*void, *void) -> void callconv(.c) = xx objc_msgSend; msg(obj, sel_registerName("release".ptr)); self.textures.items[handle - 1].tex = null; self.textures.items[handle - 1].bytes_per_pixel = 0; @@ -627,7 +627,7 @@ metal_set_shader_ios :: (self: *MetalGPU, sh: u32) { if self.encoder == null { return; } state := metal_lookup_shader(self, sh); if state == null { return; } - msg : (*void, *void, *void) -> void = xx objc_msgSend; + msg : (*void, *void, *void) -> void callconv(.c) = xx objc_msgSend; msg(self.encoder, sel_registerName("setRenderPipelineState:".ptr), state); } @@ -637,7 +637,7 @@ metal_set_vertex_buffer_ios :: (self: *MetalGPU, h: u32) { buf := metal_lookup_buffer(self, h); if buf == null { return; } // [encoder setVertexBuffer:buf offset:0 atIndex:0] - msg : (*void, *void, *void, u64, u64) -> void = xx objc_msgSend; + msg : (*void, *void, *void, u64, u64) -> void callconv(.c) = xx objc_msgSend; msg(self.encoder, sel_registerName("setVertexBuffer:offset:atIndex:".ptr), buf, 0, 0); } @@ -650,7 +650,7 @@ metal_set_texture_ios :: (self: *MetalGPU, slot: u32, h: u32) { tex := self.textures.items[h - 1].tex; if tex == null { return; } // [encoder setFragmentTexture:tex atIndex:slot] - msg : (*void, *void, *void, u64) -> void = xx objc_msgSend; + msg : (*void, *void, *void, u64) -> void callconv(.c) = xx objc_msgSend; msg(self.encoder, sel_registerName("setFragmentTexture:atIndex:".ptr), tex, xx slot); } @@ -660,7 +660,7 @@ metal_set_vertex_constants_ios :: (self: *MetalGPU, slot: u32, data: *void, size if data == null { return; } if size_bytes <= 0 { return; } // [encoder setVertexBytes:data length:size_bytes atIndex:slot] - msg : (*void, *void, *void, u64, u64) -> void = xx objc_msgSend; + msg : (*void, *void, *void, u64, u64) -> void callconv(.c) = xx objc_msgSend; msg(self.encoder, sel_registerName("setVertexBytes:length:atIndex:".ptr), data, xx size_bytes, xx slot); } @@ -689,7 +689,7 @@ metal_draw_triangles_ios :: (self: *MetalGPU, vertex_offset: s32, vertex_count: if self.encoder == null { return; } if vertex_count <= 0 { return; } // [encoder drawPrimitives:.triangle vertexStart:offset vertexCount:count] - msg : (*void, *void, u64, u64, u64) -> void = xx objc_msgSend; + msg : (*void, *void, u64, u64, u64) -> void callconv(.c) = xx objc_msgSend; msg(self.encoder, sel_registerName("drawPrimitives:vertexStart:vertexCount:".ptr), MTL_PRIMITIVE_TYPE_TRIANGLE, xx vertex_offset, xx vertex_count); } diff --git a/library/modules/opengl.sx b/library/modules/opengl.sx index 8ff7d1d..6dd9399 100644 --- a/library/modules/opengl.sx +++ b/library/modules/opengl.sx @@ -25,39 +25,39 @@ GL_LINE :u32: 0x1B01; GL_FILL :u32: 0x1B02; // Function pointer variables (mutable, loaded at runtime) -glClearColor : (f32, f32, f32, f32) -> void = ---; -glClear : (u32) -> void = ---; -glEnable : (u32) -> void = ---; -glDisable : (u32) -> void = ---; -glViewport : (s32, s32, s32, s32) -> void = ---; -glFlush : () -> void = ---; -glDrawArrays : (u32, s32, s32) -> void = ---; -glPolygonMode : (u32, u32) -> void = ---; -glLineWidth : (f32) -> void = ---; -glCreateShader : (u32) -> u32 = ---; -glShaderSource : (u32, s32, *[:0]u8, *s32) -> void = ---; -glCompileShader : (u32) -> void = ---; -glGetShaderiv : (u32, u32, *s32) -> void = ---; -glGetShaderInfoLog : (u32, s32, *s32, [*]u8) -> void = ---; -glCreateProgram : () -> u32 = ---; -glAttachShader : (u32, u32) -> void = ---; -glLinkProgram : (u32) -> void = ---; -glGetProgramiv : (u32, u32, *s32) -> void = ---; -glGetProgramInfoLog : (u32, s32, *s32, [*]u8) -> void = ---; -glUseProgram : (u32) -> void = ---; -glDeleteShader : (u32) -> void = ---; -glGenVertexArrays : (s32, *u32) -> void = ---; -glGenBuffers : (s32, *u32) -> void = ---; -glBindVertexArray : (u32) -> void = ---; -glBindBuffer : (u32, u32) -> void = ---; -glBufferData : (u32, isize, *void, u32) -> void = ---; -glVertexAttribPointer : (u32, s32, u32, u8, s32, *void) -> void = ---; -glEnableVertexAttribArray : (u32) -> void = ---; -glGetUniformLocation : (u32, [*]u8) -> s32 = ---; -glUniformMatrix4fv : (s32, s32, u8, [*]f32) -> void = ---; -glUniform3f : (s32, f32, f32, f32) -> void = ---; -glDepthFunc : (u32) -> void = ---; -glUniform1f : (s32, f32) -> void = ---; +glClearColor : (f32, f32, f32, f32) -> void callconv(.c) = ---; +glClear : (u32) -> void callconv(.c) = ---; +glEnable : (u32) -> void callconv(.c) = ---; +glDisable : (u32) -> void callconv(.c) = ---; +glViewport : (s32, s32, s32, s32) -> void callconv(.c) = ---; +glFlush : () -> void callconv(.c) = ---; +glDrawArrays : (u32, s32, s32) -> void callconv(.c) = ---; +glPolygonMode : (u32, u32) -> void callconv(.c) = ---; +glLineWidth : (f32) -> void callconv(.c) = ---; +glCreateShader : (u32) -> u32 callconv(.c) = ---; +glShaderSource : (u32, s32, *[:0]u8, *s32) -> void callconv(.c) = ---; +glCompileShader : (u32) -> void callconv(.c) = ---; +glGetShaderiv : (u32, u32, *s32) -> void callconv(.c) = ---; +glGetShaderInfoLog : (u32, s32, *s32, [*]u8) -> void callconv(.c) = ---; +glCreateProgram : () -> u32 callconv(.c) = ---; +glAttachShader : (u32, u32) -> void callconv(.c) = ---; +glLinkProgram : (u32) -> void callconv(.c) = ---; +glGetProgramiv : (u32, u32, *s32) -> void callconv(.c) = ---; +glGetProgramInfoLog : (u32, s32, *s32, [*]u8) -> void callconv(.c) = ---; +glUseProgram : (u32) -> void callconv(.c) = ---; +glDeleteShader : (u32) -> void callconv(.c) = ---; +glGenVertexArrays : (s32, *u32) -> void callconv(.c) = ---; +glGenBuffers : (s32, *u32) -> void callconv(.c) = ---; +glBindVertexArray : (u32) -> void callconv(.c) = ---; +glBindBuffer : (u32, u32) -> void callconv(.c) = ---; +glBufferData : (u32, isize, *void, u32) -> void callconv(.c) = ---; +glVertexAttribPointer : (u32, s32, u32, u8, s32, *void) -> void callconv(.c) = ---; +glEnableVertexAttribArray : (u32) -> void callconv(.c) = ---; +glGetUniformLocation : (u32, [*]u8) -> s32 callconv(.c) = ---; +glUniformMatrix4fv : (s32, s32, u8, [*]f32) -> void callconv(.c) = ---; +glUniform3f : (s32, f32, f32, f32) -> void callconv(.c) = ---; +glDepthFunc : (u32) -> void callconv(.c) = ---; +glUniform1f : (s32, f32) -> void callconv(.c) = ---; GL_LESS :u32: 0x0201; GL_LEQUAL :u32: 0x0203; GL_SCISSOR_TEST :u32: 0x0C11; @@ -76,27 +76,27 @@ GL_RED :u32: 0x1903; GL_R8 :u32: 0x8229; GL_UNPACK_ALIGNMENT :u32: 0x0CF5; -glScissor : (s32, s32, s32, s32) -> void = ---; -glBufferSubData : (u32, isize, isize, *void) -> void = ---; -glGenTextures : (s32, *u32) -> void = ---; -glBindTexture : (u32, u32) -> void = ---; -glTexImage2D : (u32, s32, s32, s32, s32, s32, u32, u32, *void) -> void = ---; -glTexParameteri : (u32, u32, s32) -> void = ---; -glBlendFunc : (u32, u32) -> void = ---; -glReadPixels : (s32, s32, s32, s32, u32, u32, *void) -> void = ---; -glActiveTexture : (u32) -> void = ---; -glUniform1i : (s32, s32) -> void = ---; -glPixelStorei : (u32, s32) -> void = ---; -glTexSubImage2D : (u32, s32, s32, s32, s32, s32, u32, u32, *void) -> void = ---; -glDeleteTextures : (s32, *u32) -> void = ---; +glScissor : (s32, s32, s32, s32) -> void callconv(.c) = ---; +glBufferSubData : (u32, isize, isize, *void) -> void callconv(.c) = ---; +glGenTextures : (s32, *u32) -> void callconv(.c) = ---; +glBindTexture : (u32, u32) -> void callconv(.c) = ---; +glTexImage2D : (u32, s32, s32, s32, s32, s32, u32, u32, *void) -> void callconv(.c) = ---; +glTexParameteri : (u32, u32, s32) -> void callconv(.c) = ---; +glBlendFunc : (u32, u32) -> void callconv(.c) = ---; +glReadPixels : (s32, s32, s32, s32, u32, u32, *void) -> void callconv(.c) = ---; +glActiveTexture : (u32) -> void callconv(.c) = ---; +glUniform1i : (s32, s32) -> void callconv(.c) = ---; +glPixelStorei : (u32, s32) -> void callconv(.c) = ---; +glTexSubImage2D : (u32, s32, s32, s32, s32, s32, u32, u32, *void) -> void callconv(.c) = ---; +glDeleteTextures : (s32, *u32) -> void callconv(.c) = ---; -glGenFramebuffers : (s32, *u32) -> void = ---; -glGenRenderbuffers : (s32, *u32) -> void = ---; -glBindFramebuffer : (u32, u32) -> void = ---; -glBindRenderbuffer : (u32, u32) -> void = ---; -glFramebufferRenderbuffer : (u32, u32, u32, u32) -> void = ---; -glGetRenderbufferParameteriv : (u32, u32, *s32) -> void = ---; -glCheckFramebufferStatus : (u32) -> u32 = ---; +glGenFramebuffers : (s32, *u32) -> void callconv(.c) = ---; +glGenRenderbuffers : (s32, *u32) -> void callconv(.c) = ---; +glBindFramebuffer : (u32, u32) -> void callconv(.c) = ---; +glBindRenderbuffer : (u32, u32) -> void callconv(.c) = ---; +glFramebufferRenderbuffer : (u32, u32, u32, u32) -> void callconv(.c) = ---; +glGetRenderbufferParameteriv : (u32, u32, *s32) -> void callconv(.c) = ---; +glCheckFramebufferStatus : (u32) -> u32 callconv(.c) = ---; GL_TEXTURE_WRAP_S :u32: 0x2802; GL_TEXTURE_WRAP_T :u32: 0x2803; @@ -104,7 +104,7 @@ GL_CLAMP_TO_EDGE :u32: 0x812F; // Loader: call once after creating GL context // Pass in a proc loader (e.g. SDL_GL_GetProcAddress) -load_gl :: (get_proc: ([*]u8) -> *void) { +load_gl :: (get_proc: ([*]u8) -> *void callconv(.c)) { glClearColor = xx get_proc("glClearColor"); glClear = xx get_proc("glClear"); glEnable = xx get_proc("glEnable"); diff --git a/library/modules/platform/android.sx b/library/modules/platform/android.sx index c39dc37..8baf424 100644 --- a/library/modules/platform/android.sx +++ b/library/modules/platform/android.sx @@ -85,7 +85,7 @@ ANativeWindow_setBuffersGeometry :: (w: *void, width: s32, height: s32, fmt: s32 AAssetManager_fromJava :: (env: *void, mgr: *void) -> *void #foreign; // pthread (link libpthread is built into bionic). -pthread_create :: (thread: *u64, attr: *void, start: (*void) -> *void, arg: *void) -> s32 #foreign; +pthread_create :: (thread: *u64, attr: *void, start: (*void) -> *void callconv(.c), arg: *void) -> s32 #foreign; pthread_mutex_init :: (m: *void, attr: *void) -> s32 #foreign; pthread_mutex_lock :: (m: *void) -> s32 #foreign; pthread_mutex_unlock :: (m: *void) -> s32 #foreign; @@ -254,7 +254,7 @@ sx_android_start_render_thread :: (plat: *AndroidPlatform, entry_fn: () -> void) plat.render_thread_started = true; } -sx_android_render_thread_entry :: (arg: *void) -> *void { +sx_android_render_thread_entry :: (arg: *void) -> *void callconv(.c) { plat : *AndroidPlatform = xx arg; while plat.app_window == null and !plat.should_stop { usleep(1000); diff --git a/library/modules/std/objc.sx b/library/modules/std/objc.sx index fcdb020..704e6a9 100644 --- a/library/modules/std/objc.sx +++ b/library/modules/std/objc.sx @@ -62,7 +62,7 @@ NSLog :: (fmt: *void) #foreign; ns_string :: (s: [*]u8) -> *void { cls := objc_getClass("NSString".ptr); sel := sel_registerName("stringWithUTF8String:".ptr); - fn_ptr : (*void, *void, [*]u8) -> *void = xx objc_msgSend; + fn_ptr : (*void, *void, [*]u8) -> *void callconv(.c) = xx objc_msgSend; return fn_ptr(cls, sel, s); } @@ -70,6 +70,6 @@ ns_string :: (s: [*]u8) -> *void { // tied to the NSString; don't free it. c_string :: (ns: *void) -> [*]u8 { sel := sel_registerName("UTF8String".ptr); - fn_ptr : (*void, *void) -> [*]u8 = xx objc_msgSend; + fn_ptr : (*void, *void) -> [*]u8 callconv(.c) = xx objc_msgSend; return fn_ptr(ns, sel); } diff --git a/library/modules/std/objc_block.sx b/library/modules/std/objc_block.sx index 1a0c24e..9c9825b 100644 --- a/library/modules/std/objc_block.sx +++ b/library/modules/std/objc_block.sx @@ -59,6 +59,10 @@ __sx_block_descriptor : BlockDescriptor = .{ // Signature: `void (^)(void)` — no args, no return. The single most // common Apple block shape (UIView animation bodies, dispatch_async, etc). __block_invoke_void :: (block_self: *Block) callconv(.c) { + // `sx_fn` is the closure trampoline — an sx-side function with the + // implicit __sx_ctx at slot 0 and env at slot 1. We're a callconv(.c) + // entry, so the call site needs ctx prepended; the typed fn-pointer + // type stays default-conv to enable that. typed_fn : (*void) -> void = xx block_self.sx_fn; typed_fn(block_self.sx_env); } diff --git a/src/ir/lower.zig b/src/ir/lower.zig index ab2ab03..c206eef 100644 --- a/src/ir/lower.zig +++ b/src/ir/lower.zig @@ -1006,9 +1006,12 @@ pub const Lowering = struct { scope.put(p.name, .{ .ref = slot, .ty = pty, .is_alloca = true }); } - // Inbound entry points: bind current_ctx_ref to the static default - // before any user code runs. - if (!wants_ctx and self.implicit_ctx_enabled and isExportedEntryName(name)) { + // Inbound entry points and callconv(.c) sx functions: bind + // current_ctx_ref to the static default before any user code + // runs. C-callable sx functions don't get a __sx_ctx param, + // but their bodies may call ctx-aware sx functions / fn-ptrs + // and need a real Context to forward. + if (!wants_ctx and self.implicit_ctx_enabled) { if (self.global_names.get("__sx_default_context")) |dctx_gi| { self.current_ctx_ref = self.builder.emit(.{ .global_addr = dctx_gi.id }, self.module.types.ptrTo(.void)); } @@ -1147,8 +1150,10 @@ pub const Lowering = struct { scope.put(p.name, .{ .ref = slot, .ty = pty, .is_alloca = true }); } - // Inbound entry points: bind current_ctx_ref to &__sx_default_context. - if (!wants_ctx_lf and self.implicit_ctx_enabled and isExportedEntryName(name)) { + // Inbound entry points + callconv(.c) sx functions: bind + // current_ctx_ref to &__sx_default_context. See companion comment + // in `lowerFunction` for the same case. + if (!wants_ctx_lf and self.implicit_ctx_enabled) { if (self.global_names.get("__sx_default_context")) |dctx_gi| { self.current_ctx_ref = self.builder.emit(.{ .global_addr = dctx_gi.id }, self.module.types.ptrTo(.void)); }