gcc 옵션 -fomit-frame-pointer 이해하기


80

Google에 gcc옵션 의 의미를 알려달라고 요청했으며 -fomit-frame-pointer,이 경우 아래 설명으로 리디렉션됩니다.

-fomit-frame-pointer

프레임 포인터가 필요하지 않은 함수에 대해서는 레지스터에 프레임 포인터를 유지하지 마십시오. 이렇게하면 프레임 포인터를 저장, 설정 및 복원하는 지침이 필요하지 않습니다. 또한 많은 기능에서 추가 레지스터를 사용할 수 있습니다. 또한 일부 컴퓨터에서는 디버깅이 불가능합니다.

각 기능에 대한 지식에 따라 모든 로컬 변수와 더 많은 정보를 유지하기 위해 프로세스 메모리 스택에 활성화 레코드가 생성됩니다. 이 프레임 포인터가 함수의 활성화 레코드 주소를 의미하기를 바랍니다.

이 경우, 레지스터에 프레임 포인터를 유지할 필요가없는 함수 유형은 무엇입니까? 이 정보를 얻으면 프레임 포인터가 레지스터에 유지되지 않으면 일부 명령어가 이진으로 생략되기 때문에 가능한 경우이를 기반으로 새 함수를 설계하려고합니다. 이것은 많은 기능이있는 애플리케이션에서 성능을 현저하게 향상시킬 것입니다.


5
이 옵션으로 컴파일 된 코드의 크래시 덤프를 하나만 디버깅하면 makefile에서이 옵션을 추출 할 수 있습니다. 명령 btw를 제거하지 않고 옵티 마이저에 저장을 위해 사용할 레지스터를 하나 더 제공합니다.
Hans Passant 2013

1
@HansPassant 실제로 릴리스 빌드에 매우 유용합니다. 메이크 두 목표를 갖는 - Release그리고 Debug실제로 매우 유용합니다, 예를 들어이 옵션을.
Kotauskas

3
@VladislavToncharov -build를 실행하는 고객의 크래시 덤프를 디버깅 할 필요가 없었던 것 Release같습니다.
Andreas Magnusson

답변:


60

대부분의 작은 함수에는 프레임 포인터가 필요하지 않습니다. 큰 함수에는 프레임 포인터가 필요할 수 있습니다.

