comptime compiler-API: Phase 1 foundation + Phase 2.1 weld plan

Introduce the welded comptime `compiler` library (`#library "compiler"` +
`abi(.zig) extern compiler`), per design/comptime-compiler-api.md, and unify
`callconv(...)` into the new `abi(...)` annotation.

abi(...) replaces callconv(...):
- New ABI enum { default, c, zig, pure }; `abi(.c|.zig|.pure)` parses in the
  postfix slot before extern/export (and standalone). `kw_callconv` -> `kw_abi`.
- Migrated 52 sx files, the call-convention-mismatch diagnostic, and docs
  (readme/specs) from `callconv(.c)` to `abi(.c)`.

Phase 1 — welded compiler library (parse -> registry -> validation -> bridge):
- `abi(.zig) extern compiler` parses on fn decls (carries abi/extern_lib) and
  struct decls (StructDecl.abi/extern_lib).
- `#library "compiler"` is the comptime-only internal surface — never dlopen'd.
- src/ir/compiler_lib.zig: the binding registry (the safety boundary). `Field`
  welded to StructInfo.Field with layout baked from the real Zig type
  (@offsetOf/@sizeOf); `findType`/`findFn`. Welded structs are layout-validated
  at registration (field set + total size) as a header checked against the impl.
- Host-call bridge: a `fn abi(.zig) extern compiler` dispatches under the
  comptime interp to its registered Zig handler (intern/text_of round-trip),
  never dlsym. IR Function.compiler_welded; validated in declareFunction.
- Comptime-only enforcement: a runtime call to a welded fn is a clean
  build-gating error (emitCall), not an undefined-symbol link failure.

Phase 2.1 — byte-layout weld foundation:
- Decision: full byte-layout weld (sx struct laid out byte-identically to the
  bound Zig type). Registered StructInfo (first non-natural / Zig-reordered
  layout). `computeWeldPlan` — pure offset-ordered element plan + padding +
  sx-field->LLVM-element remap; unit-tested. Emit/interp wiring is the next
  sub-step (2.2+, see current/CHECKPOINT-COMPILER-API.md).

Examples: 0625/0626 (welded struct + fn round-trip), 1183/1184/1185
(layout-mismatch, unexported-fn, runtime-call diagnostics).
This commit is contained in:
agra
2026-06-17 13:31:11 +03:00
parent 3a9b508502
commit cd5b958d19
100 changed files with 1490 additions and 298 deletions

View File

