Tại sao MIPS sử dụng R0 làm 0 không khi mà bạn chỉ có thể XOR hai thanh ghi để tạo 0?


10

Tôi nghĩ rằng tôi đang tìm kiếm một câu trả lời cho một câu hỏi đố. Tôi đang cố gắng hiểu tại sao kiến ​​trúc MIPS sử dụng giá trị "không" rõ ràng trong một thanh ghi khi bạn có thể đạt được điều tương tự bằng cách chỉ XOR bất kỳ thanh ghi nào chống lại chính nó. Người ta có thể nói rằng các hoạt động đã được thực hiện cho bạn; tuy nhiên, tôi thực sự không thể tưởng tượng được một tình huống mà bạn sẽ sử dụng nhiều giá trị "không". Tôi đọc các bài báo gốc của Hennessey, và nó chỉ gán số 0 là vấn đề thực tế mà không có bất kỳ lời biện minh thực sự nào.

Có một lý do hợp lý để có một phép gán nhị phân được mã hóa cứng bằng 0 không?

cập nhật: Trong 8k của một tệp thực thi từ xc32-gcc cho lõi MIPS trong PIC32MZ, tôi có một ví dụ duy nhất là "không".

add     t3,t1,zero

câu trả lời thực tế: Tôi đã trao tiền thưởng cho người có thông tin về MIPS và mã điều kiện. Câu trả lời thực sự nằm trong kiến ​​trúc MIPS cho các điều kiện. Mặc dù ban đầu tôi không muốn chỉ định thời gian cho việc này, tôi đã xem xét kiến ​​trúc cho opensparc , MIPS-V và OpenPOWER (tài liệu này là nội bộ) và đây là những phát hiện tóm tắt. Thanh ghi R0 cần thiết để so sánh trên các nhánh do kiến ​​trúc của đường ống.

  • số nguyên so với 0 và nhánh (bgez, bgtz, blez, bltz)
  • số nguyên so sánh hai thanh ghi và nhánh (beq, bne)
  • số nguyên so sánh hai thanh ghi và bẫy (teq, tge, tlt, tne)
  • đăng ký so sánh số nguyên và ngay lập tức và bẫy (teqi, tgei, tlti, tnei)

Nó chỉ đơn giản là đi xuống làm thế nào phần cứng trông trong thực hiện. Từ hướng dẫn sử dụng MIPS-V, có một trích dẫn chưa được kiểm chứng ở trang 68:

Các nhánh có điều kiện được thiết kế để bao gồm các hoạt động so sánh số học giữa hai thanh ghi (cũng được thực hiện trong PA-RISC và Xtensa ISA), thay vì sử dụng mã điều kiện (x86, ARM, SPARC, PowerPC) hoặc chỉ so sánh một thanh ghi với 0 ( Alpha, MIPS) hoặc hai thanh ghi chỉ cho đẳng thức (MIPS). Thiết kế này được thúc đẩy bởi việc quan sát rằng một lệnh hướng dẫn so sánh và nhánh kết hợp vào một đường ống thông thường, tránh trạng thái mã điều kiện bổ sung hoặc sử dụng một thanh ghi tạm thời, và giảm kích thước mã tĩnh và tìm nạp lệnh động. Một điểm khác là so sánh với số 0 đòi hỏi độ trễ mạch không tầm thường (đặc biệt là sau khi chuyển sang logic tĩnh trong các quy trình nâng cao) và do đó gần như đắt như so sánh cường độ số học. Một ưu điểm khác của lệnh so sánh và nhánh được hợp nhất là các nhánh được quan sát sớm hơn trong luồng lệnh phía trước và do đó có thể dự đoán sớm hơn. Có lẽ có một lợi thế cho một thiết kế với mã điều kiện trong trường hợp có thể lấy nhiều nhánh dựa trên cùng một mã điều kiện, nhưng chúng tôi tin rằng trường hợp này là tương đối hiếm.

Tài liệu MIPS-V không đánh vào tác giả của phần được trích dẫn. Tôi cảm ơn tất cả mọi người đã dành thời gian và sự quan tâm của họ.