실제로 컴파일러가 스택이 사용되는 방식과 스택의 위치 (로컬 변수, 현재 함수에 전달 된 인수 및 호출 될 함수를 준비중인 인수)를 추적하는 데 얼마나 잘 관리되는지에 대한 것입니다. 프레임 포인터가 필요하거나 필요하지 않은 함수를 특성화하는 것은 쉽지 않다고 생각합니다 (기술적으로 프레임 포인터를 갖는 함수는 없습니다. 컴파일러가 복잡성을 줄이기 위해 필요하다고 판단하는 경우 기타 코드 ").

코딩 전략의 일부로 "함수에 프레임 포인터가 없도록 만들려고 시도"해서는 안된다고 생각합니다. 제가 말했듯이 간단한 함수에는 필요하지 않으므로을 사용 -fomit-frame-pointer하면 하나 이상의 레지스터를 사용할 수 있습니다. 레지스터 할당 자에 대해, 기능에 대한 진입 / 종료에 대한 1-3 명령을 저장합니다. 함수에 프레임 포인터가 필요한 경우 컴파일러가 프레임 포인터를 사용하지 않는 것보다 더 나은 옵션이라고 결정하기 때문입니다. 프레임 포인터가없는 함수를 갖는 것이 목표가 아니라 정확하고 빠르게 작동하는 코드를 갖는 것이 목표입니다.

"프레임 포인터가 없음"은 더 나은 성능을 제공해야하지만 엄청난 개선을 제공하는 마법의 총알은 아닙니다. 특히 처음 시작할 레지스터가 16 개있는 x86-64에서는 그렇지 않습니다. 32 비트 x86에서는 레지스터가 8 개 뿐이며 그중 하나는 스택 포인터이고 다른 하나를 프레임 포인터로 차지한다는 것은 레지스터 공간의 25 %를 차지한다는 것을 의미합니다. 이를 12.5 %로 변경하는 것은 상당히 개선 된 것입니다. 물론 64 비트 용으로 컴파일하는 것도 많은 도움이 될 것입니다.


24
일반적으로 컴파일러는 자체적으로 스택 깊이를 추적 할 수 있으며 프레임 포인터가 필요하지 않습니다. 예외는 함수가 alloca스택 포인터를 가변 량만큼 이동시키는 것을 사용 하는 경우입니다. 프레임 포인터 누락은 디버깅을 상당히 어렵게 만듭니다. 지역 변수는 찾기가 더 어렵고 스택 추적은 도움이되는 프레임 포인터없이 재구성하기가 훨씬 더 어렵습니다. 또한 매개 변수에 액세스하는 것은 스택의 최상위에서 멀리 떨어져 있고 더 비싼 주소 지정 모드가 필요할 수 있기 때문에 더 비쌀 수 있습니다.
Raymond Chen

3
예, 우리가 alloca[누가 사용 합니까? -저는 alloca] 또는 variable size local arrays[현대적인 형태의 alloca] 를 사용하는 코드를 작성한 적이 없다고 99 % 확신합니다 . 그러면 컴파일러는 프레임 포인터를 사용하는 것이 더 나은 옵션이라고 결정할 수 있습니다. 컴파일러는 맹목적으로 다음을 따르지 않도록 작성 되었기 때문입니다. 제공되는 옵션이지만 최상의 선택을 제공합니다.
Mats Petersson 2013

6
@MatsPetersson VLA는 다음과 다릅니다 alloca. 선언 된 범위를 벗어나면 바로 버려지지만 alloca함수를 떠날 때만 공간이 해제됩니다. 이것은 VLA가.보다 훨씬 쉽게 따라갈 수 있도록합니다 alloca.
Jens Gustedt 2013

35
gcc가 -fomit-frame-pointerx86-64에 대해 기본적으로 사용 된다는 점을 언급 할 가치가 있습니다.
zwol 2013

5
@JensGustedt, 문제는 버려 질 때가 아니라 컴파일 타임에 크기 ( alloca'ed space)를 알 수 없다는 것 입니다. 일반적으로 컴파일러는 프레임 포인터를 사용하여 로컬 변수의 주소를 얻습니다. 스택 프레임의 크기가 변경되지 않으면 스택 포인터에서 고정 된 오프셋에서 찾을 수 있습니다.
vonbrand 2013

15

이것은 인텔 플랫폼의 BP / EBP / RBP 레지스터에 관한 것입니다. 이 레지스터는 스택 세그먼트로 기본 설정됩니다 (스택 세그먼트에 액세스하기 위해 특별한 접두사가 필요하지 않음).

EBP는 스택 내에서 데이터 구조, 변수 및 동적으로 할당 된 작업 공간에 액세스하기위한 최상의 레지스터 선택입니다. EBP는 현재 TOS가 아닌 스택의 고정 지점을 기준으로 스택의 요소에 액세스하는 데 자주 사용됩니다. 일반적으로 현재 프로 시저에 대해 설정된 현재 스택 프레임의 기본 주소를 식별합니다. EBP가 오프셋 계산에서 기준 레지스터로 사용되는 경우 오프셋은 현재 스택 세그먼트 (즉, SS가 현재 선택한 세그먼트)에서 자동으로 계산됩니다. SS를 명시 적으로 지정할 필요가 없기 때문에 이러한 경우 명령어 인코딩이 더 효율적입니다. EBP는 다른 세그먼트 레지스터를 통해 주소를 지정할 수있는 세그먼트로 인덱싱하는 데 사용할 수도 있습니다.

(출처 -http : //css.csail.mit.edu/6.858/2017/readings/i386/s02_03.htm )

대부분의 32 비트 플랫폼에서 데이터 세그먼트와 스택 세그먼트가 동일하기 때문에 EBP / RBP와 스택의 연관은 더 이상 문제가되지 않습니다. 64 비트 플랫폼에서도 마찬가지입니다. AMD가 2003 년에 도입 한 x86-64 아키텍처는 64 비트 모드에서 세분화 지원을 크게 중단했습니다. 세그먼트 레지스터 중 4 개 : CS, SS, DS 및 ES가 0으로 강제 설정됩니다. x86 32 비트 및 64 비트 플랫폼의 이러한 상황은 본질적으로 EBP / RBP 레지스터를 메모리에 액세스하는 프로세서 명령에서 접두사없이 사용할 수 있음을 의미합니다.

따라서 작성한 컴파일러 옵션을 사용하면 BP / EBP / RBP를 다른 수단 (예 : 지역 변수 보유)에 사용할 수 있습니다.

"이렇게하면 프레임 포인터를 저장, 설정 및 복원하는 지침이 필요하지 않습니다."는 각 기능의 항목에 다음 코드를 사용하지 않는 것을 의미합니다.

또는 enterIntel 80286 및 80386 프로세서에서 매우 유용한 명령입니다.

또한 함수가 반환되기 전에 다음 코드가 사용됩니다.

또는 leave지시.

디버깅 도구는 스택 데이터를 스캔하고 이러한 푸시 된 EBP 레지스터 데이터를 찾는 동안 사용할 수 있습니다 call sites. 즉, 함수 이름과 인수를 계층 적으로 호출 된 순서대로 표시합니다.

프로그래머는 포괄적 인 용어 (단 하나의 함수 호출 만 제공하고 반환 주소, 인수 및 지역 변수를 유지하는 스택의 단일 엔티티)가 아니라 좁은 의미에서 스택 프레임에 대해 질문 할 수 있습니다 stack frames. 컴파일러 옵션의 컨텍스트. 컴파일러의 관점에서 스택 프레임은 루틴에 대한 시작 및 종료 코드 일 뿐이며 앵커를 스택에 푸시합니다. 이는 디버깅 및 예외 처리에도 사용할 수 있습니다. 디버깅 도구는 스택 데이터를 스캔하고 이러한 앵커를 역 추적에 사용할 수 call sites있으며, 스택에서 위치를 찾는 동안 즉, 계층 적으로 호출 된 순서대로 함수 이름을 표시 할 수 있습니다.

그렇기 때문에 컴파일러가이 코드를 생성할지 여부를 제어 할 수 있기 때문에 컴파일러 옵션 측면에서 스택 프레임이 무엇인지 프로그래머에게 이해하는 것이 매우 중요합니다.

경우에 따라 스택 프레임 (루틴의 시작 및 종료 코드)은 컴파일러에서 생략 할 수 있으며 변수는 편리한 기본 포인터 (BP /)가 아닌 스택 포인터 (SP / ESP / RSP)를 통해 직접 액세스됩니다. ESP / RSP). 컴파일러가 일부 함수에 대해 스택 프레임을 생략하는 조건은 다를 수 있습니다. 예를 들면 다음과 같습니다. (1) 함수가 리프 함수입니다 (즉, 다른 함수를 호출하지 않는 최종 엔터티); (2) 예외가 사용되지 않습니다. (3) 스택에서 나가는 매개 변수로 루틴이 호출되지 않습니다. (4) 함수에 매개 변수가 없습니다.

스택 프레임 (루틴의 진입 및 종료 코드)을 생략하면 코드가 더 작고 빨라질 수 있지만 스택의 데이터를 역 추적하고 프로그래머에게 표시하는 디버거의 기능에 부정적인 영향을 미칠 수 있습니다. 컴파일러가 스택 프레임 진입 및 종료 코드를 제공하기 위해 함수가 충족해야하는 조건을 결정하는 컴파일러 옵션입니다. 예를 들어 컴파일러는 (a) 항상, (b) 전혀, (c) 필요할 때 (조건 지정) 경우 함수에 이러한 시작 및 종료 코드를 추가하는 옵션을 가질 수 있습니다.

일반성에서 특수성으로 돌아 가기 : -fomit-frame-pointerGCC 컴파일러 옵션을 사용할 경우 루틴에 대한 시작 및 종료 코드와 추가 레지스터가 있으면 이길 수 있습니다 (기본적으로 자체적으로 또는 다른 사용자에 의해 암시 적으로 켜져 있지 않는 한). 이 경우 이미 EBP / RBP 레지스터를 사용하여 이득을 얻고 있으며 이미 암시 적으로 켜져있는 경우이 옵션을 명시 적으로 지정하여 추가 이득을 얻을 수 없습니다. 그러나 16 비트 및 32 비트 모드에서 BP 레지스터는 AX가 가진 (AL 및 AH)와 같이 8 비트 부분에 액세스 할 수 없습니다.

이 옵션은 컴파일러가 최적화에서 EBP를 범용 레지스터로 사용하도록 허용하는 것 외에도 디버깅을 복잡하게하는 스택 프레임에 대한 종료 및 진입 코드 생성을 방지하기 때문에 GCC 문서에 명시 적으로 명시되어 있습니다 (비정상적으로 굵은 글꼴로 강조) 스타일)이 옵션을 활성화하면 일부 컴퓨터에서는 디버깅이 불가능합니다.

디버깅 또는 최적화와 관련된 다른 컴파일러 옵션은 암시 적으로 -fomit-frame-pointer옵션을 켜거나 끌 수 있습니다.

다른 옵션 -fomit-frame-pointer 이 x86 플랫폼에 미치는 영향 대한 공식 정보는 gcc.gnu.org에서 찾지 못했습니다 . https://gcc.gnu.org/onlinedocs/gcc-3.4.4/gcc/Optimize-Options.html 다음 사항 만 명시합니다.

-O는 또한 디버깅을 방해하지 않는 시스템에서 -fomit-frame-pointer를 켭니다.

따라서 x86 플랫폼 에서 단일 옵션으로 컴파일하는 경우 켜질 것인지 여부 는 설명서 자체에서 명확하지 않습니다 . 경험적으로 테스트 할 수 있지만이 경우 GCC 개발자는 사전 통보없이이 옵션의 동작을 변경하지 않겠다는 약속이 없습니다.-fomit-frame-pointer-O

그러나 Peter Cordes-fomit-frame-pointerx86-16 플랫폼과 x86-32 / 64 플랫폼 간의 기본 설정에 차이가 있음을 주석에서 지적했습니다 .

이 옵션 은 GCC뿐만 아니라 인텔 C ++ 컴파일러 15.0-fomit-frame-pointer 과도 관련 이 있습니다 .

인텔 컴파일러의 경우이 옵션에는 별칭이 /Oy있습니다.

다음은 인텔이 작성한 내용입니다.

이러한 옵션은 EBP가 최적화에서 범용 레지스터로 사용되는지 여부를 결정합니다. 옵션 -fomit-frame-pointer 및 / Oy를 사용하면이 사용이 가능합니다. 옵션 -fno-omit-frame-pointer 및 / Oy- 허용하지 않습니다.

일부 디버거는 EBP가 스택 프레임 포인터로 사용되기를 기대하며 그렇지 않으면 스택 역 추적을 생성 할 수 없습니다. -fno-omit-frame-pointer 및 / Oy- 옵션은 디버거가 다음을 수행하지 않고도 스택 역 추적을 생성 할 수 있도록 모든 함수에 대한 스택 프레임 포인터로 EBP를 유지하고 사용하는 코드를 생성하도록 컴파일러에 지시합니다.

-fno-omit-frame-pointer의 경우 : -O0으로 최적화 해제 / Oy-의 경우 : / O1, / O2 또는 / O3 최적화 해제 -fno-omit-frame-pointer 옵션은 옵션을 지정할 때 설정됩니다. O0 또는 -g 옵션. -fomit-frame-pointer 옵션은 -O1, -O2 또는 -O3 옵션을 지정할 때 설정됩니다.

/ Oy 옵션은 / O1, / O2 또는 / O3 옵션을 지정할 때 설정됩니다. / Od 옵션을 지정하면 / Oy- 옵션이 설정됩니다.

-fno-omit-frame-pointer 또는 / Oy- 옵션을 사용하면 사용 가능한 범용 레지스터 수가 1 개 줄어들고 코드 효율성이 약간 떨어질 수 있습니다.

참고 Linux * 시스템의 경우 : 현재 GCC 3.2 예외 처리에 문제가 있습니다. 따라서 인텔 컴파일러는 GCC 3.2가 C ++ 용으로 설치되고 예외 처리가 켜져있는 경우 (기본값)이 옵션을 무시합니다.

위의 인용문은 GCC가 아닌 인텔 C ++ 15 컴파일러에만 해당됩니다.


1
16 비트 코드와 DS 대신 SS로 기본 설정되는 BP는 실제로 gcc와 관련이 없습니다. gcc -m16존재하지만 이것은 기본적으로 모든 곳에서 접두사를 사용하여 16 비트 모드에서 실행되는 32 비트 코드를 만드는 이상한 특수한 경우입니다. 또한 -fomit-frame-pointerx86 -m32에서는 수년 동안 기본적으로 활성화되었으며 x86-64 ( -m64) 보다 오래 사용되었습니다 .
Peter Cordes

@PeterCordes-감사합니다. 귀하가 제기 한 문제에 따라 편집 내용을 업데이트했습니다.
Maxim Masiutin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.