레지스터를 자기 자신과 XOR 하는 것이 0으로 만드는 관용구입니다. 왜 sub를 쓰지 않나요?
Source: Hacker News
왜 xor eax, eax가 레지스터를 0으로 만드는 데 사용되는가
Matt Godbolt, Compiler Explorer로 가장 잘 알려진 그는 왜 x86 컴파일러가 xor eax, eax 명령을 좋아하는가라는 짧은 글을 썼다.
이 명령은 x86에서 레지스터를 0으로 만드는 가장 짧은 방법이다. 네 바이트 즉시값을 인코딩해야 하는 mov eax, 0보다 몇 바이트 짧다. x86 아키텍처에는 전용 제로 레지스터가 없기 때문에 레지스터를 처음부터 클리어해야 한다.
sub eax, eax와의 비교
xor eax, eax와 sub eax, eax는 바이트 수가 동일하고 최신 CPU에서 레이턴시도 비슷하지만 플래그에 미치는 영향이 다르다:
| 플래그 | xor eax, eax | sub eax, eax |
|---|---|---|
| OF | 클리어됨 | 클리어됨 |
| SF | 클리어됨 | 클리어됨 |
| ZF | 설정됨 | 설정됨 |
| AF | 정의되지 않음 | 클리어됨 |
| PF | 설정됨 | 설정됨 |
| CF | 클리어됨 | 클리어됨 |
sub eax, eax는 AF 플래그를 클리어하지만, xor eax, eax는 AF 플래그를 정의하지 않는다. 이 사소한 차이를 제외하면 두 명령은 레지스터를 0으로 만드는 목적에 있어 비슷하게 동작한다.
역사적·마이크로아키텍처적 이유
레지스터를 0으로 만들기 위해 xor를 선택한 초기 컴파일러들은 눈덩이 효과를 일으켰다: 개발자들이 생성된 코드에서 이 패턴을 보고 최적의 선택이라고 가정하면서 사용이 강화되었다.
인텔은 이후 디코더 프론트엔드에서 xor r, r 패턴(및 sub r, r 패턴) 을 특별히 감지하도록 추가했으며, 목적 레지스터를 내부 제로 레지스터로 바꾸고 실행 단계를 우회한다. 이로 인해 해당 명령은 사실상 제로 레이턴시가 되며, 입력값에 관계없이 결과가 0이라는 것이 알려져 있기 때문에 의존성 체인도 깨진다.
두 패턴 모두 이런 특수 처리를 받지만, 일부 CPU 제조사는 xor r, r만 최적화했을 수도 있다. Stack Overflow의 토론이 이 문제를 강조한다: 레지스터를 0으로 만드는 방법은 몇 가지인가?. xor가 보편적으로 최적화된다는 인식이 실제 사용에서 우위를 점한다.
부가적인 이야기
- 내 이전 동료 중 한 명인 Jeff Par 은 레지스터를 0으로 만들 때
sub r, r를 선호했다. 어셈블리를 볼 때sub가 사용된 것을 보면 그의 코드임을 알 수 있었다. xor트릭은 Itanium에서는 동작하지 않는다. 왜냐하면 수학 연산이 NaT 비트를 리셋하지 않기 때문이다(Old New Thing 블로그). 다행히 Itanium은 전용 제로 레지스터를 제공하므로(Old New Thing 블로그) 이 트릭이 필요하지 않다.