6
Bạn thường muốn sử dụng một thanh ghi có giá trị 0 trong một số thao tác làm giá trị nguồn. Nó sẽ là một số chi phí để không đăng ký trước các hoạt động đó, vì vậy hiệu suất sẽ mang lại lợi ích nếu bạn chỉ có thể sử dụng số 0 được cung cấp thay vì tự tạo bất cứ khi nào cần. Ví dụ bao gồm thêm một cờ mang.
JimmyB

3
Trên kiến ​​trúc AVR, gcc chú ý khởi tạo r1 về 0 khi khởi động và không bao giờ chạm lại giá trị đó, sử dụng r1 làm nguồn bất cứ nơi nào có thể sử dụng 0 ngay lập tức. Ở đây, trình đăng ký zero chuyên dụng được 'mô phỏng' trong phần mềm bởi trình biên dịch vì lý do hiệu năng. (Hầu hết các AVR đều có 32 thanh ghi, do đó, đặt một (hai, thực tế) sang một bên không tốn nhiều chi phí liên quan đến lợi ích hiệu suất và kích thước mã có thể có.)
JimmyB

1
Tôi không biết về MIPS, nhưng có thể nhanh hơn để chuyển r0 sang một thanh ghi khác so với XORing mà đăng ký để xóa nó.
JimmyB

Vì vậy, bạn không đồng ý ở điểm 0 thường xuyên đến mức nó có giá trị một vị trí trong tệp đăng ký? Vậy thì có lẽ bạn đúng vì điều này đúng, điều này gây tranh cãi và có rất nhiều ISA chọn không đặt trước một đăng ký bằng không. Giống như các tính năng gây tranh cãi khác tại thời điểm đó như cửa sổ đăng ký, vị trí nhánh, hướng dẫn sử dụng từ "ngày xưa" ... nếu bạn định thiết kế một ISA, bạn không phải sử dụng chúng nếu bạn quyết định không sử dụng.
dùng3528438

2
Có thể rất thú vị khi đọc một trong những bài báo cũ của RISC Berkeley, RISC I: Bộ hướng dẫn giảm máy tính VLSI . Nó cho thấy cách sử dụng một thanh ghi zero có dây cứng, R0, cho phép một số hướng dẫn VAX và các chế độ địa chỉ được thực hiện trong một lệnh RISC duy nhất.
Đánh dấu Plotnick

Câu trả lời:


14

Thanh ghi zero trên CPU RISC rất hữu ích vì hai lý do:

Đó là một hằng số hữu ích

Tùy thuộc vào các hạn chế của ISA, bạn không thể sử dụng một chữ trong một số mã hóa hướng dẫn, nhưng bạn có thể chắc chắn rằng bạn có thể sử dụng điều đó r0để nhận 0.

Nó có thể được sử dụng để tổng hợp các hướng dẫn khác

Đây có lẽ là điểm quan trọng nhất. Là một nhà thiết kế ISA, bạn có thể đánh đổi một thanh ghi mục đích chung thành một thanh ghi zero để có thể tổng hợp các hướng dẫn hữu ích khác. Hướng dẫn tổng hợp là tốt bởi vì có ít hướng dẫn thực tế hơn, bạn cần ít bit hơn để mã hóa một hoạt động trong opcode, giúp giải phóng không gian trong không gian mã hóa lệnh. Bạn có thể sử dụng không gian đó để có ví dụ lớn hơn về địa chỉ và / hoặc chữ.

Các ngữ nghĩa của thanh ghi zero giống như /dev/zerotrên các hệ thống * nix: mọi thứ được viết cho nó đều bị loại bỏ và bạn luôn đọc lại 0.

Chúng ta hãy xem một vài ví dụ về cách chúng ta có thể thực hiện các hướng dẫn giả với sự trợ giúp của thanh ghi r0số không:

; ### 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]

Trường hợp của MIPS

Tôi nhìn kỹ hơn vào tập lệnh MIPS. Có một số hướng dẫn giả sử dụng $zero; chúng chủ yếu được sử dụng cho các chi nhánh. Dưới đây là một số ví dụ về những gì tôi đã tìm thấy:

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

Về lý do tại sao bạn chỉ tìm thấy một phiên bản của thanh $zeroghi trong quá trình tháo gỡ của mình, có lẽ đó là trình dịch ngược của bạn đủ thông minh để chuyển đổi các chuỗi hướng dẫn đã biết thành hướng dẫn giả tương đương của chúng.

