| |
| Posted by Ali Çehreli in reply to Salih Dincer | PermalinkReply |
|
Ali Çehreli
Posted in reply to Salih Dincer
| On 4/15/22 07:19, Salih Dincer wrote:
> Aşağıdaki kod CTFE (Compile Time Function Execution) kapsamında
> derlenir.
Bir değer (genel olarak "ifade (expression)") derleme zamanında gerektiğinde derleme zamanında işletilir. (İşletilmesi için gerekenler derleme zamanında mevcut değilse işletilemez ve derleme hatasıdır.)
> Ama merak ettiğim bir şey var. Bu kod içindeki işlev an
> itibariyle %100 static mi?
İşlev static değil, çağrıldığında döndürülen değer static. (Bir işlevin kodları işletilecek biçimde derlenmemiş olabilir ama o konu derleyicinin eniyileştirme yeteneği ile ilgilidir.)
> Yani ben main() dışında çağırmak
CTFE'nin main içinde veya dışında çağrılmakla tarif edilmemeli. (Sen bu örnek için söylüyorsun.)
> ile derleme
> zamanında işletildiği garantisini verebir miyim?
Garanti, o değerin derleme zamanında gerekmiş olmasıdır.
> ```d
> auto genFactorials(int n)
> {
> auto result = new int[n];
> result[0] = 1;
> foreach (i; 1 .. n)
> {
> result[i] = result[i - 1] * i;
> }
> return result;
> }
Bir derleyici, karşısına çıkan işlevleri sıra ile derliyor olabilir. Evet, genFactorials() çalışma zamanında (da) işletilecek biçimde koda eklenecektir.
> enum factorials = genFactorials(13);
D'de 'enum' sabit değer (manifest constant) anlamına gelir. O değerin derleme zamanında bilinmesi gerektiğinden genFactorials() derleme zamanında işletilecektir. (Eğer derleyici işlevin derlenmesini bu noktaya geciktirdiyse bu artık noktada kesin derleyecektir.)
> enum fact13th = 479001600;
Orada zaten salt bir 'int' değer kullanılıyor.
Derleme zamanında işletilmeyi gerektiren durumlar şunlardır (bazılarını unutuyor olabilir):
- enum değerler
- static const değerler
- Şablon parametreleri
- static if
- static assert
- static foreach
- version değeri
- debug değeri
Yani, derleme zamanında gereken herşey.
> void main()
> {
> static assert(factorials.length == 13);
'factorials.length' derleme zamanında hesaplanır ve 13 sabit değerinin aynısıdır.
> static assert(factorials[$-1] == fact13th);
factorials[$-1] derleme zamanında şu ifade olarak işletilir: *(factorials.ptr + factorials.length - 1). Ama ürettiği değer 479001600'nın aynısıdır.
> }
> ```
> Bir soru daha, diyelim ki main() içinde runtime çağırdım.
Örneğin:
void main(string[] args) {
genFactorials(args.length);
}
> Bu durumda
> kodun gerçekten programa dahil edildiği sonucu çıkarabilir miyiz?
> Örneğin hepsini bir stack içine attım ve sonra kareköklerini aldım.
D gibi dillerde "separate compilation" diye bir kavram var: Programın parçaları ayrı ayrı derlenmiş olabilir. Buna göre, örneğin programın ilk derlenmiş parçası daha sonradan derlenmiş bir parçasını çağırıyor olabilir. (İkinci kodun tek ihtiyacı, birinci işlevin ilintisidir (D binding).) Dolayısıyla, hiçbir derleme işlevin programa dahil edilmemesi kararını veremez.
Böyle bir kararı ancak programı en sonunda oluşturacak olan bağlayıcı (linker) verebilir. O da galiba bağlayıcıya göre değişebilir veya kullanılan bağlayıcı seçenekleri önemlidir. Emin değilim...
Ben nadir de olsa bu gibi konularda iki araç kullanıyorum:
- Dissassembler: İnternette bir kaç site olabildiği gibi objdump, obj2asm, vs. gibi bir program da olabilir.
- nm: Program parçaları (.o, .lib, vs. dosyaları) veya programlar içinde bulununan semboller ile ilgili bilgi verir.
1) Programı derliyorum:
$ dmd deneme.d
2) Derleyicinin oluşturduğu kodlara bakıyorum:
$ objdump -d deneme > deneme.asm
_Dmain işlevi bomboş:
00000000000457bc <_Dmain>:
457bc: 31 c0 xor %eax,%eax
eax yazmacını
kendisi ile xor
yaparak 0 üretiyor
(başarılı sonuçlanma
anlamında)
457be: c3 ret dış dünyaya dönüyor
Program içinde ne 'factorials' var ne de 'fact13th'.
Ama yine de programın içinde genFactorials() diye bir işlev var:
00000000000456ec <_D6deneme13genFactorialsFNaNbNfiZAi>:
456ec: 55 push %rbp
456ed: 48 8b ec mov %rsp,%rbp
456f0: 48 83 ec 30 sub $0x30,%rsp
rsp, son sohbette
konuştuğumuz
"stack pointer"
// ...
457b8: c9 leave
457b9: c3 ret
(extern(C) diye derlenseydi işlevin ismi genFactorials() olurdu. Bu konu işlev yüklemeyi (overloading) desteklemek için gereken "name mangling" ile ilgilidir.)
3) Şimdi de nm ile bakalım:
$ nm deneme | grep genFactorials
00000000000456dc W _D6deneme13genFactorialsFNaNbNfiZAi
Evet, programda o sembol varmış ve 'W' imiş. nm'in belgesine 'man nm' diye baktığımızda W'nun anlamının "weak symbol" olduğunu görürüz. Yani, burada tanımlanmış ama başka bir program parçası veya kütüphane tarafından aynı sembol (isim) normal olarak tanımlanmışsa, bağlayıcı normal tanımı kullanacak demektir.
Ali
|