MIPS Fonksiyonlar 1 Konu 1 MIPSde Fonksiyonlar Fonksiyon

  • Slides: 25
Download presentation
MIPS Fonksiyonlar 1

MIPS Fonksiyonlar 1

Konu 1: MIPS’de Fonksiyonlar, § § Fonksiyon çağrımlarını yönetmenin 3 adımını göreceğiz: 1. Programda

Konu 1: MIPS’de Fonksiyonlar, § § Fonksiyon çağrımlarını yönetmenin 3 adımını göreceğiz: 1. Programda akış yönü değiştirilmelidir. 2. Parametreler ve dönüş değerleri al/ver işlemleri yapılır. 3. Yerel değişkenlere yer açılır ve fonksiyondan çıkarken kaldırılır. Ve tabi bunları MIPS’te nasıl yapacağız göreceğiz: — Fonksiyon çağrımı için yeni komutlar. — Programcılar hangi register’ları fonksiyon çağrımlarında kullanır? (ing: Conventions). — Stack (yığıt) kullanımı. 2

C’deki Program Akışı § Bir fonksiyon çağrımıyla programın akış yönü iki kez değişir. 1.

C’deki Program Akışı § Bir fonksiyon çağrımıyla programın akış yönü iki kez değişir. 1. Fonksiyon çağrımında 2. Fonksiyondan dönerken § Bu örnekte main fonksiyonu fact fonksiyonunu iki kez çağırıyor ve fact’dan main içindeki farklı noktalara iki kez dönülüyor. § fact her çağrıldığında, CPU nereye döneceğini dönüş adresi bilgisinde saklıyor § Burada önemli not: main’in kendisi de bir fonksiyon ve program çalıştırıldığında işletim sistemi tarafından çağrılıyor. int main() {. . . t 1 = fact(8); t 2 = fact(3); t 3 = t 1 + t 2; . . . } int fact(int n) { int i, f = 1; for (i = n; i > 1; i--) f = f * i; return f; } 3

MIPS’teki Program Akışı § MIPS ‘jump-and-link’ veya jal denilen bir komutla fonksiyonları çağırır. —

MIPS’teki Program Akışı § MIPS ‘jump-and-link’ veya jal denilen bir komutla fonksiyonları çağırır. — Komut jal fonksiyona sıçramadan önce dönüş adresini $ra üzerine kaydeder. • Bu adres çağrım yerinin altındaki komutun adresidir. — jal MIPS’te PC (program counter) değişkenine erişebilen tek komuttur, böylece PC+4 değerini $ra’ya kaydedebilir. jal Fact § Kontrol tekrar çağırana geçtiğinde, fonksiyon sadece $ra’da ne kayıtlıysa o adres sıçrama işlemi yapar. jr $ra § Şimdi biz faktöriyel fonksiyonunda bu yeni jal ve jr komutlarının kullanımını görelim. 4

C’deki Veri Akışı § Fonksiyonlar 0 veya daha çok parametreler alıp boş veya bir

C’deki Veri Akışı § Fonksiyonlar 0 veya daha çok parametreler alıp boş veya bir dönüş değeri üreterek dönerler. § Programdaki mavi parçalar fact isimli fonksiyonun çalışma anı ve tanım alanı parametreleridir. § Kodun mor parçaları dönüş değerini üretmek için kullanılır. . int main() {. . . t 1 = fact(8); t 2 = fact(3); t 3 = t 1 + t 2; . . . } int fact(int n) { int i, f = 1; for (i = n; i > 1; i--) f = f * i; return f; } 5

MIPS’teki Veri Akışı § MIPS program geliştiricileri oturup, parametrelerde ve dönüş değerlerinde ortak bir

MIPS’teki Veri Akışı § MIPS program geliştiricileri oturup, parametrelerde ve dönüş değerlerinde ortak bir kullanım şekline karar vermişler (ing: calling conventions). — Aslında her dildeki geliştiriciler bunu yaparlar. — MIPS’te 4 adete kadar fonksiyon parametresi fonksiyon jal komutu ile çağrılmadan önce $a 0 -$a 3 register’larına yazılabilir. — MIPS’te bir fonksiyon jr komutu ile dönüş yapmadan önce $v 0 -$v 1 register’larına bilgi yüklenebilir (evet iki adet). § Üzerinde ortak anlaşma yapılan bu kısıtlar/zorlamalar ne donanım ne de assembler dilinden kaynaklanır. Sadece programcılar aynı dilden konuşup, birbirlerinin ne dediğini anlamaya çalışırlar. — Böylece farklı kişilerce yazılan kodlar rahatça paylaşılır ve anlaşılır. — Evet siz de bu kullanıma mecbursunuz… — Biraz uzmanlaşın, sonra farklı parametreler veya dönüş değerleriyle uğraşırız. 6

