Sadece XOR iki kaydı 0 üretmek için yapabildiğinizde MIPS neden R0'ı “sıfır” olarak kullanıyor?


10

Ben bir trivia soruya bir cevap arıyorum düşünüyorum. MIPS mimarisinin neden sadece XOR'ing kendisine karşı XOR'ing ile aynı şeyi elde edebilirsiniz bir kayıtta açık "sıfır" değeri kullandığını anlamaya çalışıyorum. Operasyonun sizin için zaten yapıldığını söyleyebiliriz; ancak, çok fazla "sıfır" değer kullanacağınız bir durumu hayal bile edemiyorum. Hennessey'in orijinal makalelerini okudum ve gerçek bir gerekçe olmadan sadece bir sıfır olarak atar.

Sabit kodlu ikili atamanın sıfır olması için mantıklı bir neden var mı?

Güncelleme: PIC32MZ MIPS çekirdeği için xc32-gcc bir yürütülebilir dosya 8k, "sıfır" tek bir örneğim var.

add     t3,t1,zero

asıl cevap: MIPS ve koşul kodları hakkında bilgi sahibi olan kişiye lütuf verdim. Cevap aslında koşullar için MIPS mimarisinde yatmaktadır. Başlangıçta buna zaman ayırmak istemememe rağmen, opensparc , MIPS-V ve OpenPOWER (bu belge dahili idi) için mimariyi gözden geçirdim ve işte özet bulgular. Boru hattının mimarisi nedeniyle dalların karşılaştırılması için gerekli R0 kaydı.

  • sıfır ve dal ile tam sayı karşılaştırması (bgez, bgtz, blez, bltz)
  • tamsayı iki kayıt ve şube karşılaştırmak (beq, bne)
  • tamsayı iki yazmaç ve tuzağı karşılaştır (teq, tge, tlt, tne)
  • tamsayı karşılaştırma kayıt ve hemen ve tuzak (teqi, tgei, tlti, tnei)

Sadece donanımın uygulamada nasıl göründüğüne gelir. MIPS-V kılavuzundan sayfa 68'de referans verilmemiş bir teklif var:

Koşullu dallar, koşul kodlarını (x86, ARM, SPARC, PowerPC) kullanmak yerine iki kayıt arasındaki (PA-RISC ve Xtensa ISA'da olduğu gibi) aritmetik karşılaştırma işlemlerini içerecek veya yalnızca bir kaydı sıfıra ( Alfa, MIPS) veya yalnızca eşitlik için iki kayıt (MIPS). Bu tasarım, kombine bir karşılaştırma ve şube talimatının normal bir boru hattına girdiği, ek koşul kodu durumunu veya geçici bir kayıt kullanılmasını önlediği ve statik kod boyutunu ve dinamik komut alma izini azalttığı gözlemiyle motive edilmiştir. Bir başka nokta, sıfır ile karşılaştırmanın önemsiz olmayan devre gecikmesi gerektirmesidir (özellikle gelişmiş işlemlerde statik mantığa geçtikten sonra) ve bu nedenle neredeyse aritmetik büyüklük karşılaştırması kadar pahalıdır. Kaynaşmış bir karşılaştırma ve dallama talimatının bir başka avantajı da dalların ön uç talimat akışında daha önce gözlemlenmesidir ve bu nedenle daha önce tahmin edilebilir. Aynı koşul kodlarına dayanarak birden fazla dalın alınabilmesi durumunda, koşul kodları olan bir tasarıma belki de bir avantaj vardır, ancak bu durumun nispeten nadir olduğuna inanıyoruz.

MIPS-V belgesi alıntılanan bölümün yazarına çarpmaz. Herkese zamanları ve ilgileri için teşekkür ediyorum.


6
Kaynak değer olarak bazı işlemlerde genellikle 0 değerli bir kayıt kullanmak istersiniz . Bu işlemlerden önce bir kaydı sıfırlamak biraz genel gider olacaktır, bu nedenle gerektiğinde kendiniz oluşturmak yerine sağlanan bir sıfırı kullanabiliyorsanız performans avantajları sağlar. Örnekler arasında taşıma bayrağı eklenmesi sayılabilir.
JimmyB

3
AVR mimarisinde, gcc başlangıçta r1'i sıfıra başlatmaya özen gösterir ve hemen 0'ın kullanılamadığı yerlerde r1'i kaynak olarak kullanarak bir daha asla bu değere dokunmaz. Burada, özel sıfır kaydı performans nedenleriyle derleyici tarafından yazılımda 'taklit edilir'. (Çoğu AVR'nin 32 kaydı vardır, bu nedenle birini (aslında iki tane) bir kenara bırakmak, olası performans ve kod boyutu avantajlarına göre çok pahalı değildir.)
JimmyB

