Derleyici Tasarımı - Kod Üretimi
Kod üretimi, derlemenin son aşaması olarak düşünülebilir. Posta kodu oluşturma yoluyla, optimizasyon süreci koda uygulanabilir, ancak bu, kod oluşturma aşamasının bir parçası olarak görülebilir. Derleyici tarafından üretilen kod, bazı alt düzey programlama dillerinin bir nesne kodudur, örneğin, assembly dili. Daha yüksek seviyeli bir dilde yazılan kaynak kodun, aşağıdaki minimum özelliklere sahip olması gereken daha düşük seviyeli bir nesne koduyla sonuçlanan daha düşük seviyeli bir dile dönüştürüldüğünü gördük:
- Kaynak kodun tam anlamını taşımalıdır.
- CPU kullanımı ve bellek yönetimi açısından verimli olmalıdır.
Şimdi ara kodun hedef nesne koduna (bu durumda montaj kodu) nasıl dönüştürüldüğünü göreceğiz.
Yönlendirilmiş döngüsüz grafiği
Directed Acyclic Graph (DAG), temel blokların yapısını gösteren, temel bloklar arasında akan değerlerin akışını görmeye yardımcı olan ve optimizasyon da sunan bir araçtır. DAG, temel bloklar üzerinde kolay dönüşüm sağlar. DAG burada anlaşılabilir:
Yaprak düğümler tanımlayıcıları, isimleri veya sabitleri temsil eder.
İç düğümler operatörleri temsil eder.
İç düğümler ayrıca ifadelerin sonuçlarını veya değerlerin depolanacağı veya atanacağı tanımlayıcıları / isimleri temsil eder.
Example:
t0 = a + b
t1 = t0 + c
d = t0 + t1
[t 0 = a + b] |
[t 1 = t 0 + c] |
[d = t 0 + t 1 ] |
Gözetleme Deliği Optimizasyonu
Bu optimizasyon tekniği, onu optimize edilmiş bir koda dönüştürmek için kaynak kodu üzerinde yerel olarak çalışır. Yerel olarak, eldeki kod bloğunun küçük bir bölümünü kastediyoruz. Bu yöntemler, ara kodların yanı sıra hedef kodlara da uygulanabilir. Bir grup ifade analiz edilir ve aşağıdaki olası optimizasyon için kontrol edilir:
Gereksiz talimat eliminasyonu
Kaynak kodu düzeyinde, kullanıcı tarafından aşağıdakiler yapılabilir:
|
|
|
|
Derleme düzeyinde, derleyici doğası gereği gereksiz olan talimatları arar. Talimatların birden fazla yüklenmesi ve saklanması, bazıları kaldırılsa bile aynı anlamı taşıyabilir. Örneğin:
- MOV x, R0
- MOV R0, R1
İlk talimatı silebilir ve cümleyi şu şekilde yeniden yazabiliriz:
MOV x, R1
Ulaşılamaz kod
Ulaşılamayan kod, program kodunun programlama yapıları nedeniyle asla erişilemeyen bir parçasıdır. Programcılar yanlışlıkla ulaşılamayacak bir kod parçası yazmış olabilirler.
Example:
void add_ten(int x)
{
return x + 10;
printf(“value of x is %d”, x);
}
Bu kod segmentinde, printf deyimi, program denetimi çalıştırılmadan önce geri döndüğü için asla çalıştırılmayacaktır, bu nedenle printf kaldırılabilir.
Kontrol optimizasyonu akışı
Bir kodda, program kontrolünün önemli bir görev gerçekleştirmeden ileri geri atladığı örnekler vardır. Bu sıçramalar kaldırılabilir. Aşağıdaki kod parçasını düşünün:
...
MOV R1, R2
GOTO L1
...
L1 : GOTO L2
L2 : INC R1
Bu kodda, L1 etiketi kontrolü L2'ye geçirirken çıkarılabilir. Dolayısıyla, L1'e ve sonra L2'ye atlamak yerine, kontrol aşağıda gösterildiği gibi doğrudan L2'ye ulaşabilir:
...
MOV R1, R2
GOTO L2
...
L2 : INC R1
Cebirsel ifade basitleştirme
Cebirsel ifadelerin basitleştirilebileceği durumlar vardır. Örneğin, ifadea = a + 0 ile değiştirilebilir a kendisi ve a = a + 1 ifadesi basitçe INC a ile değiştirilebilir.
Güç azaltma
Daha fazla zaman ve alan tüketen operasyonlar var. Daha az zaman ve alan tüketen ancak aynı sonucu üreten diğer işlemlerle değiştirilerek 'güçleri' azaltılabilir.
Örneğin, x * 2 ile değiştirilebilir x << 1, sadece bir sola kaydırma içerir. A * a ve a 2'nin çıktısı aynı olsa da, 2'nin uygulanması çok daha etkilidir.
Makine talimatlarına erişim
Hedef makine, belirli işlemleri çok daha verimli bir şekilde gerçekleştirme yeteneğine sahip olabilen daha karmaşık talimatları uygulayabilir. Hedef kod bu talimatlara doğrudan uyabilirse, bu yalnızca kodun kalitesini iyileştirmekle kalmaz, aynı zamanda daha verimli sonuçlar da verir.
Kod üreteci
Bir kod üretecinin, hedef makinenin çalışma zamanı ortamını ve komut setini anlaması beklenir. Kod oluşturucu, kodu oluşturmak için aşağıdaki hususları dikkate almalıdır:
Target language: Kod oluşturucu, kodun dönüştürüleceği hedef dilin doğasının farkında olmalıdır. Bu dil, derleyicinin kodu daha uygun bir şekilde oluşturmasına yardımcı olmak için bazı makineye özgü talimatları kolaylaştırabilir. Hedef makine, CISC veya RISC işlemci mimarisine sahip olabilir.
IR Type: Ara temsilin çeşitli biçimleri vardır. Abstract Syntax Tree (AST) yapısında, Reverse Polish Notation'da veya 3 adresli kodda olabilir.
Selection of instruction: Kod üreteci, Ara Gösterimi girdi olarak alır ve hedef makinenin komut setine dönüştürür (eşler). Bir temsilin onu dönüştürmek için birçok yolu (talimat) olabilir, bu nedenle uygun talimatları akıllıca seçmek kod üreticinin sorumluluğuna girer.
Register allocation: Bir programın yürütme sırasında korunması gereken bir dizi değeri vardır. Hedef makinenin mimarisi, tüm değerlerin CPU belleğinde veya kayıtlarında tutulmasına izin vermeyebilir. Kod oluşturucu, kayıtlarda hangi değerlerin tutulacağına karar verir. Ayrıca bu değerleri korumak için kullanılacak kayıtlara karar verir.
Ordering of instructions: Son olarak, kod üreteci komutun yürütüleceği sıraya karar verir. Bunları yürütmek için talimatlar için programlar oluşturur.
Tanımlayıcılar
Kod üreteci, kodu oluştururken hem kayıtları (kullanılabilirlik için) hem de adresleri (değerlerin konumu) izlemelidir. Her ikisi için de aşağıdaki iki tanımlayıcı kullanılır:
Register descriptor: Kayıt tanımlayıcı, kod oluşturucuyu kayıtların kullanılabilirliği hakkında bilgilendirmek için kullanılır. Kayıt tanımlayıcı, her kayıtta depolanan değerlerin kaydını tutar. Kod üretimi sırasında yeni bir kayıt gerektiğinde, kayıt kullanılabilirliği için bu tanımlayıcıya danışılır.
Address descriptor: Programda kullanılan adların (tanımlayıcıların) değerleri, yürütme sırasında farklı yerlerde saklanabilir. Adres tanımlayıcıları, tanımlayıcıların değerlerinin saklandığı hafıza konumlarını takip etmek için kullanılır. Bu konumlar CPU kayıtlarını, yığınları, yığınları, belleği veya belirtilen konumların bir kombinasyonunu içerebilir.
Kod oluşturucu, her iki tanımlayıcıyı gerçek zamanlı olarak güncel tutar. Bir yük ifadesi için, LD R1, x, kod oluşturucu:
- x değerine sahip olan Kayıt Tanımlayıcı R1'i günceller ve
- Adres Tanımlayıcısını (x), bir x örneğinin R1'de olduğunu gösterecek şekilde günceller.
Kod Üretimi
Temel bloklar bir dizi üç adresli komuttan oluşur. Kod üreteci bu komut dizisini girdi olarak alır.
Note: Bir adın değeri birden fazla yerde (kayıt, önbellek veya hafıza) bulunursa, kaydın değeri önbellek ve ana hafıza yerine tercih edilecektir. Aynı şekilde önbelleğin değeri ana belleğe tercih edilecektir. Ana bellek neredeyse hiç tercih edilmez.
getReg: Kod oluşturucu , mevcut kayıtların durumunu ve ad değerlerinin konumunu belirlemek için getReg işlevini kullanır . getReg şu şekilde çalışır:
Y değişkeni zaten R kaydında ise, bu kaydı kullanır.
Aksi takdirde, bazı R kaydı mevcutsa, bu kaydı kullanır.
Aksi takdirde, yukarıdaki seçeneklerin ikisi de mümkün değilse, minimum sayıda yükleme ve saklama talimatı gerektiren bir kayıt seçer.
Bir x = y OP z talimatı için, kod oluşturucu aşağıdaki eylemleri gerçekleştirebilir. L'nin y OP z'nin çıktısının kaydedileceği konum (tercihen kayıt) olduğunu varsayalım:
L'nin konumuna karar vermek için getReg işlevini çağırın.
Mevcut konumunu (kayıt veya hafıza) belirleyin. y Adres Tanımlayıcısına danışarak y. Eğery şu anda kayıtta değil L, ardından değerini kopyalamak için aşağıdaki talimatı oluşturun y -e L:
MOV y ', L
nerede y’ kopyalanan değeri temsil eder y.
Mevcut konumunu belirle z 2. adımda kullanılan aynı yöntemi kullanarak y ve aşağıdaki talimatı oluşturun:
OP z ', L
nerede z’ kopyalanan değeri temsil eder z.
Şimdi L, atanması amaçlanan y OP z değerini içerir. x. Bu nedenle, L bir kayıt ise, tanımlayıcısını, değerini içerdiğini gösterecek şekilde güncelleyin.x. Tanımlayıcısını güncelleyinx yerinde depolandığını belirtmek için L.
Y ve z'nin başka bir kullanımı yoksa, sisteme geri verilebilirler.
Döngüler ve koşullu ifadeler gibi diğer kod yapıları, genel assembly biçiminde assembly diline dönüştürülür.