X86 derlemesindeki kayıtlarda kullanılan push / pop komutlarının işlevi nedir?


101

Sık sık onlar yazılı insanlara rastlamak montajcı hakkında okurken itmek işlemcinin belli bir kayıt ve pop tekrar sonradan önceki durumuna geri yüklemek için.

  • Bir kaydı nasıl itebilirsiniz? Nereye itiliyor? Bu neden gerekli?
  • Bu tek bir işlemci talimatına mı indirgeniyor yoksa daha karmaşık mı?

3
Uyarı: tüm mevcut yanıtlar Intel'in assembly sözdiziminde verilmiştir; örneğin, AT & T sözdiziminde basmalı açılır gibi bir post-fix kullanır b, w, lya da qbellek büyüklüğü manipüle göstermek için kullanılır. Ör: pushl %eaxvepopl %eax
Hawken

5
@hawken AT&T sözdizimini (özellikle gas) yutabilen çoğu derleyicide, işlenen boyutu işlenen boyutundan çıkarılabilirse boyut soneki çıkarılabilir. Bu, %eaxher zaman 32 bit boyutunda olduğu gibi , verdiğiniz örnekler için geçerlidir .
Gunther Piez

Yanıtlar:


153

bir değeri itmek (bir kayıtta saklanması gerekmez), onu yığına yazmak anlamına gelir.

popping , yığının tepesinde bulunan her şeyi bir kasaya geri yüklemek anlamına gelir . Bunlar temel talimatlardır:

push 0xdeadbeef      ; push a value to the stack
pop eax              ; eax is now 0xdeadbeef

; swap contents of registers
push eax
mov eax, ebx
pop ebx

5
Push ve pop için açık işlenen r/msadece kayıt değil, böylece yapabilirsiniz push dword [esi]. Veya pop dword [esp]aynı değeri yüklemek ve sonra aynı adrese geri depolamak için bile . ( github.com/HJLebbink/asm-dude/wiki/POP ). Bunu sadece "kayıt olmak zorunda değil" dediğiniz için söylüyorum.
Peter Cordes

2
Ayrıca popbir hafıza alanına da girebilirsiniz:pop [0xdeadbeef]
SS Anne

Merhabalar, push / pop ve pushq / popq arasındaki fark nedir?
Macos

46

İşte bir kaydı nasıl ittiğiniz. X86 hakkında konuştuğumuzu varsayıyorum.

push ebx
push eax

Yığın üzerine itilir. Değeri ESPyığını 86 sistemlerinde aşağı doğru büyür olarak kayıt itilir değer boyutuna azaltılır.

Değerlerin korunması gerekir. Genel kullanım

push eax           ;   preserve the value of eax
call some_method   ;   some method is called which will put return value in eax
mov  edx, eax      ;    move the return value to edx
pop  eax           ;    restore original eax

A push, x86'da dahili olarak iki şey yapan tek bir talimattır.

  1. Kaydı ESPitilen değerin boyutuna göre azaltın.
  2. Basılan değeri mevcut ESPkayıt adresinde saklayın.

@vavan düzeltilmesi için az önce bir istek gönderdi
jgh fun-run

38

Nereye itiliyor?

esp - 4. Daha kesin:

  • esp 4 çıkarılır
  • değer itilir esp

pop bunu tersine çeviriyor.

System V ABI, Linux'a rsp, program çalışmaya başladığında makul bir yığın konumuna işaret etmesini söyler : Program başladığında (asm, linux) varsayılan kayıt durumu nedir? bu genellikle kullanmanız gereken şeydir.

Bir kaydı nasıl itebilirsiniz?

Minimal GNU GAS örneği:

.data
    /* .long takes 4 bytes each. */
    val1:
        /* Store bytes 0x 01 00 00 00 here. */
        .long 1
    val2:
        /* 0x 02 00 00 00 */
        .long 2
.text
    /* Make esp point to the address of val2.
     * Unusual, but totally possible. */
    mov $val2, %esp

    /* eax = 3 */
    mov $3, %ea 

    push %eax
    /*
    Outcome:
    - esp == val1
    - val1 == 3
    esp was changed to point to val1,
    and then val1 was modified.
    */

    pop %ebx
    /*
    Outcome:
    - esp == &val2
    - ebx == 3
    Inverses push: ebx gets the value of val1 (first)
    and then esp is increased back to point to val2.
    */

Yukarıdaki çalıştırılabilir iddialarla GitHub'da .

Bu neden gerekli?

Bu talimatların mov, addve aracılığıyla kolayca uygulanabileceği doğrudur sub.

Var olmalarının nedeni, bu talimat kombinasyonlarının o kadar sık ​​olması ki Intel bunları bizim için sağlamaya karar verdi.

Bu kombinasyonların bu kadar sık ​​olmasının nedeni, yazmaçların değerlerini geçici olarak belleğe kaydetmeyi ve geri yüklemeyi kolaylaştırmaları ve böylece üzerine yazılmamalarıdır.

Sorunu anlamak için, bazı C kodunu elle derlemeyi deneyin.

En büyük zorluk, her değişkenin nerede saklanacağına karar vermektir.

