왜 두 레지스터를 XOR하여 0을 생성 할 수 있는데 MIPS가 R0을“0”으로 사용합니까? 비교 (bgez, bgtz, blez, bltz) 두

나는 사소한 질문에 대한 답을 찾고 있다고 생각합니다. MIPS 아키텍처가 레지스터 자체에 대해 XOR을 수행하여 동일한 결과를 얻을 수있을 때 레지스터에서 명시 적 “0”값을 사용하는 이유를 이해하려고합니다. 작업이 이미 완료되었다고 말할 수 있습니다. 그러나 많은 “제로”값을 사용하는 상황을 실제로 상상할 수 없습니다. 나는 Hennessey의 원본 논문을 읽었으며, 정당한 정당화없이 사실의 문제로 0을 할당합니다.

하드 코딩 된 이진 할당이 0 인 논리적 이유가 있습니까?

업데이트 :
PIC32MZ의 MIPS 코어 용 xc32-gcc 실행 파일의 8k에서 “0”이라는 단일 인스턴스가 있습니다.

add     t3,t1,zero

실제 답변 :
MIPS 및 조건 코드에 대한 정보를 보유한 사람에게 현상금을 수여했습니다. 정답은 실제로 조건에 대한 MIPS 아키텍처에 있습니다. 처음에 이것에 시간을 할당하고 싶지는 않았지만 opensparc , MIPS-V 및 OpenPOWER (이 문서는 내부에 있음)의 아키텍처를 검토했으며 요약 결과는 다음과 같습니다. R0 레지스터는 파이프 라인 아키텍처로 인해 분기를 비교하는 데 필요합니다.

  • 0과 분기에 대한 정수 비교 (bgez, bgtz, blez, bltz)
  • 두 레지스터와 분기를 정수로 비교 (beq, bne)
  • 정수는 두 레지스터와 트랩을 비교합니다 (teq, tge, tlt, tne)
  • 정수 비교 레지스터 및 즉시 및 트랩 (teqi, tgei, tlti, tnei)

단순히 하드웨어가 구현 방식에 따라 달라집니다. MIPS-V 매뉴얼에는 68 페이지의 참조되지 않은 인용문이 있습니다.

조건부 분기는 조건 코드 (x86, ARM, SPARC, PowerPC)를 사용하지 않고 두 레지스터 (PA-RISC 및 Xtensa ISA에서도 수행됨) 간의 산술 비교 연산을 포함하거나 하나의 레지스터 만 0과 비교하도록 설계되었습니다 ( Alpha, MIPS) 또는 동등성 (MIPS) 전용의 두 레지스터. 이 설계는 결합 된 비교 및 ​​분기 명령을 일반 파이프 라인으로 통합하고 추가 조건 코드 상태 또는 임시 레지스터 사용을 피하며 정적 코드 크기 및 동적 명령 페치 트랙을 줄인다는 관찰에 동기를 부여했습니다. 또 다른 요점은 0에 대한 비교에는 사소한 회로 지연이 필요하며 (특히 고급 프로세스에서 정적 논리로 이동 한 후) 산술 크기 비교만큼 비용이 많이 든다는 것입니다. 융합 비교 및 ​​분기 명령의 또 다른 장점은 분기가 프런트 엔드 명령 스트림에서 더 일찍 관찰되므로 더 일찍 예측할 수 있다는 것입니다. 동일한 조건 코드를 기반으로 여러 분기를 수행 할 수있는 경우 조건 코드가있는 설계에 이점이있을 수 있지만이 경우는 비교적 드문 것으로 판단됩니다.

인용 된 섹션의 작성자에게 MIPS-V 문서가 적중되지 않습니다. 시간과 배려에 감사합니다.



답변

RISC CPU의 제로 레지스터는 다음 두 가지 이유로 유용합니다.

유용한 상수입니다

ISA의 제한 사항에 따라 일부 명령어 인코딩에서 리터럴을 사용할 수 없지만 r00을 얻기 위해 리터럴을 사용할 수 있습니다 .