1
MIPS hakkında bir bilgim yok ama r0'ı başka bir kayıt defterine taşımak XORing'e göre daha temiz olabilir.
JimmyB

Yani sıfırın o kadar sık ​​olduğu konusunda katılmıyorsunuz ki kayıt dosyasında bir pozisyona değer mi? O zaman muhtemelen haklısınız çünkü bu tartışmalı ve sıfır kayıt ayırmamayı tercih eden birçok ISA var. O sırada kayıt pencereleri, şube yuvaları, "eski günlerden" talimat tahmini gibi diğer tartışmalı özellikler gibi ... Bir ISA tasarlayacaksanız, kullanmayacağınıza karar verirseniz bunları kullanmanız gerekmez.
user3528438

2
Eski Berkeley RISC gazetelerinden biri olan RISC I: İndirgenmiş Komut Seti VLSI Bilgisayarını okumak ilginç olabilir . Sabit kablolu sıfır yazmacı R0'ın bir dizi VAX komutunun ve adresleme modunun tek bir RISC komutunda nasıl uygulanabileceğini gösterir.
Mark Plotnick

Yanıtlar:


14

RISC CPU'larındaki sıfır kaydı iki nedenden dolayı yararlıdır:

Yararlı bir sabit

ISA'nın kısıtlamalarına bağlı olarak, bazı yönergeleri kodlamada değişmez bir bilgi kullanamazsınız, ancak bunu r00 elde etmek için kullanabileceğinizden emin olabilirsiniz .

Diğer talimatları sentezlemek için kullanılabilir

Bu belki de en önemli nokta. Bir ISA tasarımcısı olarak, diğer faydalı talimatları sentezleyebilmek için genel amaçlı bir kaydı sıfır kaydına takas edebilirsiniz. Sentezleme talimatları iyidir, çünkü daha az gerçek talimatlara sahip olarak, bir opcode'ta bir işlemi kodlamak için daha az bite ihtiyacınız vardır, bu da talimat kodlama alanında yer açar. Bu alanı, örneğin daha büyük adres ofsetleri ve / veya değişmez değerleri elde etmek için kullanabilirsiniz.

Sıfır kaydının semantiği /dev/zero* nix sistemlerinde olduğu gibidir : ona yazılan her şey atılır ve her zaman 0 değerini tekrar okursunuz.

r0Sıfır kayıt yardımıyla nasıl sahte talimatlar yapabileceğimize dair birkaç örnek görelim :

; ### Hypothetical CPU ###

; Assembler with syntax:
; op rd, rm, rn 
; => rd: destination, rm: 1st operand, rn: 2nd operand
; literal as #lit

; On an CPU architecture with a status register (which contains arithmetic status
; flags), `sub` can be used, with r0 as destination to discard result.
cmp rn, rm     ; => sub r0, rn, rm

; `add` instruction can be used as a `mov` instruction:
mov rd, rm     ; => add rd, rm, r0
mov rd, #lit   ; => add rd, r0, #lit

; Negate:
neg rd, rm     ; => sub rd, r0, rm

; On CPU without status flags,
nop            ; => add r0, r0, r0

; RISC-V's `jal` instruction -- Jump and Link: Jump to PC-relative instruction,
; save return address into rd; we can synthesize a `jmp` instruction out of it.
jmp dest       ; => jal r0, dest

