왜 두 레지스터를 XOR하여 0을 생성 할 수 있는데 MIPS가 R0을“0”으로 사용합니까?


10

나는 사소한 질문에 대한 답을 찾고 있다고 생각합니다. 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 문서가 적중되지 않습니다. 시간과 배려에 감사합니다.


6
일부 작업에서 0 값 레지스터를 소스 값으로 자주 사용 하려고 합니다. 이러한 작업을 수행하기 전에 레지스터를 0으로 만드는 것은 약간의 오버 헤드가 있으므로 필요할 때마다 직접 생성하지 않고 제공된 0을 사용하면 성능이 향상됩니다. 예에는 캐리 플래그 추가가 포함됩니다.
JimmyB

3
AVR 아키텍처에서 gcc는 시작시 r1을 0으로 초기화하는 데주의를 기울이고 r0을 즉시 0을 사용할 수없는 소스로 사용하여 해당 값을 다시는 건드리지 않습니다. 여기서 전용 제로 레지스터는 성능상의 이유로 컴파일러에 의해 소프트웨어에서 '에뮬레이트'됩니다. (대부분의 AVR에는 32 개의 레지스터가 있으므로 가능한 한 성능 및 코드 크기 이점과 관련하여 1 개 (실제 2 개)를 따로 설정하는 데 비용이 많이 들지 않습니다.)
JimmyB

1
MIPS에 대해서는 잘 모르겠지만 XORing에 비해 r0을 다른 레지스터로 이동하는 것이 더 빠를 수 있습니다.
JimmyB

따라서 0이 너무 자주 등록 파일의 위치에 가치가 있다는 점에 동의하지 않습니까? 그렇다면 논란의 여지가 많고 많은 ISA가 제로 레지스터를 예약하지 않기로 선택했기 때문에 아마 당신은 옳습니다. 등록 창, 분기 슬롯, "이전 시대"의 명령 설명과 같은 다른 논란의 여지가있는 기능과 마찬가지로 ISA를 디자인하려는 경우, 사용하지 않기로 결정한 경우에는이를 사용할 필요가 없습니다.
user3528438

2
오래된 버클리 RISC 논문, RISC I : 축소 된 명령 세트 VLSI 컴퓨터 중 하나를 읽는 것이 흥미로울 수 있습니다 . 또한 하드 와이어드 제로 레지스터 R0을 사용하여 여러 VAX 명령어와 주소 지정 모드를 단일 RISC 명령어로 구현하는 방법을 보여줍니다.
Mark Plotnick

답변:


14

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 레지스터가있을 정도로 영점 레지스터가 충분히 유용하다는 것을 알았습니다. 이전에는 제로 레지스터가 없었습니다. (레지스터는 약간 특수하지만 일부 인코딩 컨텍스트에서는 제로 레지스터이며 다른 경우 스택 포인터를 지정합니다. )


MIPS가 플래그를 사용한다고 생각하지 않습니까? 제로 레지스터는 CPU 레지스터의 내용에 관계없이 특정 주소를 무조건 읽기 / 쓰기 할 수있는 기능을 추가하고 "mov 즉시"스타일 작업을 용이하게하는데 도움이되지만 다른 mov는 자체적으로 소스를 논리적으로 연결하여 수행 할 수 있습니다. .
supercat

1
사실, 연산 플래그를 잡고 더 레지스터가없는 대신 세 가지 지침이있다 그 도움 에뮬레이션의 일반적인 조건 분기 ( slt, slti, sltu).
Jarhmander

MIPS 명령어 세트를 살펴보면 이전 명령어가 실행될 때까지 각 명령어가 페치 될 것이라는 점을 감안할 때 직접 수행하지 않고 대신에 opcode를 갖는 것이 어려울 지 궁금합니다. 즉시 모드 명령어가 실행되고 다음 페치 된 명령어에 해당 비트 패턴이있는 경우 피연산자의 상위 16 비트가 프리 페치 된 명령어에서 가져 옵니까? 그것은 두 단어와 두 사이클을 소비하지 않고 두 단어 두 사이클 명령으로 처리되는 32 비트 즉시 모드 작업입니다 ...
supercat

