- Zig master branch'ine LLVM backend'inde ABI dışı tamsayı işleme iyileştirmeleri ve yeni
@bitCastsemantiği eklendi; böylece optimizasyon sorunları ve dil davranışındaki tutarsızlıklar birlikte ele alındı u4,i13,u40gibi keyfi bit genişliğine sahip tamsayılar, SSA değerlerinde bit-int olarak ele alınırken belleğe yazılırken ABI boyutlu tamsayılara genişletilecek şekilde değiştirildi- Eski
@bitCast, bellek baytlarını yeniden yorumlamaya daha yakındı; yeni tanım ise türün mantıksal bit dizisini temel alarak endian bağımlılığını azaltıyor - Değişiklik LLVM ve C backend'leri ile
comptimeyürütmesine kadar genişletildi; standart kütüphane, derleyici vecompiler_rtiçindeki ilgili kullanımlar da birlikte gözden geçirildi - LLVM'nin kaçırdığı optimizasyonlar geri kazanılırken Zig derleyicisinin kendisinde yaklaşık %5 performans artışı gözlemlendi; 0.17.0'da bazı çalışma zamanı performans iyileştirmeleri bekleniyor
LLVM backend'inde keyfi bit genişliğine sahip tamsayı işleme değişikliği
- Zig, daha önce
u4,i13,u40gibi keyfi bit genişliğine sahip tamsayı türlerini LLVM IR'nin bit-int türleri olani4,i13,i40'a doğrudan lowering ediyordu - Bu yaklaşım, LLVM'nin bellek gösterimi semantiği nedeniyle optimize edicide gereksiz kısıtlar oluşturuyordu ve Clang bu tür LLVM IR üretmediği için LLVM içindeki bu yollar da yeterince test edilmiyordu
- Son birkaç yılda gerçekten kaçırılan optimizasyonlar ve miscompilation örnekleri gözlemlendi
- Yeni yaklaşım, SSA değerleri üzerinde işlem yaparken bit-int türlerini koruyor; ancak belleğe yazarken
i8,i16,i32gibi ABI boyutlu türlere zero-extend veya sign-extend uyguluyor - Bu lowering, Clang'in C'deki
_BitInt(N)için kullandığı lowering ile uyumlu; dolayısıyla LLVM içinde daha iyi desteklenen bir yol olması bekleniyor
Eski @bitCast'in sınırları
- Eski
@bitCast, kavramsal olarak şu davranışa daha yakındı- İşlenen değerin pointer'ını almak
- Bu pointer'ı hedef türün pointer'ına cast etmek
- O pointer'dan değeri load etmek
- Yani eski tanım, türün mantıksal yapısından çok bellekteki baytların yeniden yorumlanmasına yakındı
- Zamanla gerçek davranış bu tanımdan uzaklaştı ve çoğu hedefte
@sizeOf(u24)değeri@sizeOf([3]u8)değerinden büyük olmasına rağmen[3]u8'iu24'e@bitCastetmek yine de mümkün oldu - LLVM backend'i yeterince spesifik tanımlanmamış
@bitCastsemantiğini uyguluyordu; tamsayı türlerinin belleğe yazılma biçimi değişince derleyici test paketinde Illegal Behavior ve çökme durumları ortaya çıktı - LLVM backend'ine eski davranışı taklit eden ek mantık eklemek yerine, yeni
@bitCasttanımını genel olarak uygulama yönü seçildi
Yeni @bitCast semantiği
- Yeni semantik, 2024'te sunulup kabul edilen dil önerisi #19755 temel alınarak oluşturuldu
- Bu semantik zaten self-hosted x86_64 backend'inde uygulanmıştı; bu değişiklikle birlikte LLVM ve C backend'lerine ve
comptimeyürütmesine de genişletildi - Yeni
@bitCast, bellek baytlarına göre değil, türü mantıksal olarak temsil eden bit sırasına göre çalışıyoru5, least-significant bit'ten most-significant bit'e doğru 5 mantıksal bitten oluşur[2]u5, ilk elemanın 5 bitinin ardından ikinci elemanın 5 bitinin geldiği 10 mantıksal bitten oluşur
u8'i aynı boyuttakii8'e dönüştürmek gibi basit tamsayılar arası dönüşümlerde bitler aynen korunur ve en üst bit işaret biti olarak yorumlanır- Tamsayı türleri ile
packed structveyapacked unionarasındaki@bitCastsemantiği de korunuyor
Dizi ve vektörlerde değişen davranış
- Yeni semantiğin eskisinden ayrıldığı nokta, diziler ve vektörler gibi aggregate türlerin işin içine girdiği durumlar
- Örneğin
[2]u8'iu16'ya@bitCastettiğinizde eski semantikte sonuç hedefin endian düzenine göre değişiyordu- big-endian hedeflerde ilk dizi elemanı üst 8 bit oluyordu
- little-endian hedeflerde ilk dizi elemanı alt 8 bit oluyordu
- Yeni semantik yalnızca mantıksal bit gösterimini dikkate aldığı için endian'dan bağımsız; tüm hedeflerde ilk dizi elemanı alt 8 bit oluyor
- Genel olarak, little-endian hedeflerdeki eski davranışa daha yakın
[2]u3'ü@Vector(3, u2)'ye dönüştürmek gibi alışılmadık dönüşümler de mümkün- Dizinin mantıksal bitleri birleştirildikten sonra 2 bitlik birimler halinde okunup vektör elemanları oluşturuluyor
- Bir tamsayıyı
@Vector(n, u1)'e@bitCastederek tek tek bitlerden oluşan bir vektöre ayırmak için de kullanılabiliyor
Birlikte uygulanan öneriler ve geçiş süreci
- Bu çalışma sırasında
@bitCastile ilgili kabul edilmiş küçük öneriler de hayata geçirildi - Yeni semantik, eski semantikten anlamlı biçimde farklı olduğu için standart kütüphane, derleyici ve
compiler_rtgibi destek kütüphanelerindeki@bitCastkullanımları gözden geçirildi - İlgili PR codeberg.org/ziglang/zig/pulls/35711; master'a merge edilmesiyle birlikte çeşitli issue'lar da kapatıldı
- Değişen semantik ve önerilen geçiş adımları, Zig 0.17.0 release notes içinde özetlenecek
0.17.0'da beklenen performans etkisi
- Başlangıçtaki hedef olan LLVM backend'inde ABI dışı tamsayı lowering değişikliği, kaçırılan optimizasyonları geri kazanmada başarılı oldu
- İlgili sonuçlar demonstrably successful bağlantısında görülebilir
- Zig derleyicisinin kendisi dahili olarak keyfi bit genişliğine sahip tamsayıları çok sık kullanmasa da, daha iyi optimizasyonlar sayesinde yaklaşık %5 performans artışı gösterdi
- 0.17.0'da bazı kodlarda küçük çalışma zamanı performans iyileştirmeleri görülebilir
1 yorum
Lobste.rs yorumları
Yazıda sözü edilen mantıksal bit gösteriminin endian’dan bağımsız olduğu söyleniyor, ancak gerçek açıklama büyük endian bit sırasını ya da bayt sırasını desteklemeyen bariz bir küçük endian yaklaşımı gibi görünüyor
25 Haziran 2026 tarihli yeni geliştirme günlüğü; yeni
@bitCastsemantiğinin ve LLVM arka uç iyileştirmelerinin yakın tarihli bir pull request’e birleştirildiğini anlatıyorİlginç, ama nadiren test edilen büyük endian hedeflerde aşağıdaki gibi yazılmış kodların birden bozulabileceğini düşündürüyor
Zig dışı sözde kodla yazarsak:
Aslında büyük bir sorun olmayacak gibi; Zig deposundaki binlerce
@bitCastiçinden bu değişiklikten etkilenenlerin 100’den çok daha az olduğunu sanıyorumAçıkçası çoğu Zig kullanıcısının dizi/vektör ile skaler arasındaki dönüşümlerde
@bitCast’in nasıl davrandığını tam olarak bildiğini de düşünmüyorum. Önceden yalnızca yazarın sisteminde test edilip sadece küçük endian’da çalışan kodların, artık her yerde çalışır hale gelmesi de çok olasıEski bir C programcısı olarak, C’deki bit field’ların mimariler arasında taşınabilir davranmadığı için pek popüler olmadığını hatırlıyorum
Yeni Zig
@bitCastsemantiği, farklı mimarilerde de aynı sonucu veren taşınabilir soyut semantik sunduğu için tam da ihtiyaç duyulan yön bu benceSon dönemde kendi dilimde bit field ve bit cast tasarladığım için, kodumun nasıl davranması gerektiğini netleştirmek adına Zig tasarım ve uygulama belgelerine daha ayrıntılı bakmayı düşünüyorum
packed structvepacked union; ikisi de yeni@bitCasttanımıyla iyi uyum sağlayacak şekilde tanımlanmışpacked struct, alanların bitlerini “temel tamsayı”ya yerleştiren bir yaklaşım. Örneğin alanlarbool,u6,i9ve temel tamsayıu16ise,u16’nın en düşük anlamlı bitibool, sonraki 6 bitiu6, kalan 9 bitii9olur. Yani Zig’in packed struct’ı, bir dizi shift ve maske işleminin üzerine konmuş sözdizimsel şekerlemeye oldukça yakınpacked unionda bir temel tamsayıya sahiptir, ancak tüm alanların temel tamsayıyla tam olarak aynı sayıda bit kullanması gerekir. Bu yüzden bir alana yazıp başka bir alandan okuma davranışı, yeni semantikteki@bitCastile neredeyse aynıdır. Ancakpacked union/packed structalanları dizi ya da vektör tipi alamazKişisel olarak bu araçların “bit ile ilgili yapıları” ifade etmeye gayet uygun olduğunu düşünüyorum. Birden fazla değeri
packed structile bit düzeyinde paketleyip C bit field’ları gibi kullanabilirsiniz; bit işlemleri üstünde sözdizimsel şekerleme olduğu için, C’de tip güvenli olmayan makro yığınlarıyla çözülen bit flag’ler de temiz biçimde ifade edilebilirÖrneğin RWX erişim flag’leri C’de
ACCESS_READ,ACCESS_WRITE,ACCESS_EXECmakroları veuint8_tAPI’siyle alınabilirken, Zig’deAccess = packed struct(u8)ileread,write,exec,reservedalanları tanımlanıp API’deAccessalınabilirpacked structvepacked unionkullanarak epey garip bit yerleşimlerini de ifade edebilirsiniz. Mach-O nesne formatının sembol tablosu girdisinde, tarihsel nedenlerden kaynaklanıyor gibi görünen tuhaf birn_typealanı var; bunupacked union(u8)içindebits: packed struct(u8)vestab: enum(u8)biçiminde modellemek mümkünBu
n_typedeğerini işlerken elle shift ya da maskeleme gerekmez.n_type.bits.is_stab != 0kontrol edilir; doğruysan_type.stabüzerindenswitchyapılır, değilsen_type.bitsiçindeki diğer alanlara bakılır. Tersine,.{ .stab = .gsym }ya da.{ .bits = .{ .ext = false, .type = .undf, .pext = false, .is_stab = 0 } }gibi değerler de oluşturulabilirAsıl yazının konusundan farklı bir dil özelliğine biraz uzadı, ama yeni dil tasarımında referans alacak bir şey arıyorsanız Zig’in
packed structvepacked unionözelliklerini doğrudan denemenizi öneririm. Basit ama epey iyi araçlar olduklarını düşünüyorum