32 비트 레지스터의 x86-64 명령어가 전체 64 비트 레지스터의 위쪽 부분을 0으로 만드는 이유는 무엇입니까?


119

에서 인텔 설명서의 x86-64에 투어 , 본인은

아마도 가장 놀라운 사실은 같은 명령어가 MOV EAX, EBX상위 32 비트 RAX레지스터 를 자동으로 제로화 한다는 것입니다 .

동일한 소스에서 인용 된 인텔 문서 (3.4.1.1 수동 기본 아키텍처의 64 비트 모드에서 범용 레지스터)는 다음과 같이 알려줍니다.

  • 64 비트 피연산자는 대상 범용 레지스터에서 64 비트 결과를 생성합니다.
  • 32 비트 피연산자는 대상 범용 레지스터에서 64 비트 결과로 0 확장 된 32 비트 결과를 생성합니다.
  • 8 비트 및 16 비트 피연산자는 8 비트 또는 16 비트 결과를 생성합니다. 대상 범용 레지스터의 상위 56 비트 또는 48 비트 (각각)는 연산에 의해 수정되지 않습니다. 8 비트 또는 16 비트 연산의 결과가 64 비트 주소 계산 용인 경우 레지스터를 전체 64 비트로 명시 적으로 부호 확장합니다.

x86-32 및 x86-64 어셈블리에서 다음과 같은 16 비트 명령어

mov ax, bx

eax의 상위 단어가 0이되는 이런 종류의 "이상한"동작을 표시하지 마십시오.

따라서이 행동이 도입 된 이유는 무엇입니까? 언뜻보기에는 비논리적으로 보입니다 (하지만 x86-32 어셈블리의 단점에 익숙하기 때문일 수 있습니다).


16
Google에서 "Partial register stall"을 검색한다면 그들이 (거의 확실하게) 피하려고했던 문제에 대한 많은 정보를 찾을 수있을 것입니다.
Jerry Coffin


4
"대부분"이 아닙니다. AFAIK, 대상 피연산자 가있는 모든 명령어 r32는 병합이 아니라 높은 32를 0으로합니다. 예를 들어 일부 어셈블러는 64 비트 대상 버전이 동일하게 동작하기 때문에 REX를 절약하면서로 대체 pmovmskb r64, xmm됩니다 pmovmskb r32, xmm. 매뉴얼Operation 섹션 에는 32 / 64bit dest 및 64 / 128 / 256b 소스의 6 가지 조합이 모두 별도로 나열되어 있지만 r32 형식의 암시 적 제로 확장은 r64 형식의 명시 적 제로 확장을 복제합니다. HW 구현에 대해 궁금합니다 ...
Peter Cordes 2016 년

2
@HansPassant, 순환 참조가 시작됩니다.
kchoi

답변:


98

나는 AMD가 아니거나 그들에게 말하고 있지는 않지만 같은 방식으로했을 것입니다. 상위 절반을 0으로 설정해도 이전 값에 대한 종속성이 생성되지 않으므로 CPU가 기다려야합니다. 레지스터 이름 바꾸기 가 그런 식으로 일을하지 않은 경우 메커니즘은 본질적으로 패배 할 것이다.

이렇게하면 항상 종속성을 명시 적으로 중단하지 않고도 64 비트 모드에서 32 비트 값을 사용하여 빠른 코드를 작성할 수 있습니다. 이 동작이 없으면 64 비트 모드의 모든 32 비트 명령어는 상위 부분이 거의 사용되지 않더라도 이전에 일어난 일을 기다려야합니다. ( int64 비트를 만들면 캐시 공간과 메모리 대역폭이 낭비됩니다. x86-64는 32 비트 및 64 비트 피연산자 크기를 가장 효율적으로 지원합니다. )

8 비트 및 16 비트 피연산자 크기의 동작은 이상합니다. 의존성 광기는 현재 16 비트 명령어를 피하는 이유 중 하나입니다. x86-64는 8 비트의 경우 8086, 16 비트의 경우 386에서이를 상속했으며 8 비트 및 16 비트 레지스터가 32 비트 모드에서와 동일한 방식으로 64 비트 모드에서 작동하도록 결정했습니다.


GCC가 부분 레지스터를 사용하지 않는 이유 도 참조하십시오 . 실제 CPU에서 8 비트 및 16 비트 부분 레지스터에 대한 쓰기 (및 전체 레지스터의 후속 읽기)를 처리하는 방법에 대한 실제 세부 정보를 참조하십시오.