Là đăng ký không thực sự hữu ích?

Rõ ràng, ARM thấy rằng có một thanh ghi 0 đủ hữu ích để trong lõi ARMv8-A mới (phần nào) của họ, thực hiện AArch64, giờ đây đã có thanh ghi 0 ở chế độ 64 bit; không có đăng ký trước. (Tuy nhiên, thanh ghi hơi đặc biệt, trong một số bối cảnh mã hóa, nó là thanh ghi không, trong một số khác, nó thay vào đó chỉ định con trỏ ngăn xếp )


Tôi không nghĩ MIPS sử dụng cờ, phải không? Thanh ghi zero bổ sung khả năng đọc / ghi vô điều kiện một số địa chỉ nhất định mà không cần quan tâm đến nội dung của bất kỳ thanh ghi CPU nào và giúp tạo điều kiện cho hoạt động kiểu "ngay lập tức", nhưng các Mov khác có thể được thực hiện bằng cách logic hoặc tự lấy nguồn .
supercat

1
Trên thực tế, không có đăng ký giữ cờ số học, thay vì có ba hướng dẫn chi nhánh giúp đỡ thi đua chung có điều kiện ( slt, slti, sltu).
Jarhmander

Nhìn vào tập lệnh MIPS, và cho rằng từ những gì tôi hiểu, mỗi lệnh sẽ được tìm nạp vào thời điểm lệnh trước đó thực thi, tôi tự hỏi liệu có khó để có một opcode không trực tiếp với bất cứ thứ gì không mà thay vào đó nói rằng nếu một lệnh chế độ tức thời thực thi và lệnh được tìm nạp tiếp theo có mẫu bit đó, 16 bit trên của toán hạng sẽ được lấy từ lệnh được tìm nạp trước? Điều đó sẽ xử lý các hoạt động ở chế độ tức thời 32 bit bằng một lệnh hai chu kỳ hai từ thay vì phải sử dụng hai từ và hai chu kỳ ...
supercat

... tải một toán hạng và sau đó là chu kỳ thứ ba để thực sự sử dụng nó.
supercat

7

Hầu hết các triển khai ARM / POWER / SPARC đều có thanh ghi RAZ ẩn

Bạn có thể nghĩ rằng ARM32, SPARC, v.v. không có đăng ký 0 nhưng thực tế họ có! Ở cấp độ kiến ​​trúc vi mô, hầu hết các kỹ sư thiết kế CPU đều thêm vào một thanh ghi 0 có thể ẩn với phần mềm (thanh ghi 0 của ARM là vô hình) và sử dụng thanh ghi 0 đó để giải mã hướng dẫn.

Hãy xem xét một thiết kế ARM32 hiện đại điển hình có một thanh ghi vô hình phần mềm, giả sử R16 có dây đến 0. Hãy xem xét tải ARM32, nhiều trường hợp lệnh tải ARM32 rơi vào một trong các hình thức này (Bỏ qua lập chỉ mục trước khi đăng để giữ cho cuộc thảo luận đơn giản ) ...

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

Bên trong bộ xử lý, điều này giải mã thành chung

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

trước khi bước vào giai đoạn phát hành nơi đăng ký được đọc. Lưu ý rằng rx đại diện cho đăng ký để ghi lại địa chỉ cập nhật. Dưới đây là một số ví dụ giải mã:

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.

Ở cấp độ mạch, cả ba tải thực sự là cùng một hướng dẫn nội bộ và một cách dễ dàng để có được tính trực giao này là tạo ra một thanh ghi mặt đất R16. Vì R16 luôn được nối đất, các hướng dẫn này tự nhiên giải mã chính xác mà không cần thêm logic. Ánh xạ một lớp hướng dẫn sang một định dạng nội bộ duy nhất giúp ích rất nhiều cho việc triển khai siêu khối vì nó làm giảm độ phức tạp logic.

Một lý do khác là một cách hợp lý để vứt bỏ viết. Hướng dẫn có thể bị vô hiệu hóa bằng cách chỉ cần đặt thanh ghi đích và cờ thành R16. Không cần tạo bất kỳ tín hiệu điều khiển nào khác để vô hiệu hóa ghi lại, v.v.