İdeal olarak, tüm değişkenler, erişilmesi en hızlı bellek olan (şu anda RAM'den yaklaşık 100 kat daha hızlı ) yazmaçlara sığacaktır .

Ama elbette, özellikle iç içe geçmiş fonksiyonların argümanları için yazmaçlardan daha fazla değişkene sahip olabiliriz, bu yüzden tek çözüm belleğe yazmaktır.

Herhangi bir bellek adresine yazabiliriz, ancak işlev çağrılarının ve geri dönüşlerinin yerel değişkenleri ve argümanları, bellek parçalanmasını önleyen güzel bir yığın modeline uyduğundan , bununla başa çıkmanın en iyi yolu budur. Bunu bir yığın ayırıcı yazmanın çılgınlığıyla karşılaştırın.

Ardından, derleyicilerin bizim için yazmaç tahsisini optimize etmelerine izin veriyoruz, çünkü bu NP tamamlandı ve bir derleyici yazmanın en zor kısımlarından biri. Bu soruna kayıt ayırma denir ve grafik renklendirmesi izomorfiktir .

Derleyicinin ayırıcısı bir şeyleri sadece yazmaçlar yerine bellekte depolamaya zorlandığında, bu dökülme olarak bilinir .

Bu tek bir işlemci talimatına mı indirgeniyor yoksa daha karmaşık mı?

Kesin olarak bildiğimiz tek şey Intel'in a pushve bir poptalimatı belgelediği , bu nedenle bunlar bu anlamda bir talimat.

Dahili olarak, biri değiştirmek, diğeri espbellek IO'yu yapmak için birden fazla mikrokoda genişletilebilir ve birden çok döngü alabilir.

Ancak pushdaha spesifik olduğu için, tek bir komutun diğer komutların eşdeğer kombinasyonundan daha hızlı olması da mümkündür .

Bu çoğunlukla belgelenmiştir (der):


4
Sen nasıl tahmin etmeye gerek yok push/ popUOPs içine kod çözme. Performans sayaçları sayesinde deneysel test yapmak mümkündür ve Agner Fog bunu yaptı ve talimat tabloları yayınladı . Pentium-M ve sonraki CPU'larda yığın motoru sayesinde single-uop push/ vardır ( Agner'ın microarch pdf'sine popbakın). Bu, Intel / AMD patent paylaşım anlaşması sayesinde en son AMD işlemcileri içerir.
Peter Cordes

@PeterCordes harika! Yani performans sayaçları, mikro işlemleri saymak için Intel tarafından belgelendi mi?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Ayrıca, reg'lerden dökülen yerel değişkenler, bunlardan herhangi biri gerçekten kullanılıyorsa, genellikle L1 önbelleğinde hala sıcak olacaktır. Ancak bir kayıttan okuma, etkili bir şekilde ücretsizdir, sıfır gecikme. Dolayısıyla, terimleri nasıl tanımlamak istediğinize bağlı olarak, L1 önbelleğinden sonsuz derecede daha hızlıdır. Yığına dökülen salt okunur yereller için ana maliyet sadece ekstra yüklerdir (bazen bellek işlenenleri, bazen ayrı movyüklemelerle). Sabit olmayan değişkenler için, mağaza yönlendirme gidiş dönüşleri çok fazla ekstra gecikmedir (doğrudan iletmeye karşı fazladan ~ 5c ve mağaza talimatları ucuz değildir).
Peter Cordes

Evet, birkaç farklı ardışık düzen aşamasında (sorun / yürütme / kullanımdan kaldırma) toplam uops için sayaçlar vardır, böylece fused-domain veya fused-domain sayabilirsiniz. Örneğin bu yanıta bakın . Bu yanıtı şimdi yeniden yazıyor olsaydım ocperf.py, sayaçlar için kolay sembolik isimler elde etmek için sarmalayıcı komut dosyasını kullanırdım.
Peter Cordes

23

İtme ve patlama kayıtları, buna eşdeğer perde arkasındadır:

push reg   <= same as =>      sub  $8,%rsp        # subtract 8 from rsp
                              mov  reg,(%rsp)     # store, using rsp as the address

pop  reg    <= same as=>      mov  (%rsp),reg     # load, using rsp as the address
                              add  $8,%rsp        # add 8 to the rsp

Bunun x86-64 At & t sözdizimi olduğuna dikkat edin.

Çift olarak kullanıldığında, yığına bir kayıt kaydetmenizi ve daha sonra geri yüklemenizi sağlar. Başka kullanımlar da var.


4
Evet, bu diziler doğru bir şekilde push / pop taklit ediyor. (push / pop dışında bayrakları etkilemez).
Peter Cordes

2
Bayraklar üzerindeki / işaretlerinin etkisini daha iyi taklit etmek için / lea rsp, [rsp±8]yerine kullanmanız daha iyi olur . addsubpushpop
Ruslan

12

Hemen hemen tüm CPU'lar yığın kullanır. Program yığını, donanım destekli yönetmeli LIFO tekniğidir.

Yığın, normalde CPU bellek yığınının tepesine tahsis edilen program (RAM) belleği miktarıdır ve ters yönde büyür (PUSH komutunda yığın işaretçisi azaltılır). Yığına eklemek için standart terim PUSH ve yığından kaldırmak için standart terim POP'dur .

Yığın, yığın işaretçisi olarak da adlandırılan, yığın amaçlı CPU kaydı aracılığıyla yönetilir; bu nedenle, CPU, POP veya PUSH gerçekleştirdiğinde , yığın işaretçisi, yığın belleğine bir kayıt veya sabit yükler / depolar ve yığın işaretçisi, itilen sözcük sayısına göre otomatik olarak azaltılır veya artırılır. veya yığına (nereden) yerleştirilir.

Assembler talimatları aracılığıyla istiflemek için saklayabiliriz:

  1. CPU kayıtları ve ayrıca sabitler.
  2. İşlevler veya prosedürler için iade adresleri
  3. Giriş / çıkış değişkenleri fonksiyonlar / prosedürler
  4. Fonksiyonlar / prosedürler yerel değişkenler.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.