8
나는 그것이 이상하다고 생각하지 않는다. 나는 그들이 너무 많이 깨고 싶지 않았고 거기에 오래된 행동을 유지하고 있다고 생각한다.
Alexey Frunze 2012-06-24

5
@Alex가 32 비트 모드를 도입했을 때 높은 부분에 대한 오래된 동작이 없었습니다. 이전에는 높은 부분이 없었습니다. 물론 그 후에는 더 이상 변경할 수 없습니다.
해롤드

1
나는 16 비트 피연산자에 대해 말하고 있었는데,이 경우 상위 비트가 0이되지 않는 이유입니다. 64 비트가 아닌 모드에는 없습니다. 그리고 그것은 64 비트 모드에서도 유지됩니다.
Alexey Frunze 2012-06-24

3
나는 "16 비트 명령어의 동작이 이상하다"고 해석했다. "64 비트 모드에서 16 비트 피연산자에서 제로 확장이 발생하지 않는다는 것이 이상하다". 따라서 더 나은 호환성을 위해 64 비트 모드에서 동일한 방식으로 유지하는 것에 대한 제 의견입니다.
Alexey Frunze 2012-06-24

8
@Alex 오, 알겠습니다. 확인. 그런 관점에서 이상하다고 생각하지 않습니다. "뒤돌아 보면, 그다지 좋은 생각이 아니었을지도 몰라"관점에서. 내가 더 명확해야 했어 :)
harold

9

단순히 명령어와 명령어 세트의 공간을 절약합니다. 기존 (32 비트) 명령어를 사용하여 작은 즉치 값을 64 비트 레지스터로 이동할 수 있습니다.

또한 재사용 MOV RAX, 42MOV EAX, 42수있는 경우에 대해 8 바이트 값을 인코딩하지 않아도됩니다 .

이 최적화는 8 비트 및 16 비트 작업에 대해 중요하지 않으며 (더 작기 때문에) 규칙을 변경하면 이전 코드도 손상됩니다.


7
그것이 맞다면 0 확장보다는 부호 확장이 더 합리적이지 않았을까요?
Damien_The_Unbeliever

16
부호 확장은 하드웨어에서도 느립니다. 제로 확장은 하반부를 생성하는 계산과 병렬로 수행 할 수 있지만 부호 확장은 하반부가 계산 될 때까지 수행 할 수 없습니다.
Jerry Coffin

13
또 다른 관련 트릭은 REX 접두사가 필요 하기 XOR EAX, EAX때문에 사용하는 XOR RAX, RAX것입니다.
Neil

3
@Nubok : 물론, 그들은 즉각적인 인수를 취하는 movzx / movsx의 인코딩을 추가했을 수 있습니다. 대부분의 경우 상위 비트를 0 으로 설정하는 것이 편리하므로 값을 배열 인덱스로 사용할 수 있습니다 (모든 reg가 유효 주소에서 동일한 크기 여야하기 때문에 [rsi + edx]허용되지 않음). 물론 잘못된 종속성 / 부분 등록 중단 (다른 답변)을 피하는 것이 또 다른 주요 이유입니다.
Peter Cordes

4
규칙을 변경하면 오래된 코드도 깨집니다. 어쨌든 이전 코드는 64 비트 모드에서 실행할 수 없습니다 (예 : 1 바이트 inc / dec는 REX 접두사입니다). 이것은 관련이 없습니다. x86의 사마귀를 정리 하지 않는 이유 는 long 모드와 compat / legacy 모드 간의 차이가 적기 때문에 모드에 따라 다르게 디코딩해야하는 명령어가 적습니다. AMD는 AMD64가 따라 잡을 것이라는 사실을 몰랐고, 안타깝게도 매우 보수적이어서 지원하는 데 더 적은 트랜지스터가 필요했습니다. 장기적으로는 컴파일러와 인간이 64 비트 모드에서 어떤 것이 다르게 작동하는지 기억해야한다면 괜찮 았을 것입니다.
Peter Cordes 2019

1

0이 64 비트로 확장되지 않으면에서 읽는 명령어 raxrax피연산자 (에 쓰는 eax명령어와 그 rax앞에 쓰는 명령어)에 대해 2 개의 종속성을 갖게됩니다. 즉, 1) ROB에 대한 항목이 있어야합니다. 단일 피연산자에 대한 다중 종속성, 이는 ROB가 더 많은 로직과 트랜지스터를 필요로하고 더 많은 공간을 차지하며 실행하는 데 오래 걸릴 수있는 불필요한 두 번째 종속성을 기다리면 실행 속도가 느려짐을 의미합니다. 또는 2), 16 비트 명령에서 발생한다고 생각하는 것은 할당 단계가 아마도 중단 될 것입니다 (즉, RAT에 ax쓰기에 대한 활성 할당이 있고 eax읽기가 나타나면 ax쓰기가 중단 될 때까지 중단됨).

