EBP 프레임 포인터 레지스터의 용도는 무엇입니까?


94

저는 어셈블리 언어의 초보자이며 컴파일러가 내 보낸 x86 코드가 일반적으로 EBP레지스터를 다른 용도로 사용할 수있을 때 릴리스 / 최적화 모드에서도 프레임 포인터를 유지한다는 것을 알았 습니다.

프레임 포인터가 코드를 디버그하기 쉽게 만드는 이유를 이해 alloca()하고 함수 내에서 호출되는 경우 필요할 수 있습니다 . 그러나 x86에는 레지스터가 매우 적으며 그중 하나가 충분할 때 스택 프레임의 위치를 ​​유지하기 위해 두 개를 사용하는 것은 나에게 의미가 없습니다. 최적화 / 릴리스 빌드에서도 프레임 포인터를 생략하는 것이 나쁜 생각으로 간주되는 이유는 무엇입니까?


19
x86에 레지스터가 거의
없다고


1
C99 VLA도 이점을 누릴 수 있습니다.
치로 틸리는郝海东冠状病六四事件法轮功


1
프레임 포인터로 인해 스택 포인터가 중복되지 않습니까? . TL; DR : 1. 사소하지 않은 스택 정렬 2. 스택 할당 ( alloca) 3. 런타임 구현 용이성 : 예외 처리, 샌드 박스, GC
Alexander Malakhov

답변:


102

프레임 포인터는 디버거가 단일 상수 오프셋으로 지역 변수 또는 인수가있는 위치를 알 수 있도록하는 참조 포인터입니다. ESP의 값은 실행 과정에서 변경되지만 EBP는 동일하게 유지되어 동일한 오프셋에서 동일한 변수에 도달 할 수 있습니다 (예 : 첫 번째 매개 변수는 항상 EBP + 8에있는 반면 ESP 오프셋은 사용자가 밀기 때문에 크게 변경 될 수 있음). / 팝핑 것들)

컴파일러가 프레임 포인터를 버리지 않는 이유는 무엇입니까? 프레임 포인터를 사용하면 디버거는 지역 변수와 인수가 EBP에 대한 상수 오프셋에 있도록 보장되기 때문에 기호 테이블을 사용하는 위치를 파악할 수 있습니다. 그렇지 않으면 코드의 어느 지점에서나 지역 변수가있는 위치를 쉽게 파악할 수 없습니다.

Greg가 언급했듯이 EBP는 스택 프레임의 역 연결 목록을 제공하므로 디버거가 함수의 스택 프레임 (로컬 변수 + 인수)의 크기를 파악할 수 있으므로 디버거의 스택 해제에도 도움이됩니다.

대부분의 컴파일러는 디버깅을 정말 어렵게 만들지 만 프레임 포인터를 생략하는 옵션을 제공합니다. 이 옵션은 릴리스 코드에서도 전역 적으로 사용해서는 안됩니다. 사용자의 충돌을 언제 디버깅해야하는지 알 수 없습니다.


10
컴파일러는 아마도 ESP에 대해 무엇을하는지 알고있을 것입니다. 다른 포인트는 유효하지만 +1
erikkallen

8
최신 디버거는 .NET Framework로 컴파일 된 코드에서도 스택 역 추적을 수행 할 수 있습니다 -fomit-frame-pointer. 이 설정은 최근 gcc의 기본값입니다.
Peter Cordes

2
@SedatKapanoglu : 데이터 섹션은 필요한 정보를 기록 yosefk.com/blog/...는
피터 코르

3
@SedatKapanoglu :이 .eh_frame_hdr섹션은 런타임 예외에도 사용됩니다. 당신은 (와를 찾을 수 있습니다 objdump -h그것은을 위해 16K에 관하여, 리눅스 시스템에서 대부분의 바이너리) /bin/bash대, GNU에 대한 572B /bin/true를 들어, 108K ffmpeg. 생성을 비활성화하는 gcc 옵션이 있지만 strip기본적으로 제거되는 디버그 섹션이 아니라 "일반"데이터 섹션 입니다. 그렇지 않으면 디버그 기호가없는 라이브러리 함수를 통해 역 추적 할 수 없습니다. 이 섹션은 push/mov/pop대체 하는 명령어 보다 클 수 있지만 런타임 비용이 거의 없습니다 (예 : uop 캐시).
피터 코르

3
"예를 들어 첫 번째 매개 변수는 항상 EBP-4에 있습니다"에 관하여 : EBP + 8 (x86에서)의 첫 번째 매개 변수가 아닙니까?
Aydin K.

31

이미 좋은 답변에 2 센트 만 더하면됩니다.

스택 프레임 체인을 갖는 것은 좋은 언어 아키텍처의 일부입니다. BP는 서브 루틴 로컬 변수가 저장된 현재 프레임을 가리 킵니다. (로컬은 음의 오프셋에 있고 인수는 양의 오프셋에 있습니다.)

완벽하게 좋은 레지스터가 최적화에 사용되는 것을 막는다는 생각은 다음과 같은 질문을 제기합니다. 최적화는 실제로 언제 어디서 가치가 있는가?

최적화는 1) 함수를 호출하지 않는, 2) 프로그램 카운터가 시간의 상당 부분을 소비하는 경우, 3) 컴파일러가 실제로 볼 수있는 코드 (즉, 라이브러리가 아닌 함수) 인 타이트 루프에서만 가치가 있습니다. 이는 일반적으로 특히 대규모 시스템에서 전체 코드의 매우 작은 부분입니다.