... 피연산자를로드 한 다음 실제로 사용하려면 세 번째 사이클을로드하십시오.
supercat

7

대부분의 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 기능을 유지합니다.


1
모든 ARM32 CPU가 설명대로 작동하는 것은 아닙니다. 일부는 더 복잡한로드 명령어 및 / 또는 레지스터에 대한 쓰기 백 성능이 떨어집니다. 따라서 모두 정확히 같은 방식으로 디코딩 할 수는 없습니다.
Peter Cordes

6

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

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

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

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


1
로 구현하는 데 문제 MOV Rx,RyAND Rx,Ry,Ry있습니까?
supercat

3
@supercat 인코딩 할 수 mov RX, Imm없거나 mov RX, mem[RY]명령어 세트가 명령어 당 하나의 즉각적인 값과 하나의 메모리 액세스 만 지원하는 경우.
Dmitry Grigoryev

MIPS의 주소 지정 모드에 익숙하지 않습니다. ARM에는 [Rx + Ry << scale] 및 [Rx + disp] 모드가 있으며, 절대 주소에 후자를 사용할 수 있지만 어떤 경우에는 유용하지 않을 수 있습니다. 직선형 [Rx] 모드는 제로 변위를 사용하여 [Rx + disp]를 통해 에뮬레이션 할 수 있습니다. MIPS는 무엇을 사용합니까?
supercat

mov나쁜 예입니다. 0 레지스터 대신 즉시 0으로 구현할 수 있습니다. 예 ori dst, src, 0. 하지만 그래, 당신은 당신이하지 않은 경우 MOV-즉시 등록에 대한 오피 필요할 것 addiu $dst, $zero, 1234처럼, lui대신 위 (16)의 하위 16 비트에 대한 그리고 사용하지 수 nor또는 sub하나의 피연산자하지 / NEG를 구축을 .
Peter Cordes

@supercat : 여전히 궁금한 점이있는 경우 : 클래식 MIPS에는 단일 주소 지정 모드 만 있습니다 : register + disp16. 최신 MIPS는 FP로드 / 스토어의 2- 레지스터 주소 지정 모드를위한 다른 opcode를 추가하여 어레이 인덱싱 속도를 높였습니다. (그러나 여전히 정수로드 / 스토어에는 해당되지 않습니다. 아마도 2 개의 레지스터 레지스터 + 스토어의 데이터 레지스터에 대해 정수 레지스터 파일에서 더 많은 읽기 포트가 필요할 수 있기 때문입니다.
Peter Cordes

3

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

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

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


6
실제 비용은 R0몇 개의 전선을 접지하는 것이 아니라 레지스터를 다루는 모든 명령에서 코드를 예약해야한다는 사실입니다.
Dmitry Grigoryev

Xor는 붉은 청어입니다. xor-zeroing은 CPU가 관용구를 인식하고 입력에 대한 종속성을 피하는 x86에서만 유효합니다. 지적했듯이 Sandybridge 제품군은 등록 이름 변경 단계에서 처리하기 위해 uop을 실행하지 않습니다. ( x86 어셈블리에서 레지스터를 0으로 설정하는 가장 좋은 방법은 무엇입니까 : xor, mov 또는 and? ). 그러나 MIPS에서 XORing 레지스터는 잘못된 종속성을 갖습니다. 메모리 종속성 순서 규칙 (C ++과 동등한 HW std::memory_order_consume)은 종속성을 전파하기 위해 XOR이 필요합니다.
Peter Cordes 23 년

레지스터가 0이 아닌 경우 즉시 레지스터로 이동하는 opcode가 포함됩니다. 마찬가지로 lui당신은 여전히 하나 개의 명령어로 레지스터에 작은 숫자를 넣을 수 있지만이 (16)에 의해 이동 된 왼쪽. 잘못된 종속성으로 0 만 허용하는 것은 미쳤습니다. (정상 MIPS는 addiu $dst, $zero, 1234또는 로 0이 아닌 값을 생성 ori하므로 "전력 비용"인수가 분해됩니다. ALU 발생을 피하려면 소프트웨어 ADD 또는 OR 대신 mov-immediate에 등록 할 opcode를 포함 시키십시오
Peter Cordes
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.