mov rdx, 1
mov rax, 6
imul rax, rdx
mov rbx, rax
mov eax, 7 //retires before add rax, 6
mov rdx, rax // has to wait for both imul rax, rdx and mov eax, 7 to finish before dispatch to the execution units, even though the higher order bits are identical anyway

제로 확장이 아닌 경우의 유일한 이점은 더 높은 순서의 비트 rax가 포함 되도록하는 것 입니다. 예를 들어 원래 0xffffffffffffffff를 포함하는 경우 결과는 0xffffffff00000007이되지만 ISA가 이러한 비용으로이 보장을 수행 할 이유가 거의 없습니다. 제로 확장의 이점이 실제로 더 많이 필요할 가능성이 높으므로 추가 코드 줄이 절약 mov rax, 0됩니다. 항상 제로가 64 비트로 확장 될 것입니다 보장함으로써, 컴파일러에있는 동안 마음이 공리와 함께 작업 할 수 있습니다 mov rdx, rax, rax단지 그것을 실행 유닛을 확보, 빠른 실행을 시작하고 은퇴 할 수 있음을 의미, 그 하나의 종속 기다려야합니다. 또한 REX 바이트 없이도 xor eax, eax제로화 와 같은보다 효율적인 제로 관용구를 허용합니다 rax.


Skylake의 부분 플래그는 적어도 CF 대 SPAZO에 대한 별도의 입력을 통해 작동합니다. (그래서 cmovbe2 uops이지만 cmovb1입니다). 그러나 부분 등록 이름 변경을 수행하는 CPU는 사용자가 제안한 방식으로 수행하지 않습니다. 대신 부분 reg가 전체 reg와 별도로 이름이 변경되면 병합 uop을 삽입합니다 (즉, "dirty"). GCC가 부분 레지스터를 사용하지 않는 이유는 무엇입니까?를 참조하십시오 . 그리고 정확히 어떻게 하 스웰 / 스카이 레이크에 부분 레지스터 수행합니까? AL을 작성하는 것은 RAX에 대한 잘못된 의존성을 가지고있는 것으로 보이며 AH는 일관성이 없습니다
Peter Cordes

P6 제품군 CPU는 병합 uop (Core2 / Nehalem)을 삽입하기 위해 ~ 3 사이클 동안 멈췄거나 이전 P6 제품군 (PM, PIII, PII, PPro)은 (적어도?) ~ 6 사이클 동안 멈 춥니 다. 아마도 그것은 영구 / 아키텍처 레지스터 파일에 대한 쓰기 저장을 통해 전체 reg 값을 사용할 수 있기를 기다리는 2에서 제안한 것과 같을 것입니다.
Peter Cordes

@PeterCordes 오, 적어도 부분적인 플래그 스톨에 대해 uop을 병합하는 것에 대해 알고 있습니다. 말이 되긴했지만 어떻게 작동하는지 잊었다. 한 번 클릭했지만 메모 작성을 잊었습니다
Lewis Kelsey

@PeterCordes microarchitecture.pdf : This gives a delay of 5 - 6 clocks. The reason is that a temporary register has been assigned to AL to make it independent of AH. The execution unit has to wait until the write to AL has retired before it is possible to combine the value from AL with the value of the rest of EAX이 문제를 해결하는 데 사용되는 '병합 uop'의 예를 찾을 수 없습니다. 부분적인 플래그 실속의 경우와 동일합니다
Lewis Kelsey

맞습니다, 초기 P6은 쓰기 저장까지 중단됩니다. Core2와 Nehalem은 전후에 병합 uop을 삽입합니까? 짧은 시간 동안 만 프런트 엔드를 지연시킵니다. Sandybridge는 중단없이 병합 uop를 삽입합니다. (그러나 AH 병합은 자체적으로 주기적으로 발행해야하는 반면 AL 병합은 전체 그룹의 일부일 수 있습니다.) Haswell / SKL은 RAX와 별도로 AL의 이름을 전혀 바꾸지 않으므로 mov al, [mem]마이크로 퓨즈로드 + ALU- 병합, AH 이름 만 변경하고 AH 병합 uop은 여전히 ​​단독으로 문제가됩니다. 이러한 CPU의 부분 플래그 병합 메커니즘은 다양합니다. 예를 들어 Core2 / Nehalem은 partial-reg와 달리 부분 플래그에 대해 여전히 멈 춥니 다.
Peter Cordes
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.