Hầu hết các triển khai bộ xử lý bất kể kiến ​​trúc kết thúc với mô hình thanh ghi RAZ sớm trong đường ống. Đường ống MIPS về cơ bản bắt đầu tại một điểm mà trong các kiến ​​trúc khác sẽ là một vài giai đoạn.

MIPS đã lựa chọn đúng

Do đó, một thanh ghi đọc bằng 0 gần như là bắt buộc trong bất kỳ triển khai bộ xử lý hiện đại nào và MIPS làm cho nó hiển thị với phần mềm chắc chắn là một điểm cộng cho cách nó hợp lý hóa logic giải mã bên trong. Người thiết kế bộ xử lý MIPS không cần thêm vào một thanh ghi RAZ bổ sung vì $ 0 đã sẵn sàng. Do RAZ có sẵn cho trình biên dịch, nên có rất nhiều hướng dẫn psuedo có sẵn cho MIPS và người ta có thể nghĩ điều này như là đẩy một phần logic giải mã sang chính trình biên dịch thay vì tạo các định dạng chuyên dụng cho từng loại lệnh để ẩn thanh ghi RAZ khỏi phần mềm như với các kiến ​​trúc khác. Thanh ghi RAZ là một ý tưởng tốt và đó là lý do ARMv8 sao chép nó.

Nếu ARM32 có đăng ký $ 0, logic giải mã sẽ trở nên đơn giản hơn và kiến ​​trúc sẽ tốt hơn nhiều về tốc độ, diện tích và sức mạnh. Ví dụ, trong ba phiên bản LDR được trình bày ở trên, chỉ cần 2 định dạng. Tương tự, không cần phải dự trữ logic giải mã cho các lệnh MOV và MVN. Ngoài ra, CMP / CMN / TST / TEQ sẽ trở nên dư thừa. Cũng không cần phân biệt giữa phép nhân ngắn (MUL) và phép nhân dài (UMULL / SMULL) vì phép nhân ngắn có thể được coi là phép nhân dài với thanh ghi cao được đặt thành $ 0, v.v.

Do MIPS ban đầu được thiết kế bởi một nhóm nhỏ, sự đơn giản của thiết kế rất quan trọng và do đó $ 0 được chọn rõ ràng theo tinh thần của RISC. ARM32 giữ lại rất nhiều tính năng CISC truyền thống ở cấp độ kiến ​​trúc.


1
Không phải tất cả các CPU ARM32 hoạt động theo cách bạn mô tả. Một số có hiệu suất thấp hơn cho các hướng dẫn tải phức tạp hơn và / hoặc để ghi lại vào thanh ghi. Vì vậy, tất cả họ không thể giải mã chính xác theo cùng một cách.
Peter Cordes

6

Disclamer: Tôi không thực sự biết trình biên dịch MIPS, nhưng thanh ghi 0 giá trị không phải là duy nhất cho kiến ​​trúc này và tôi đoán nó được sử dụng theo cách tương tự như trong các kiến ​​trúc RISC khác mà tôi biết.

XOR một thanh ghi để có được 0 sẽ khiến bạn mất một lệnh, trong khi sử dụng thanh ghi 0 giá trị được xác định trước thì không.

Ví dụ, mov RX, RYhướng dẫn thường được thực hiện như add RX, RY, R0. Nếu không có đăng ký 0 giá trị, bạn sẽ phải xor RZ, RZsử dụng mỗi lần bạn muốn sử dụng mov.

Một ví dụ khác là cmphướng dẫn và các biến thể của nó (như "so sánh và nhảy", "so sánh và di chuyển", v.v.), trong đó cmp RX, R0được sử dụng để kiểm tra các số âm.


1
Sẽ có bất kỳ vấn đề thực hiện MOV Rx,Rynhư AND Rx,Ry,Ry?
supercat

3
@supercat Bạn sẽ không thể mã hóa mov RX, Immhoặc mov RX, mem[RY]nếu tập lệnh của bạn chỉ hỗ trợ một giá trị ngay lập tức và một truy cập bộ nhớ duy nhất cho mỗi hướng dẫn.
Dmitry Grigoryev