사이클을 없애기 위해 다른 코드를 비틀고 압착 할 수 있으며, 프로그램 카운터가 실제로 존재하지 않기 때문에 문제가되지 않습니다.

나는 당신이 이것을 묻지 않았다는 것을 알고 있지만, 내 경험상 99 %의 성능 문제는 컴파일러 최적화와 전혀 관련이 없습니다. 그들은 과도한 디자인과 관련이 있습니다.


@Mike에게 감사합니다. 귀하의 답변이 매우 유용하다는 것을 알았습니다.
sixtyfootersdude 2010 년

2
프레임 포인터를 제거하면 함수 호출마다 몇 가지 명령을 절약 할 수 있습니다. 이는 자체적으로 작은 최적화입니다. BTW, "begs the question"의 사용이 올바르지 않습니다. "질문을 제기한다"는 뜻입니다.
augurar

@augurar : 고정. 감사. 나는 :) 문법 약간의 자신을 툴툴 해요
마이크 Dunlavey

3
@augurar 언어는 진화합니다 : "질문을 구걸합니다"는 이제 "질문 제기"를 의미합니다. 구식 사용에 대한 규범 주의자 nitpicker는 아무것도 추가하지 않습니다.
user3364825

9

확실히 컴파일러에 따라 다릅니다. EBP 레지스터를 범용 레지스터로 자유롭게 사용하는 x86 컴파일러에서 내 보낸 최적화 된 코드를 보았습니다. (하지만 어떤 컴파일러를 사용했는지 기억하지 못합니다.)

컴파일러는 예외 처리 중에 스택 해제를 지원하기 위해 EBP 레지스터를 유지하도록 선택할 수도 있지만 이는 정확한 컴파일러 구현에 따라 달라집니다.


대부분의 컴파일러 -fomit-frame-pointer는 최적화가 활성화 된 경우로 기본 설정됩니다. (ABI가 허용하는 경우). GCC, clang, ICC 및 MSVC는 모두 32 비트 Windows를 대상으로하는 경우에도 IIRC를 수행합니다. 그래, 스택에서 매개 변수를 찾기 위해 esp 레지스터보다 ebp를 사용하는 것이 더 나은 이유는 무엇입니까? 32 비트 Windows에서도 프레임 포인터를 생략 할 수 있음을 보여줍니다. 32 비트 x86 Linux는 확실히 할 수 있고 할 수 있습니다. 물론 64 비트 ABI는 처음부터 프레임 포인터 누락을 허용했습니다.
Peter Cordes

4

그러나 x86에는 레지스터가 거의 없습니다.

이것은 opcode가 8 개의 레지스터에만 주소를 지정할 수 있다는 점에서만 사실입니다. 프로세서 자체에는 실제로 그보다 더 많은 레지스터가 있으며 레지스터 이름 변경, 파이프 라이닝, 예측 실행 및 기타 프로세서 용어를 사용하여 해당 제한을 극복합니다. Wikipedia에는 ​​x86 프로세서가 레지스터 제한을 극복하기 위해 수행 할 수있는 작업에 대한 좋은 소개 단락이 있습니다. http://en.wikipedia.org/wiki/X86#Current_implementations .


1
원래 질문은 생성 된 코드에 관한 것으로 opcode에서 참조 할 수있는 레지스터로 엄격하게 제한됩니다.
Darron

1
예,하지만 이것이 최적화 된 빌드에서 프레임 포인터를 생략하는 것이 오늘날 그렇게 중요하지 않은 이유입니다.
Michael

1
레지스터 이름 변경은 실제로 더 많은 수의 레지스터를 사용할 수있는 것과는 다릅니다. 레지스터 이름 변경이 도움이되지 않는 상황이 여전히 많이 있지만 더 "일반적인"레지스터는 도움이됩니다.
jalf

1

스택 프레임을 사용하는 것은 원격으로 현대적인 하드웨어에서도 엄청나게 저렴합니다. 저렴한 스택 프레임이있는 경우 레지스터 몇 개를 저장하는 것이 중요하지 않습니다. 빠른 스택 프레임 대 더 많은 레지스터는 엔지니어링 트레이드 오프였으며 빠른 스택 프레임이 이겼습니다.

순수 레지스터를 통해 얼마나 절약하고 있습니까? 그만한 가치가 있습니까?


더 많은 레지스터는 명령어 인코딩에 의해 제한됩니다. x86-64는 REX 접두사 바이트의 비트를 사용하여 src 및 dest 레지스터에 대한 명령어의 레지스터 지정 부분을 3 비트에서 4 비트로 확장합니다. 공간이 있다면 x86-64는 아마도 32 개의 아키텍처 레지스터로 갔을 것입니다.하지만 컨텍스트 스위치에서 많은 것을 저장 / 복원하는 것이 합산되기 시작합니다. 15는 7에서 크게 향상되었지만 31은 대부분의 경우 훨씬 작은 개선입니다. (스택 포인터를 범용으로 계산하지 않습니다.) 푸시 / 팝을 빠르게 만드는 것은 스택 프레임 이상에 좋습니다. 그래도 reg 수와의 절충안은 아닙니다.
Peter Cordes
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.