; You can even load from an absolute (direct) address, for a usually small range
; of addresses by using a literal offset as an address.
ld rd, addr    ; => ld rd, [r0, #addr]

MIPS davası

MIPS komut setine daha yakından baktım. Kullanan bir avuç sahte talimat var $zero; çoğunlukla dallar için kullanılırlar. Bulduğum şeylerin bazı örnekleri:

move $rt, $rs          => add $rt, $rs, $zero

not $rt, $rs           => nor $rt, $rs, $zero

b Label                => beq $zero, $zero, Label ; a small relative branch

bgt $rs, $rt, Label    => slt $at, $rt, $rs
                          bne $at, $zero, Label

blt $rs, $rt, Label    => slt $at, $rs, $rt
                          bne $at, $zero, Label

bge $rs, $rt, Label    => slt $at, $rs, $rt
                          beq $at, $zero, Label

ble $rs, $rt, Label    => slt $at, $rt, $rs
                          beq $at, $zero, Label

$zeroSökme işleminizde neden kaydın yalnızca bir örneğini bulduğunuza göre , belki de bilinen talimat dizilerini eşdeğer sahte talimatlara dönüştürecek kadar akıllı olan sizin sökücünüzdür.

Sıfır kaydı gerçekten faydalı mı?

Görünüşe göre ARM, AArch64'ü uygulayan (bir şekilde) yeni ARMv8-A çekirdeğinde artık 64-bit modunda sıfır-kayıt bulunduğunu gösteren bir sıfır kaydına sahip olduğunu buluyor; daha önce sıfır kaydı yoktu. (Kayıt biraz özel olsa da, bazı kodlama bağlamında sıfır kayıttır, diğerlerinde bunun yerine yığın işaretçisini belirtir )


MIPS'nin bayrak kullandığını sanmıyorum, değil mi? Sıfır kaydı, herhangi bir CPU kaydının içeriğine bakılmaksızın belirli adresleri koşulsuz olarak okuma / yazma yeteneği ekler ve "mov anında" tarzı bir işlemi kolaylaştırmaya yardımcı olur, ancak diğer movs, kaynağın kendisiyle mantıksal olarak veya kaynakla yapılabilir .
supercat

1
Nitekim, aritmetik bayrakları tutun hiçbir kayıt yoktur, bunun yerine üç talimatları bulunduğunu yardım taklit ortak koşullu dallar ( slt, slti, sltu).
Jarhmander

MIPS komut setine bakarak ve anladığım kadarıyla her talimatın önceki talimatın yürütülmesiyle alınacağı göz önüne alındığında, doğrudan hiçbir şeye sahip olmayan bir opcode'a sahip olmanın zor olup olmayacağını merak ediyorum. bir acil mod komutu yürütülürse ve bir sonraki getirilen komut bu bit desenine sahipse, işlenenin üst 16 biti önceden getirilmiş komuttan alınır mı? Bu, 32 bit hızlı mod işlemlerinin iki kelime ve iki döngü harcamak yerine iki kelimelik iki döngülü bir talimatla ele alınmasını sağlar ...
Supercat

... gerçekte kullanmak için bir işlenen ve daha sonra üçüncü bir döngüyü yüklemek.
supercat

7

Çoğu ARM / POWER / SPARC uygulamasının gizli bir RAZ kaydı vardır

Sen ARM32, SPARC vb 0 kayıt olmadığını düşünebilirsiniz ama aslında var! Mikro mimari düzeyinde, çoğu CPU tasarım mühendisi, yazılım tarafından görülemeyen bir 0 yazmaç ekler (ARM'nin sıfır kaydı görünmez) ve bu sıfır yazmaçını komut kod çözme işlemini kolaylaştırmak için kullanır.

Yazılım görünmez bir kaydı olan tipik bir modern ARM32 tasarımını düşünün, örneğin R16, 0'a kablolu. ARM32 yükünü göz önünde bulundurun, birçok ARM32 yük talimatı vakası bu formlardan birine düşüyor (Tartışmayı basit tutmak için bir süre ön yazı endekslemesini göz ardı edin ) ...

LDR ra, [rb] // NOTE:The ! is optional and represents address writeback.
LDR ra, [rb, rc](!)
LDR ra, [rb, #k](!)

İşlemcinin içinde, bu bir genel

ldr.uop ra, rb, rx, rc, #c // Internal decoded instruction format.

kayıtların okunduğu sayı aşamasına girmeden önce. Rx'in güncellenen adresi yazmak için kaydı temsil ettiğini unutmayın. İşte bazı kod çözme örnekleri:

LDR R0, [R1]      ==> ldr.uop R0, R1, R16, R16, #0 // Writeback to NULL. 
LDR R0, [R1, R2]! ==> ldr.uop R0, R1, R1, R2,   #0 // Writeback to R1.
LDR R0, [R1, #2]  ==> ldr.uop R0, R1, R16, R16, #2 // Writeback to NULL.

Devre düzeyinde, her üç yük de aslında aynı dahili talimattır ve bu tür bir dikeylik elde etmenin kolay bir yolu, bir yer kaydı R16 oluşturmaktır. R16 her zaman topraklandığından, bu talimatlar doğal olarak herhangi bir ekstra mantık olmadan doğru şekilde çözülür. Bir talimat sınıfını tek bir dahili formata eşleştirmek, mantıksal karmaşıklığı azalttığı için süperskalar uygulamalara büyük ölçüde yardımcı olur.

Başka bir neden ise, yazıları atmanın akıcı bir yoludur. Hedef kayıt ve bayraklar R16 olarak ayarlanarak talimatlar devre dışı bırakılabilir. Geri yazmayı vb. Devre dışı bırakmak için başka bir kontrol sinyali oluşturmaya gerek yoktur.

Mimariden bağımsız olarak işlemci uygulamalarının çoğu, boru hattının başlarında bir RAZ kayıt modeliyle sonuçlanır. MIPS boru hattı esasen diğer mimarilerde birkaç aşama olacak bir noktada başlar.

MIPS doğru seçimi yaptı

Bu nedenle, herhangi bir modern işlemci uygulamasında sıfır olarak okuma kaydı neredeyse zorunludur ve MIPS, dahili kod çözme mantığını nasıl aktive ettiği göz önüne alındığında, yazılımı yazılım için görünür kılan kesinlikle artı bir noktadır. MIPS işlemcilerin tasarımcıları, 0 $ zaten mevcut olduğundan ekstra bir RAZ kaydı eklemelerine gerek yoktur. RAZ, montajcı tarafından kullanılabilir olduğundan, MIPS için birçok psuedo talimatı mevcuttur ve bunu, RAZ kaydını yazılımdan gizlemek için her talimat türü için özel formatlar oluşturmak yerine kod çözme mantığının bir kısmını montajcıya itmek olarak düşünebilirsiniz. diğer mimarilerde olduğu gibi. RAZ kaydı iyi bir fikir ve bu yüzden ARMv8 kopyaladı.

ARM32'nin 0 dolarlık bir kaydı olsaydı, kod çözme mantığı daha basit olurdu ve mimari hız, alan ve güç açısından çok daha iyi olurdu. Örneğin, yukarıda sunulan LDR'nin üç versiyonundan sadece 2 format gerekli olacaktır. Benzer şekilde, MOV ve MVN talimatları için kod çözme mantığını ayırmaya gerek yoktur. Ayrıca, CMP / CMN / TST / TEQ gereksiz olur. Kısa (MUL) ve uzun çarpma (UMULL / SMULL) arasında ayrım yapmaya da gerek yoktur, çünkü kısa çarpma 0 $ 'a ayarlanmış yüksek kayıtla uzun çarpma olarak kabul edilebilir.

MIPS başlangıçta küçük bir ekip tarafından tasarlandığından, tasarımın sadeliği önemliydi ve bu nedenle RISC ruhunda açıkça 0 $ seçildi. ARM32, mimari düzeyde birçok geleneksel CISC özelliğini korur.


1
Tüm ARM32 CPU'lar sizin tarif ettiğiniz şekilde çalışmaz. Bazıları daha karmaşık yük talimatları ve / veya sicile geri yazma için daha düşük performansa sahiptir. Yani hepsi aynı şekilde kod çözemezler.
Peter Cordes

6

Yasal Uyarı: MIPS birleştiricisini gerçekten bilmiyorum, ancak 0 değerli kayıt bu mimariye özgü değil ve sanırım bildiğim diğer RISC mimarileriyle aynı şekilde kullanılıyor.

0 elde etmek için bir kayıt XORing size bir yönerge mal olacak, önceden tanımlanmış 0 değeri bir kayıt kullanmak olmaz.

Örneğin, mov RX, RYtalimat genellikle olarak uygulanır add RX, RY, R0. 0 değerli bir kayıt olmadan, xor RZ, RZher kullanmak istediğinizde gerekir mov.

Başka bir örnek, negatif sayıları test etmek için kullanılan cmpkomut ve değişkenleridir ("karşılaştır ve atla", "karşılaştır ve taşı" vb.) cmp RX, R0.


1
MOV Rx,RyOlarak uygulamada herhangi bir sorun olur AND Rx,Ry,Rymu?
Ocak 17'de supercat

3
@supercat Kodlama yapamazsınız mov RX, Immveya mov RX, mem[RY]komut kümeniz her komut için yalnızca tek bir anlık değeri ve tek bir bellek erişimini destekliyorsa.
Dmitry Grigoryev

MIPS'in hangi adresleme modlarına sahip olduğunu bilmiyorum. ARM'nin [Rx + Ry << ölçeği] ve [Rx + disp] modları olduğunu biliyorum ve ikincisini bazı mutlak adresler için kullanabilmek bazı durumlarda yararlı olmayabilir. Düz bir [Rx] modu, sıfır yer değiştirme kullanılarak [Rx + disp] yoluyla taklit edilebilir. MIPS ne kullanıyor?
17'de supercat

movkötü bir örnektir; sıfır kaydı yerine hemen 0 ile uygulayabilirsiniz. örn ori dst, src, 0. Ama evet, sen olmasaydı mov-acil kayıt için bir işlem kodu gerekiyordu addiu $dst, $zero, 1234gibi luiancak bunun yerine üst 16 alt 16 bit Ve kullanamadı norveya subtek işlenen değil / neg inşa etmek .
Peter Cordes

@supercat: Hala merak ediyorsanız: klasik MIPS'nin tek bir adresleme modu vardır: register + disp16. Modern MIPS, FP yükleri / depoları için 2 kayıtlı adresleme modları için başka opcodlar ekleyerek dizi indekslemeyi hızlandırdı. (Ama yine de tamsayı yükleme / saklama için değil, belki de 2 adres regs + bir mağaza için bir veri reg için tamsayı kayıt dosyasında daha fazla okuma portu gerektirebilir çünkü bkz . Bir kaydı ofset olarak kullanma )
Peter Cordes

3

Kayıt bankanızın sonunda birkaç ipucunu toprağa bağlamak ucuzdur (tam şişmiş bir kayıt yapmaktan daha ucuzdur).

Gerçek xor yapmak kapıları değiştirmek ve daha sonra kayıt defterinde saklamak için biraz güç ve zaman alır, neden mevcut bir 0 değeri kolayca mevcut olduğunda bu maliyeti ödersiniz.

Modern cpus ayrıca, xor eax eaxkayıt yeniden adlandırma yoluyla bir komutun sonucu olarak kullanabilecekleri (gizli) 0 değerli bir kayıt defterine sahiptir.


6
Gerçek maliyeti R0birkaç kablo topraklama değil, aslında kayıtlarla ilgilenen her talimat için bir kod ayırmak zorunda olmasıdır.
Dmitry Grigoryev

Xor kırmızı bir ringa balığıdır. xor-zeroing yalnızca CPU'ların deyimi tanıdığı ve girişlere bağımlılıktan kaçındığı x86'da iyidir. İşaret ettiğiniz gibi, Sandybridge ailesi bunun için bir kayıt bile yapmıyor, sadece kayıt-yeniden adlandırma aşamasında kullanıyor. ( X86 derlemesinde sıfırı sıfıra ayarlamanın en iyi yolu nedir: xor, mov veya ve? ). Ancak MIPS'de bir XORing kaydının yanlış bağımlılığı olurdu; bellek bağımlılığı sıralama kuralları (C ++ HW eşdeğeri std::memory_order_consume), bağımlılığın yayılması için XOR gerektirir.
Peter Cordes

Sıfır kaydınız yoksa, hemen bir kayda taşımak için bir opcode eklersiniz. Gibi luiama 16 ile sola kaydırılmadı. Böylece bir talimatla bir sicile hala küçük bir sayı koyabilirsiniz. Yanlış bağımlılıkla sadece sıfıra izin vermek delilik olur. (Normal MIPS, addiu $dst, $zero, 1234veya ile sıfırdan farklı değerler oluşturur ori, bu nedenle "güç maliyeti" bağımsız değişkeniniz bozulur. Bir ALU'yu tetiklemekten kaçınmak isterseniz, ADD veya OR yazılımına sahip olmak yerine mov-instant için kaydolmak için bir opcode eklersiniz sıfır ile hemen.)
Peter Cordes
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.