어셈블러에 대해 읽을 때 나는 종종 프로세서의 특정 레지스터를 푸시 하고 나중에 이전 상태로 복원하기 위해 다시 팝 한다고 쓰는 사람들을 만납니다 .
- 레지스터를 어떻게 밀 수 있습니까? 어디로 밀려나? 왜 이것이 필요한가요?
- 이것이 단일 프로세서 명령으로 귀결됩니까 아니면 더 복잡합니까?
어셈블러에 대해 읽을 때 나는 종종 프로세서의 특정 레지스터를 푸시 하고 나중에 이전 상태로 복원하기 위해 다시 팝 한다고 쓰는 사람들을 만납니다 .
%eax
항상 32 비트 크기이므로 귀하가 제공 한 예의 경우입니다 .
답변:
값을 푸시 하는 것은 (레지스터에 반드시 저장되지는 않음) 스택에 쓰는 것을 의미합니다.
팝핑 은 스택의 맨 위에있는 모든 것을 레지스터 로 복원하는 것을 의미 합니다. 다음은 기본 지침입니다.
push 0xdeadbeef ; push a value to the stack
pop eax ; eax is now 0xdeadbeef
; swap contents of registers
push eax
mov eax, ebx
pop ebx
r/m
등록뿐 아니라이므로 push dword [esi]
. 또는 pop dword [esp]
동일한 값을로드 한 다음 동일한 주소에 다시 저장할 수도 있습니다. ( github.com/HJLebbink/asm-dude/wiki/POP ). 나는 당신이 "반드시 레지스터는 아니다"라고 말했기 때문에 이것을 언급 할뿐입니다.
pop
메모리 영역으로 도 들어갈 수 있습니다 .pop [0xdeadbeef]
레지스터를 푸시하는 방법은 다음과 같습니다. x86에 대해 이야기하고 있다고 가정합니다.
push ebx
push eax
스택에 푸시됩니다. 의 가치ESP
레지스터 x86 시스템에서 스택이 아래로 커짐에 따라 푸시 된 값의 크기로 감소합니다.
가치를 보존 할 필요가 있습니다. 일반적인 사용법은
push eax ; preserve the value of eax
call some_method ; some method is called which will put return value in eax
mov edx, eax ; move the return value to edx
pop eax ; restore original eax
A push
는 x86의 단일 명령어로 내부적으로 두 가지 작업을 수행합니다.
ESP
푸시 값의 크기에 따라 레지스터.ESP
레지스터의 현재 주소에 저장합니다 .어디로 밀려나?
esp - 4
. 더 정확하게:
esp
4를 뺀다esp
pop
이것을 뒤집습니다.
System V ABI는 Linux에 rsp
프로그램이 실행될 때 적절한 스택 위치를 가리 키도록 합니다. 프로그램이 시작될 때 기본 등록 상태 (asm, linux)는 무엇입니까? 일반적으로 사용해야하는 것입니다.
레지스터를 어떻게 밀 수 있습니까?
최소 GNU GAS 예 :
.data
/* .long takes 4 bytes each. */
val1:
/* Store bytes 0x 01 00 00 00 here. */
.long 1
val2:
/* 0x 02 00 00 00 */
.long 2
.text
/* Make esp point to the address of val2.
* Unusual, but totally possible. */
mov $val2, %esp
/* eax = 3 */
mov $3, %ea
push %eax
/*
Outcome:
- esp == val1
- val1 == 3
esp was changed to point to val1,
and then val1 was modified.
*/
pop %ebx
/*
Outcome:
- esp == &val2
- ebx == 3
Inverses push: ebx gets the value of val1 (first)
and then esp is increased back to point to val2.
*/
위 GitHub에서 실행 가능한 어설 션이 있습니다.
왜 이것이 필요한가요?
이러한 명령은를 통해 쉽게 구현할 수 있다는 것은 사실입니다 mov
.add
하고 sub
.
그들이 존재하는 이유는 이러한 명령 조합이 너무 자주 발생하여 인텔이 우리에게 제공하기로 결정했기 때문입니다.
이러한 조합이 자주 발생하는 이유는 레지스터 값을 일시적으로 메모리에 저장하고 복원하여 덮어 쓰지 않기 때문입니다.
문제를 이해하려면 C 코드를 직접 컴파일 해보십시오.
가장 큰 어려움은 각 변수를 저장할 위치를 결정하는 것입니다.
이상적으로는 모든 변수가 레지스터에 들어가는데 , 이는 액세스하기 가장 빠른 메모리입니다 (현재 RAM보다 약 100 배 빠름 ).
그러나 물론, 특히 중첩 함수의 인수에 대해 레지스터보다 더 많은 변수를 쉽게 가질 수 있으므로 유일한 해결책은 메모리에 쓰는 것입니다.
모든 메모리 주소에 쓸 수 있지만 함수 호출 및 반환의 지역 변수와 인수가 멋진 스택 패턴에 맞기 때문에 메모리 조각화 를 방지 합니다. 때문에이를 처리하는 가장 좋은 방법입니다. 힙 할당자를 작성하는 광기와 비교하십시오.
그런 다음 컴파일러가 NP 완료이고 컴파일러 작성에서 가장 어려운 부분 중 하나이기 때문에 레지스터 할당을 최적화하도록합니다. 이 문제를 레지스터 할당 이라고 하며 그래프 색상 과 동형 입니다.
컴파일러의 할당자가 레지스터 대신 메모리에 항목을 저장하도록 강제하는 경우이를 spill이라고 합니다.
이것이 단일 프로세서 명령으로 요약됩니까 아니면 더 복잡합니까?
우리가 확실히 아는 것은 인텔이 a push
와 pop
명령을 문서화 한다는 것입니다. 따라서 그것들은 그런 의미에서 하나의 명령입니다.
내부적으로는 여러 마이크로 코드로 확장 할 수 있습니다. 하나는 수정 esp
하고 다른 하나는 메모리 IO를 수행하며 여러주기를 필요로합니다.
그러나 push
더 구체적이기 때문에 단일 명령어가 다른 명령어의 동등한 조합보다 빠를 수도 있습니다 .
이것은 대부분 문서화되지 않았습니다.
push
하고 pop
하나 개의 마이크로 동작을. push
/ pop
마이크로 연산으로 디코드. 성능 카운터 덕분에 실험적 테스트가 가능하며 Agner Fog는이를 수행하고 지침 표를 게시했습니다 . 펜티엄 M 나중에 CPU는 단일 UOP가 push
/ pop
스택 엔진 덕분에 (참조 Agner의 microarch의 PDF를). 여기에는 Intel / AMD 특허 공유 계약 덕분에 최신 AMD CPU가 포함됩니다.
mov
로드 포함)입니다. 유출 된 상수가 아닌 변수의 경우 저장 전달 왕복은 많은 추가 지연 시간입니다 (직접 전달에 비해 추가 ~ 5c이며 저장 명령은 저렴하지 않습니다).
ocperf.py
래퍼 스크립트를 사용 하여 카운터에 대한 쉬운 기호 이름을 얻습니다.
푸시 및 팝 레지스터는 다음과 같은 배후에 있습니다.
push reg <= same as => sub $8,%rsp # subtract 8 from rsp
mov reg,(%rsp) # store, using rsp as the address
pop reg <= same as=> mov (%rsp),reg # load, using rsp as the address
add $8,%rsp # add 8 to the rsp
이것은 x86-64 At & t 구문입니다.
쌍으로 사용하면 레지스터를 스택에 저장하고 나중에 복원 할 수 있습니다. 다른 용도도 있습니다.
lea rsp, [rsp±8]
대신 사용 하는 것이 좋습니다 . add
sub
push
pop
거의 모든 CPU가 스택을 사용합니다. 프로그램 스택은 관리를 지원하는 하드웨어 가있는 LIFO 기술입니다.
스택은 CPU 메모리 힙의 맨 위에 일반적으로 할당되고 반대 방향으로 증가 (PUSH 명령에서 스택 포인터가 감소 함)되는 프로그램 (RAM) 메모리의 양입니다. 스택에 삽입하는 표준 용어는 PUSH 이고 스택에서 제거 하기위한 표준 용어 는 POP 입니다.
스택은 스택 포인터라고도하는 스택 의도 CPU 레지스터를 통해 관리되므로 CPU가 POP 또는 PUSH를 수행 할 때 스택 포인터는 레지스터 또는 상수를 스택 메모리에로드 / 저장하고 스택 포인터는 푸시 된 단어 수에 따라 자동 감소 x 또는 증가합니다. 또는 스택에서 (에서) 팝되었습니다.
어셈블러 명령어를 통해 스택에 저장할 수 있습니다.
b
,w
,l
, 또는q
상기 메모리의 크기를 나타내는 조작 될 수있다. 예 :pushl %eax
andpopl %eax