다른 명령을 합성하는 데 사용할 수 있습니다

이것은 아마도 가장 중요한 요점 일 것입니다. ISA 디자이너는 범용 레지스터를 제로 레지스터로 교환하여 다른 유용한 명령어를 합성 할 수 있습니다. 실제 명령어가 적을수록 연산 인코딩으로 연산을 인코딩하는 데 더 적은 비트가 필요하므로 명령어 인코딩 공간에서 공간을 확보하기 때문에 명령어 합성이 좋습니다. 이 공간을 사용하여 더 큰 주소 오프셋 및 / 또는 리터럴을 가질 수 있습니다.

제로 레지스터의 의미는 /dev/zero* nix 시스템에서 와 같습니다. 여기 에 기록 된 모든 내용이 삭제되고 항상 0을 다시 읽습니다.

r0제로 레지스터 의 도움으로 의사 명령어를 만드는 방법에 대한 몇 가지 예를 살펴 보겠습니다 .

; ### 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의 경우

MIPS 명령어 세트를 자세히 살펴 보았습니다. 사용하는 소수의 의사 명령어가 있습니다 $zero. 그들은 주로 가지에 사용됩니다. 내가 찾은 것의 몇 가지 예는 다음과 같습니다.

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

$zero디스 어셈블리 에서 레지스터의 인스턴스를 하나만 찾은 이유 는 알려진 명령 시퀀스를 동등한 의사 명령어로 변환하기에 충분히 똑똑한 디스어셈블러 일 것입니다.

제로 레지스터가 실제로 유용합니까?

아마도 ARM은 AArch64를 구현하는 새로운 ARMv8-A 코어에 64 비트 모드에서 0 레지스터가있을 정도로 영점 레지스터가 충분히 유용하다는 것을 알았습니다. 이전에는 제로 레지스터가 없었습니다. (레지스터는 약간 특수하지만 일부 인코딩 컨텍스트에서는 제로 레지스터이며 다른 경우 스택 포인터를 지정합니다. )


답변

대부분의 ARM / POWER / SPARC 구현에는 숨겨진 RAZ 레지스터가 있습니다.

ARM32, SPARC 등에는 0 레지스터가 없지만 실제로는 있다고 생각할 수 있습니다! 마이크로 아키텍처 수준에서 대부분의 CPU 설계 엔지니어는 소프트웨어에 보이지 않을 수있는 0 레지스터 (ARM의 0 레지스터는 보이지 않음)를 추가하고 해당 0 레지스터를 사용하여 명령 디코딩을 간소화합니다.

R16과 같이 소프트웨어가 보이지 않는 레지스터가있는 일반적인 최신 ARM32 설계를 고려하십시오. ARM32로드를 고려하면 ARM32로드 명령의 많은 경우가 이러한 형식 중 하나에 해당합니다 (토론을 간단하게 유지하기 위해 사전 포스트 인덱싱 무시) ) …

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

프로세서 내부에서 일반으로 디코딩

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

레지스터를 읽는 이슈 단계에 들어가기 전에. rx는 업데이트 된 주소를 다시 쓰기위한 레지스터를 나타냅니다. 다음은 몇 가지 디코드 예입니다.

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.

회로 수준에서 세 가지 부하는 모두 실제로 동일한 내부 명령이며 이러한 직교성을 얻는 쉬운 방법은 접지 레지스터 R16을 만드는 것입니다. R16은 항상 접지되어 있으므로 이러한 명령어는 추가 로직없이 자연스럽게 올바르게 디코딩됩니다. 명령어 클래스를 단일 내부 형식으로 매핑하면 논리 복잡성이 줄어들 기 때문에 슈퍼 스칼라 구현에 큰 도움이됩니다.