@@ -58,7 +58,7 @@ MTLClearColor :: struct {
// MTLRegion is 48 bytes and MTLScissorRect is 32 bytes; both are passed
// by value to the Obj-C runtime, which the compiler marshals as
// `ptr byval(<T>)` via the C-ABI byval coercion. The fn-pointer cast
// must spell `callconv(.c)` so the indirect call applies that coercion.
// must spell `abi(.c)` so the indirect call applies that coercion.
MTLOrigin :: struct { x: u64; y: u64; z: u64; }
MTLSize :: struct { width: u64; height: u64; depth: u64; }
MTLRegion :: struct { origin: MTLOrigin; size: MTLSize; }
@@ -257,11 +257,11 @@ metal_init_ios :: (self: *MetalGPU) -> bool {
if self.device == null { return false; }
}
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;
msg_oo : (*void, *void, *void) -> void abi(.c) = xx objc_msgSend;
msg_ou : (*void, *void, u64) -> void abi(.c) = xx objc_msgSend;
msg_ob : (*void, *void, u8) -> void abi(.c) = xx objc_msgSend;
msg_osize : (*void, *void, CGSize) -> void abi(.c) = xx objc_msgSend;
msg_o : (*void, *void) -> *void abi(.c) = xx objc_msgSend;
if self.queue == null {
self.queue = msg_o(self.device, sel_registerName("newCommandQueue".ptr));
@@ -290,7 +290,7 @@ metal_resize_ios :: (self: *MetalGPU) {
inline if OS != .ios { return; }
if self.layer == null { return; }
msg_osize : (*void, *void, CGSize) -> void callconv(.c) = xx objc_msgSend;
msg_osize : (*void, *void, CGSize) -> void abi(.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);
}
@@ -301,12 +301,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 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;
msg_o : (*void, *void) -> *void abi(.c) = xx objc_msgSend;
msg_oo : (*void, *void, *void) -> void abi(.c) = xx objc_msgSend;
msg_oo_ret : (*void, *void, *void) -> *void abi(.c) = xx objc_msgSend;
msg_ou : (*void, *void, u64) -> void abi(.c) = xx objc_msgSend;
msg_ouret : (*void, *void, u64) -> *void abi(.c) = xx objc_msgSend;
msg_oclear : (*void, *void, MTLClearColor) -> void abi(.c) = xx objc_msgSend;
// drawable = [layer nextDrawable]
self.drawable = msg_o(self.layer, sel_registerName("nextDrawable".ptr));
@@ -353,9 +353,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 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 : (*void, *void) -> void abi(.c) = xx objc_msgSend;
msg_oo : (*void, *void, *void) -> void abi(.c) = xx objc_msgSend;
msg_ood : (*void, *void, *void, f64) -> void abi(.c) = xx objc_msgSend;
msg_v(self.encoder, sel_registerName("endEncoding".ptr));
@@ -386,15 +386,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 callconv(.c) = xx objc_msgSend;
msg_oo : (*void, *void, *void) -> void callconv(.c) = xx objc_msgSend;
msg_oo_r : (*void, *void, *NSString) -> *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;
msg_o : (*void, *void) -> *void abi(.c) = xx objc_msgSend;
msg_oo : (*void, *void, *void) -> void abi(.c) = xx objc_msgSend;
msg_oo_r : (*void, *void, *NSString) -> *void abi(.c) = xx objc_msgSend;
msg_ou : (*void, *void, u64) -> void abi(.c) = xx objc_msgSend;
msg_ouret: (*void, *void, u64) -> *void abi(.c) = xx objc_msgSend;
msg_ob : (*void, *void, u8) -> void abi(.c) = xx objc_msgSend;
// [device newLibraryWithSource:src options:nil error:&err]
msg_lib : (*void, *void, *NSString, *void, **void) -> *void callconv(.c) = xx objc_msgSend;
msg_lib : (*void, *void, *NSString, *void, **void) -> *void abi(.c) = xx objc_msgSend;
err : *void = null;
library := msg_lib(self.device,
sel_registerName("newLibraryWithSource:options:error:".ptr),
@@ -428,7 +428,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 callconv(.c) = xx objc_msgSend;
msg_pipe : (*void, *void, *void, **void) -> *void abi(.c) = xx objc_msgSend;
err2 : *void = null;
state := msg_pipe(self.device,
sel_registerName("newRenderPipelineStateWithDescriptor:error:".ptr),
@@ -452,7 +452,7 @@ metal_create_buffer_ios :: (self: *MetalGPU, size_bytes: i64) -> u32 {
if size_bytes <= 0 { return 0; }
// MTLResourceStorageModeShared is the default (option value 0).
msg_buf : (*void, *void, u64, u64) -> *void callconv(.c) = xx objc_msgSend;
msg_buf : (*void, *void, u64, u64) -> *void abi(.c) = xx objc_msgSend;
buf := msg_buf(self.device,
sel_registerName("newBufferWithLength:options:".ptr),
xx size_bytes, 0);
@@ -469,7 +469,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 callconv(.c) = xx objc_msgSend;
msg_o : (*void, *void) -> *void abi(.c) = xx objc_msgSend;
dst := msg_o(buf, sel_registerName("contents".ptr));
if dst == null { return; }
memcpy(dst, data, size_bytes);
@@ -483,7 +483,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 callconv(.c) = xx objc_msgSend;
msg_o : (*void, *void) -> *void abi(.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`
@@ -530,7 +530,7 @@ metal_create_texture_ios :: (self: *MetalGPU, w: i32, h: i32, format: TextureFor
// [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:width:height:mipmapped:]
MTLTextureDescriptor := objc_getClass("MTLTextureDescriptor".ptr);
msg_desc : (*void, *void, u64, u64, u64, u8) -> *void callconv(.c) = xx objc_msgSend;
msg_desc : (*void, *void, u64, u64, u64, u8) -> *void abi(.c) = xx objc_msgSend;
desc := msg_desc(MTLTextureDescriptor,
sel_registerName("texture2DDescriptorWithPixelFormat:width:height:mipmapped:".ptr),
pixel_format, xx w, xx h, 0);
@@ -539,10 +539,10 @@ metal_create_texture_ios :: (self: *MetalGPU, w: i32, h: i32, 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 callconv(.c) = xx objc_msgSend;
msg_ou_void : (*void, *void, u64) -> void abi(.c) = xx objc_msgSend;
msg_ou_void(desc, sel_registerName("setStorageMode:".ptr), MTL_STORAGE_MODE_SHARED);
msg_oo : (*void, *void, *void) -> *void callconv(.c) = xx objc_msgSend;
msg_oo : (*void, *void, *void) -> *void abi(.c) = xx objc_msgSend;
tex := msg_oo(self.device, sel_registerName("newTextureWithDescriptor:".ptr), desc);
if tex == null { return 0; }
@@ -575,7 +575,7 @@ metal_update_texture_region_ios :: (self: *MetalGPU, handle: u32, x: i32, y: i32
bytes_per_row : u64 = xx (slot.bytes_per_pixel * cast(u32) w);
// [tex replaceRegion:region mipmapLevel:0 withBytes:pixels bytesPerRow:bytes_per_row]
msg_replace : (*void, *void, MTLRegion, u64, *void, u64) -> void callconv(.c) = xx objc_msgSend;
msg_replace : (*void, *void, MTLRegion, u64, *void, u64) -> void abi(.c) = xx objc_msgSend;
msg_replace(slot.tex,
sel_registerName("replaceRegion:mipmapLevel:withBytes:bytesPerRow:".ptr),
region, 0, pixels, bytes_per_row);
@@ -596,7 +596,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 callconv(.c) = xx objc_msgSend;
msg : (*void, *void) -> void abi(.c) = xx objc_msgSend;
msg(obj, sel_registerName("release".ptr));
self.shaders.items[handle - 1] = null;
}
@@ -608,7 +608,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 callconv(.c) = xx objc_msgSend;
msg : (*void, *void) -> void abi(.c) = xx objc_msgSend;
msg(obj, sel_registerName("release".ptr));
self.buffers.items[handle - 1] = null;
}
@@ -620,7 +620,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 callconv(.c) = xx objc_msgSend;
msg : (*void, *void) -> void abi(.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;
@@ -633,7 +633,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 callconv(.c) = xx objc_msgSend;
msg : (*void, *void, *void) -> void abi(.c) = xx objc_msgSend;
msg(self.encoder, sel_registerName("setRenderPipelineState:".ptr), state);
}
@@ -643,7 +643,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 callconv(.c) = xx objc_msgSend;
msg : (*void, *void, *void, u64, u64) -> void abi(.c) = xx objc_msgSend;
msg(self.encoder, sel_registerName("setVertexBuffer:offset:atIndex:".ptr), buf, 0, 0);
}
@@ -656,7 +656,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 callconv(.c) = xx objc_msgSend;
msg : (*void, *void, *void, u64) -> void abi(.c) = xx objc_msgSend;
msg(self.encoder, sel_registerName("setFragmentTexture:atIndex:".ptr), tex, xx slot);
}
@@ -666,7 +666,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 callconv(.c) = xx objc_msgSend;
msg : (*void, *void, *void, u64, u64) -> void abi(.c) = xx objc_msgSend;
msg(self.encoder, sel_registerName("setVertexBytes:length:atIndex:".ptr),
data, xx size_bytes, xx slot);
}
@@ -676,7 +676,7 @@ metal_set_scissor_ios :: (self: *MetalGPU, x: i32, y: i32, w: i32, h: i32) {
if self.encoder == null { return; }
rect : MTLScissorRect = .{ x = xx x, y = xx y, width = xx w, height = xx h };
// [encoder setScissorRect:rect] (MTLScissorRect is 32 bytes → ptr byval)
msg : (*void, *void, MTLScissorRect) -> void callconv(.c) = xx objc_msgSend;
msg : (*void, *void, MTLScissorRect) -> void abi(.c) = xx objc_msgSend;
msg(self.encoder, sel_registerName("setScissorRect:".ptr), rect);
}
@@ -686,7 +686,7 @@ metal_disable_scissor_ios :: (self: *MetalGPU) {
// Metal has no "disable scissor" — set the rect to cover the full
// drawable so subsequent draws aren't clipped.
rect : MTLScissorRect = .{ x = 0, y = 0, width = xx self.pixel_w, height = xx self.pixel_h };
msg : (*void, *void, MTLScissorRect) -> void callconv(.c) = xx objc_msgSend;
msg : (*void, *void, MTLScissorRect) -> void abi(.c) = xx objc_msgSend;
msg(self.encoder, sel_registerName("setScissorRect:".ptr), rect);
}
@@ -695,7 +695,7 @@ metal_draw_triangles_ios :: (self: *MetalGPU, vertex_offset: i32, 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 callconv(.c) = xx objc_msgSend;
msg : (*void, *void, u64, u64, u64) -> void abi(.c) = xx objc_msgSend;
msg(self.encoder, sel_registerName("drawPrimitives:vertexStart:vertexCount:".ptr),
MTL_PRIMITIVE_TYPE_TRIANGLE, xx vertex_offset, xx vertex_count);
}