Tôi không quen với các chế độ địa chỉ mà MIPS có. Tôi biết ARM có chế độ [Rx + Ry << scale] và [Rx + dist], và trong khi có thể sử dụng cái sau cho một số địa chỉ tuyệt đối có thể hữu ích trong một số trường hợp, nó thường không cần thiết. Chế độ [Rx] thẳng có thể được mô phỏng qua [Rx + dist] bằng cách sử dụng độ dịch chuyển bằng không. MIPS sử dụng cái gì?
supercat

movlà một ví dụ nghèo nàn; bạn có thể thực hiện nó với 0 ngay lập tức thay vì đăng ký 0. ví dụ ori dst, src, 0. Nhưng vâng, bạn cần một mã opcode ngay lập tức để đăng ký nếu bạn không có addiu $dst, $zero, 1234, như thế luinhưng với 16 bit thấp hơn thay vì trên 16. Và bạn không thể sử dụng norhoặc subxây dựng một toán hạng không / neg .
Peter Cordes

@supercat: trong trường hợp bạn vẫn tự hỏi: MIPS cổ điển chỉ có một chế độ địa chỉ duy nhất: register + dist16. MIPS hiện đại đã thêm các opcodes khác cho các chế độ địa chỉ 2 thanh ghi cho tải / lưu trữ FP, tăng tốc độ lập chỉ mục mảng. (Nhưng vẫn không cho số nguyên load / store, có lẽ bởi vì đó có thể yêu cầu cổng đọc hơn trong tệp số nguyên đăng ký cho 2 regs địa chỉ + a reg dữ liệu cho một cửa hàng See. Sử dụng một thanh ghi là giờ chênh lệch )
Peter Cordes

3

Nhập một vài khách hàng tiềm năng vào cuối ngân hàng đăng ký của bạn là rẻ (rẻ hơn so với làm cho nó trở thành một đăng ký đầy đủ).

Làm xor thực tế cần một chút năng lượng và thời gian để chuyển đổi các cổng và sau đó lưu nó vào thanh ghi, tại sao phải trả chi phí đó khi giá trị 0 hiện có có thể dễ dàng có sẵn.

Các cpus hiện đại cũng có một thanh ghi 0 giá trị (ẩn) mà họ có thể sử dụng như là kết quả của một xor eax eaxhướng dẫn thông qua việc đổi tên đăng ký.


6
Chi phí thực sự R0không nằm ở việc nối đất một vài dây mà thực tế là bạn phải dự trữ mã cho nó trong mọi hướng dẫn liên quan đến các thanh ghi.
Dmitry Grigoryev

Xor là một cá trích đỏ. xor-zeroing chỉ tốt trên x86, trong đó CPU nhận ra thành ngữ và tránh sự phụ thuộc vào đầu vào. Như bạn chỉ ra, Sandybridge-gia đình thậm chí không chạy một uop cho nó, chỉ xử lý nó trong giai đoạn đăng ký đổi tên. ( Cách tốt nhất để đặt một thanh ghi về 0 trong cụm x86: xor, Mov hoặc và? ). Nhưng trên MIPS, XOR một thanh ghi sẽ có sự phụ thuộc sai; quy tắc đặt hàng phụ thuộc bộ nhớ (tương đương CTNH của C ++ std::memory_order_consume) yêu cầu XOR truyền bá sự phụ thuộc.
Peter Cordes

Nếu bạn không có đăng ký bằng 0, bạn sẽ bao gồm một opcode để chuyển ngay lập tức sang đăng ký. Thích luinhưng không dịch chuyển trái bởi 16. Vì vậy, bạn vẫn có thể đặt một số nhỏ vào một thanh ghi với một hướng dẫn. Cho phép chỉ bằng 0 với một phụ thuộc sai sẽ là điên rồ. (MIPS bình thường tạo ra các giá trị khác không bằng addiu $dst, $zero, 1234hoặc ori, do đó, đối số "chi phí điện năng" của bạn bị phá vỡ. Nếu bạn muốn tránh bắn ALU, bạn sẽ bao gồm một opcode để di chuyển ngay lập tức thay vì có phần mềm THÊM hoặc HOẶC ngay lập tức với số không.)
Peter Cordes
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.