또 다른 이유는 쓰기를 버리는 능률적 인 방법입니다. 대상 레지스터 및 플래그를 R16으로 설정하면 명령어를 비활성화 할 수 있습니다. 다시 쓰기 등을 비활성화하기 위해 다른 제어 신호를 만들 필요가 없습니다.

아키텍처에 관계없이 대부분의 프로세서 구현은 파이프 라인 초기에 RAZ 레지스터 모델로 끝납니다. MIPS 파이프 라인은 본질적으로 다른 아키텍처에서 몇 단계가되는 시점에서 시작됩니다.

MIPS가 올바른 선택을했습니다

따라서 0으로 읽기 레지스터는 최신 프로세서 구현에서 거의 필수이며 MIPS가 내부 디코드 로직을 간소화하는 방법을 감안할 때 소프트웨어에서 볼 수있는 MIPS는 확실히 장점입니다. $ 0가 이미 시작되었으므로 MIPS 프로세서 설계자는 추가 RAZ 레지스터를 추가 할 필요가 없습니다. RAZ는 어셈블러에서 사용할 수 있기 때문에 MIPS에서 많은 의사 명령어를 사용할 수 있으며, 소프트웨어에서 RAZ 레지스터를 숨기도록 각 명령어 유형에 대한 전용 형식을 만드는 대신 디코드 로직의 일부를 어셈블러 자체로 푸시하는 것으로 생각할 수 있습니다. 다른 아키텍처와 마찬가지로. RAZ 레지스터는 좋은 생각이므로 ARMv8에서 복사 한 이유입니다.

ARM32에 레지스터가 $ 0이면 디코드 로직이 단순 해졌으며 아키텍처는 속도, 면적 및 전력면에서 훨씬 더 나았을 것입니다. 예를 들어 위에 제시된 3 가지 버전의 LDR 중 2 가지 형식 만 필요합니다. 마찬가지로 MOV 및 MVN 명령어에 대한 디코딩 로직을 예약 할 필요가 없습니다. 또한 CMP / CMN / TST / TEQ는 중복됩니다. 짧은 곱셈은 높은 레지스터가 $ 0 등으로 설정된 긴 곱셈으로 간주 될 수 있으므로 짧은 (MUL)과 긴 곱셈 (UMULL / SMULL)을 구분할 필요가 없습니다.

MIPS는 처음에 소규모 팀에 의해 설계되었으므로 설계의 단순성이 중요하므로 RISC의 정신에 따라 $ 0가 명시 적으로 선택되었습니다. ARM32는 아키텍처 수준에서 많은 기존 CISC 기능을 유지합니다.


답변

Disclamer : MIPS 어셈블러는 잘 모르지만 0- 값 레지스터는이 아키텍처에 고유하지 않으며 다른 RISC 아키텍처와 동일한 방식으로 사용됩니다.

0을 얻기 위해 레지스터를 XOR하는 것은 하나의 명령 비용이 들지만 사전 정의 된 0 값 레지스터를 사용하는 것은 아닙니다.

예를 들어, mov RX, RY명령어는 종종로 구현됩니다 add RX, RY, R0. 값이 0 인 레지스터가 없으면를 xor RZ, RZ사용할 때마다해야합니다 mov.

또 다른 예는 음수를 테스트하는 데 사용되는 cmp명령어 및 해당 변형 (예 : “비교 및 점프”, “비교 및 이동”등) cmp RX, R0입니다.


답변

레지스터 뱅크 끝 부분에 몇 개의 리드를 연결하는 것이 저렴합니다 (풀 레지스터를 만드는 것보다 저렴합니다).

실제 xor를 수행하려면 게이트를 전환하고 레지스터에 저장하는 데 약간의 전력과 시간이 필요합니다. 기존의 0 값을 쉽게 사용할 수있을 때 비용을 지불하는 이유는 무엇입니까?

최신 CPU에는 xor eax eax레지스터 이름 바꾸기를 통한 명령 의 결과로 사용할 수있는 (숨겨진) 0 값 레지스터가 있습니다 .