Veri Tipleri Hakkında Ufak Bir Not § Assembly dili veri tipi kullanmaz (ing: untyped

Veri Tipleri Hakkında Ufak Bir Not § Assembly dili veri tipi kullanmaz (ing: untyped structure). — Tamsayılar, karakterler, pointer’lar veya diğer tip veriler arasında bir ayrım yoktur. § Programlarınızdaki veri tipi kontrolü (ing: type check) tamamen size bırakılır. — Bugünkü konumuza uysun diye söylüyorum; Fonksiyona verilen parametre sayısı ve tipleri ile dönüş değerlerinin amacınıza ve tanımlamalarınıza uygun olduğundan emin olun yeter. § Örneğin, birisi bir tamsayının kendisini değil de adresini fact fonksiyonuna geçiriyorsa ne yapmak istiyor olabilir? 7

C MIPS İlk Denememiz int main() {. . . t 1 = fact(8); t

C MIPS İlk Denememiz int main() {. . . t 1 = fact(8); t 2 = fact(3); t 3 = t 1 + t 2; . . . } int fact(int n) { int i, f = 1; for (i = n; i > 1; i--) f = f * i; return f; } main: // int main(). . . addi $a 0, $0, 8 jal fact add $t 1, $0, $v 0 addi $a 0, $0, 3 jal fact add $t 1, $v 0. . . fact: addi $v 0, $0, 1 loop: blti $a 0, 2, exit mul $v 0, $a 0; subi $a 0, 1 j loop exit: jr $ra 8

Büyük Problemi Fark Ettiniz mi? § Burada ciddi problemler olabilir ! — main kod

Büyük Problemi Fark Ettiniz mi? § Burada ciddi problemler olabilir ! — main kod kısmında $t 1 fact(8) değerini saklamak için kullanılmış. — Hadi biz dikkat ettik, $t 1’i fact fonksiyonunda kullanmadık da, ya kullansaydık! — İkinci çağrım olan fact(3) $t 1’i bozar ve fact(8) değeri sonsuza dek yok olurdu. § Demek ki, herkes kendi verisine sahip olacak! — main içinde kendi register’larımızı korumaya almalıyız. § Başkalarının yazdığı bir fonksiyonu çağırırken de kendi verimizi silinebileceğini bilelim. 9

İç İçe Fonksiyon Çağrımları da Problemdir § Benzer bir durum bir fonksiyon başka bir

İç İçe Fonksiyon Çağrımları da Problemdir § Benzer bir durum bir fonksiyon başka bir fonksiyonu çağırdığında da olur. § Mesela A – B’yi, B - C’yi çağırsın. — Öncelikle C’nin çağrımıyla parametreler yani $a 0 -$a 3 bozulur çünkü B’nin parametreleri üzerine yazılır. — Aynı şekilde, jal C dönüş adresi olarak kaydedilen $ra‘yı siler ve daha önce jal B. le kaydedilenin üzerine yazar. A: . . . # B’nin prm’leri $a 0 -$a 3 jal B # $ra = A 2: . . . B: . . . # C’nin prm’leri $a 0 -$a 3, # B’ninkiler gitti! jal C # $ra = B 2: . . . jr $ra # Burada # neler oldu? ? ? C: . . . jr $ra 10

Register’ları Ayırmak § Sonuçta CPU tüm fonksiyonlarca kullanılması için limitli sayıda register’a sahip ve

Register’ları Ayırmak § Sonuçta CPU tüm fonksiyonlarca kullanılması için limitli sayıda register’a sahip ve birkaç fonksiyon da kullansak hep aynı register’lar gerekecek. § O halde biz bazı önemli register’ları fonksiyon çağrımlarında bozulur diye daha fonksiyonu çağırmadan korumaya almalıyız, fonksiyondan dönünce de eski değerleri geri yüklemeliyiz. § Ama iki önemli sorumuz var. — Register’ları korumaktan kim sorumlu olacak—çağıran mı, çağrılan mı? — Register içerikleri tam olarak nereye saklanacak? 11

Register’ları Kim Koruyacak? § Fonksiyon çağrımlarındaki bozulmalardan önemli register’ları korumaktan kim sorumlu olacak? —

Register’ları Kim Koruyacak? § Fonksiyon çağrımlarındaki bozulmalardan önemli register’ları korumaktan kim sorumlu olacak? — Çağıran hangi register’lar önemli bildiği için kendisi onları korumalı, — Çağrılan tam olarak hangi register’lar içeride kullanılıyor ve üzerine yazıp, bozulma ihtimali varsa onları korumalıdır. § Ancak, tipik “kara kutu” programlama tekniğinde, çağıran ve çağrılan kesinlikle diğerlerinin uygulamalarından haberdar değillerdir. — Farklı fonksiyonlar belki farklı kişiler veya şirketlerce yazılmış olabiliyor. — Bir fonksiyon daha önce kullanılan ismiyle kullanılsa da, farklı uygulaması yapıldığı için, içeriğinde bizim zannettiğimiz gibi davranmadığından sıkıntılı durumlar doğurabilir. § İki fonksiyon birbirlerinin uygulamalarından bu kadar bihaber iken, register’ları paylaşmak için nasıl davranmalı? — Yağ var, un var, şeker var, öyleyse ne yapayım? 12

Çağıran Bazı Register’ları Korumalıdır… § Bir öneri: Çağıran bazı önemli register’ları fonksiyon çağrımlarında daha

Çağıran Bazı Register’ları Korumalıdır… § Bir öneri: Çağıran bazı önemli register’ları fonksiyon çağrımlarında daha fonksiyonu çağırmadan korumaya alır, fonksiyondan dönünce de eski değerleri geri yükler. § Fakat çağıran fonksiyonun kesinlikle bu register’lar üzerine yazacağından emin değildir, yani gerekli olandan fazla register’ı korumaya almış olabilir. § Sağdaki örnekte, frodo fonksiyonu $a 0, $a 1, $s 0 ve $s 1 register’larını gollum’dan korumak ister, fakat gollum belki de bu register’ların hiçbirini kullanmayabilir. frodo: li li $a 0, $a 1, $s 0, $s 1, 3 1 4 1 # Şu register’ları koru # $a 0, $a 1, $s 0, $s 1 jal gollum # Şu register’ları # geri yükle # $a 0, $a 1, $s 0, $s 1 add $v 0, $a 1 add $v 1, $s 0, $s 1 jr $ra 13

…veya Çağrılan Bazı Register’ları Korumalıdır… § Başka bir öneri de çağrılan kendi kullanacaklarını girişte

…veya Çağrılan Bazı Register’ları Korumalıdır… § Başka bir öneri de çağrılan kendi kullanacaklarını girişte korumaya alır, çıkarken de geri yükler. § Örneğin, gollum fonksiyonu $a 0, $a 2, $s 0 ve $s 2 register’larını kullanacağı için önce orijinal değerlerini korur ve dönüş yapmadan da onları geri yükler. § Burada da çağrılan hangi register’lar çağıran fonksiyon için önemlidir bilemez, yani yine gerekli olandan fazla register korunuyor olabilir. gollum: # Şu register’ları koru # $a 0 $a 2 $s 0 $s 2 li li. . . $a 0, $a 2, $s 0, $s 2, 2 7 1 8 # Şu register’ları # geri yükle # $a 0 $a 2 $s 0 $s 2 jr $ra 14

…Ya da Bu Konuda Beraber Çalışabilirler § MIPS yazarları her konuda anlaştıkları gibi, register

…Ya da Bu Konuda Beraber Çalışabilirler § MIPS yazarları her konuda anlaştıkları gibi, register ayırma konusunda da ortak kararlar almışlardır. § Çağıran aşağıda verilen ve ‘çağıranın-koruyacağı-register’ları‘ olarak üzerinde anlaşmaya varılan register’ları korumaya alır. $t 0 -$t 9 $a 0 -$a 3 $v 0 -$v 1 Başka bir deyişle, çağrılan bu register’larla rahatça oynayabilir, çünkü kendisini çağıranın gerekli görmesi durumunda bunları koruyacağını varsayar. § Çağrılan da aşağıda verilen ve ‘çağrılanın-koruyacağı-register’ları‘ olarak üzerinde anlaşmaya varılan register’ları korumaya alır. (Hatırlatma $ra ‘jal’ tarafından dolduruluyor. ) $s 0 -$s 7 $ra Yani çağıranın varsayımı da bu register’lara çağırdığım fonksiyonun bir müdahalesi olmayacaktır şeklindedir. — $ra önemli; çağrılan bunu korur da kendisi de bazen çağıran oluyor. — İç içe fonksiyon çağrımlarında kim çağıran, kim çağrılan karıştırmayın! 15

Register Ayırmaya Bir Örnek § Üzerinde anlaşmaya varılan bu konuyla çağıran ve çağrılanın önemli

Register Ayırmaya Bir Örnek § Üzerinde anlaşmaya varılan bu konuyla çağıran ve çağrılanın önemli register’ları birlikte korumaları garantileniyor — frodo : $a 0 ve $a 1, gollum : $s 0 ve $s 2’dan sorumlular. frodo: li li $a 0, $a 1, $s 0, $s 1, 3 1 4 1 # Şu register’ları koru # $a 0, $a 1, $ra jal gollum: # Şu register’ları koru # $s 0 ve $s 2 li li. . . $a 0, $a 2, $s 0, $s 2, 2 7 1 8 # Şu register’ları # geri yükle # $a 0, $a 1, $ra # Şu register’ları # geri yükle # $s 0 ve $s 2 add jr jr $v 0, $a 1 $v 1, $s 0, $s 1 $ra 16

Faktöriyel Fonksiyonunu Düzenlersek § Burada main (çağıran) iki register’ı korumalıdır. — $t 1 ikinci

Faktöriyel Fonksiyonunu Düzenlersek § Burada main (çağıran) iki register’ı korumalıdır. — $t 1 ikinci fact çağrımından önce korunmalıdır. — $ra ‘jal’ komutlarınca bozuluyor, korunmalıdır. § Fakat fact (çağrılan) hiçbir şey yapmak zorunda değildir. O sadece register $a 0 ve $v 0 üzerinde oynuyor, bunları da çağıran korursa korur. main: // int main(). . . # $ra’yı koru addi $a 0, $0, 8 jal fact add $t 1, $0, $v 0 # $t 1’i koru addi $a 0, $0, 3 jal fact # $t 1’i geri yükle add $t 1, $v 0 # $ra’yi geri yükle. . . fact: # Aynı kod geçerli 17

STACK BELLEK 18

STACK BELLEK 18

Register’lar Nerede Korunacak? § Artık hangi register’ı kim koruyacağını biliyoruz da, henüz bunları nerede

Register’lar Nerede Korunacak? § Artık hangi register’ı kim koruyacağını biliyoruz da, henüz bunları nerede koruyacaklarını konuşmadık. § Keşke her fonksiyonun kendi özel bellek alanı olsaydı: Stack bellek var. — Böyle bir alan diğer fonksiyon çağrımlarının bizim korunan register’lar üzerine yazmalarından bizi koruyacaktır • Bunları ana belleğe yazıp dursak register kullanımı gibi faydalı olmaz ki. — Böyle özel bir alanı lokal değişkenleri tutmak gibi başka amaçlar için de kullanabiliriz. 19

Fonksiyon Çağrımları ve Stack Bellekler § Fonksiyon çağrımları ve dönüşler yığıt (ing: stack) tipi

Fonksiyon Çağrımları ve Stack Bellekler § Fonksiyon çağrımları ve dönüşler yığıt (ing: stack) tipi kullanım gerektirir: Son çağrılan fonksiyon ilk döner (LIFO). 1. Birileri A’yı çağırır 2. A B’yı çağırır 3. B C’yi çağırır 4. C B’ye döner 5. B A’ya döner 6. A geri döner 1 2 A: . . . jal B A 2: . . . jr $ra 6 5 B: 3 . . . jal C B 2: . . . jr $ra § Bu örnekte, C B’ye B A’ya dönmeden önce geri dönmelidir. C: 4 . . . jr $ra 20

Fonksiyon Çağrımları ve Stack Bellekler § Bir stack belleğin fonksiyon çağrımlarında kullanımı gayet doğaldır.

Fonksiyon Çağrımları ve Stack Bellekler § Bir stack belleğin fonksiyon çağrımlarında kullanımı gayet doğaldır. Stack alanı, ‘stack frame’ diye de anılır, tüm fonksiyon çağrımlarında kullanılmak üzere ayrılmıştır. — Bir fonksiyon çağrıldığında, lokal değişkenler için kullanılmak üzere stack üzerinde yeni bir çerçeve alan (ing: frame) yaratır. — Fonksiyon dönmeden önce, bu alanı serbest bırakır ve stack orijinal haline döner. § Stack alanları birkaç amaç için kullanılır. — Çağıran ve çağrılan fonksiyonlar register’larını stack alanına koyabilirler. — Stack alanında lokal değişkenler veya ek parametreler ve dönüş değerleri tutulabilir. 21

MIPS Stack Yapısı § MIPS makinelerde, ana belleğin bir parçası 0 x 7 FFFFFFF

MIPS Stack Yapısı § MIPS makinelerde, ana belleğin bir parçası 0 x 7 FFFFFFF stack için ayrılmaktadır. — Stack yapısında sadece en üst elemanın adresi “stack pointer” register’ı olarak $sp içinde saklanmaktadır. — Stack pointer’ı bellek adresi olarak aşağı doğru ilerlemektedir. stack § MIPS size Intel gibi “push” ve “pop” komutları vermez. Programcı kodlar ile bunu halletmelidir. 0 x 0000 22

Stack Belleğe Ekleme (Pushing Elements) § Verileri stack belleğe ‘push’ etmek için : —

Stack Belleğe Ekleme (Pushing Elements) § Verileri stack belleğe ‘push’ etmek için : — Stack pointer $sp bu veriler için ne kadar alan gerekliyse o kadar aşağı çekilir. — Elemanlar stack içine yazılır. § Örneğin, register $t 1 ve $t 2 stack içine atılacaksa: word 1 $sp sub $sp, 8 sw $t 1, 4($sp) sw $t 2, 0($sp) Önceki word 1 § Veya başka bir yoldan: sw $t 1, -4($sp) sw $t 2, -8($sp) sub $sp, 8 § Stack için önceki-sonraki durumlar sağda verilmektedir. word 2 $t 1 $sp $t 2 Sonraki 23

Stack’te Elemanlara Ulaşım ve Silme (Poping Elements) § Stack bir bellek olduğundan içindeki herhangi

Stack’te Elemanlara Ulaşım ve Silme (Poping Elements) § Stack bir bellek olduğundan içindeki herhangi bir elemana ulaşabilirsiniz (sadece tepedekine değil). Bilmeniz gereken $sp’ye göre nerede olduğudur. § Örneğin, $t 1 değerine ulaşım: lw word 1 word 2 $t 1 $sp $t 2 $s 0, 4($sp) § ‘pop‘ işlemi (silme) stack pointer’ı yukarı almaktır. § $t 2 değerini silmek için aşağıdaki gibi bir komut vermek yeterli olacaktır: word 1 addi $sp, 4 word 2 § Not: Silinenler hala bellekte yerlerinde kalırlar. Ama stack pointer’ın altında kalan değerler geçersiz sayılır. $sp $t 1 $t 2 24

Özet § Bu derste MIPS’te fonksiyon çağrımlarına odaklandık. — Fonksiyonları jal ile çağırıp parametreleri

Özet § Bu derste MIPS’te fonksiyon çağrımlarına odaklandık. — Fonksiyonları jal ile çağırıp parametreleri de $a 0 -$a 3 register’ları ile aktarırız. — Fonksiyonlar elde ettikleri sonuçları $v 0 -$v 1 register’larına yazar ve jr $ra ile dönüş yaparlar. § Kaynakların yönetimi Fonksiyon çağrımlarının önemli bir parçasıdır. — Önemli veriler üzerine yazılmasın diye, üzerinde varılan anlaşmalara göre register’lar ya çağıranın-koruyacağı ya da çağrılanın-koruyacağı sınıflarına girip, korunurlar. — Her fonksiyon çağrımı register’ları, yerel değişkenleri, aktarılan ekstra parametreleri ve dönüş değerlerini saklamak için stack belleği kullanır. § Assembly programcıları bir sürü takip edilmesi gereken anlaşmalara uyarlar. Yoksa hiçbir program register’lar veya stack belleğin başka fonksiyonlarca üzerine yazılmasından korunamaz. 25