2013-10-31 2 views
2

안녕하세요왜 컴파일러는 변수를 레지스터에 저장합니까?

레지스터

는 CPU 레지스터에 선언되는 변수를 저장하도록 컴파일러에 지시 다양한 문서에서 이런 종류의 물건을 읽고있다.

표준 C의 방언

는 키워드 레지스터는 다음 구문 사용

레지스터 데이터 정의; 레지스터 유형 수정자는 액세스를 최적화하기 위해> CPU 레지스터 (가능한 경우)에 선언 된 변수를 저장하도록 컴파일러에 지시합니다. 예 :

register int i; 최적화가 켜지면 TIGCC는 자주 사용되는 변수를 CPU 레지스터에 자동으로 저장하지만 키워드 레지스터는 최적화가 꺼져도 레지스터에 저장해야합니다. 그러나 컴파일러가이 장소에서 사용하기에 충분한 여유 레지스터가 없다고 결론을 내리는 경우 레지스터에 데이터 저장 요구가 거부 될 수 있습니다.

요점은 등록에 관한 것만이 아닙니다. 내 요점은 컴파일러가 변수를 메모리에 저장하는 이유입니다. 컴파일러 비즈니스는 단지 컴파일하고 객체 파일을 생성하는 것입니다. 런타임에 실제 메모리 할당이 발생합니다. 왜 컴파일러가이 사업을할까요? 내 생각에 파일 자체를 컴파일하여 객체 파일을 실행하지 않고 메모리 할당이 C의 경우에 발생합니까?

+1

나는 당신이 묻는 것을 보지 못했습니다. 명령을 실행하려면 변수가 레지스터에 있어야합니다. 이것이 바로 CPU가 작동하는 방식입니다. – Kevin

+1

정확히 무엇을 요구하고 있는지 불분명합니다. 읽는 문서는 컴파일러가 생성하는 코드에 대해 이야기합니다. 즉 C 코드에서 컴파일러는 변수를 메모리에 저장하는 위치, 명령이 실행될 때 사용할 CPU 레지스터 등을 포함하여 CPU 작동 방법을 알려주는 코드를 생성해야합니다. 컴파일러가 해당 코드를 생성하면 컴파일 된 코드를 실행할 때 실제 메모리/레지스터 할당 및 사용이 발생합니다. – nos

답변

5

컴파일러가 기계 코드를 생성합니까? 그리고 실제로 프로그램을 실행하기 위해 기계 코드가 실행됩니다. 따라서 컴파일러는 어떤 머신 코드를 생성할지 결정하므로 런타임에 어떤 종류의 할당이 발생할지에 대한 결정을 내리고 있습니다. gcc foo.c을 입력하면 실행할 수 없지만 나중에 실행 파일을 실행하면 실행중인 GCC 코드가 생성됩니다.

이것은 컴파일러가 가능한 빨리 금식 코드를 생성하고 컴파일 할 때 가능한 한 많은 결정을 내리고 싶어한다는 것을 의미합니다. 여기에는 사물을 할당하는 방법이 포함됩니다.

+0

덧붙여서'register'는 컴파일러가 변수를 레지스터에 실제로 할당하도록 강제하지는 않지만 유용하다는 표시 일뿐입니다. 내가 읽은 주석 중 일부가 사실이라면, 현대 컴파일러는 어떤 변수가'register'로 더 유용 할 지 짐작할 때 더 잘 수행 할 수 있도록 최적화되어 있으며 주로 키워드를 무시합니다. – SJuan76

+0

모든 컴파일러와 동일합니다. 런타임은 항상 실제 비즈니스 인 코드를 실행하며 컴파일러가 생성하는 코드를 실행합니다 (런타임에 발생할 수있는 결정이 거의 없을 수 있음). 그러나 다른 언어 문서에서는 컴파일러가이를 수행하고 그렇게합니다. 그들은 컴파일러가 코드를 생성하고 런타임이 코드를 실행한다고 말합니다. 인터프리터 언어를 사용하더라도 동일합니다. 나는 c가 기계어 코드를 생성한다는 것을 알고 있지만 컴파일러는 소스를 기계로 이해할 수 있지만 실제로 실행해야한다면 실행되어야한다. 왜 C 문서가 다른 문서와 다른가요? –

+0

@MuralidharYaragalla 모든 C 컴파일러에서 컴파일러는 이러한 작업을 수행하는 코드를 생성하기로 결정합니다. 그것은 "지휘관이 그 섬을 공격 할 것"이라고 말하는 것과 같습니다, 지휘관이 직접하지는 않겠지 만 그는 명령을 내립니다 – jozefg

1

컴파일러는 코드를 기계어 명령으로 변환하고 코드 실행 방법을 컴퓨터에 알려야합니다. 여기에는 두 개의 숫자를 곱하는 것과 같은 연산을 만드는 방법과 데이터 (스택, 힙 또는 레지스터)를 저장하는 방법이 포함됩니다.

1

컴파일러는 프로파일 링과 더 나은 코드 실행을 위해 몇 번 반복하지 않는 한 코드를 실행하지 않지만이를 준비해야합니다 - 프로그램에서 정의한 변수를 유지하는 방법, 레지스터로 효율적으로 저장하거나, 느린 (그리고 더 부작용을 일으키기 쉬운) 메모리를 사용하십시오.

처음에는 지역 변수에 스택 프레임의 위치가 지정됩니다 (동적 할당을 명시 적으로 사용하는 메모리는 제외). 함수가 int를 할당하면 컴파일러는 스택에 몇 바이트 씩 증가하도록 지시하고 해당 메모리 주소를 사용하여 해당 변수를 저장하고 코드가 해당 변수에서 수행하는 모든 연산에 피연산자로 전달할 수 있습니다.
그러나 메모리가 (캐시 된 경우에도) 더 느리고 CPU를 더 많이 사용하기 때문에 나중에 컴파일러에서 일부 변수를 레지스터로 이동하려고 할 수 있습니다. 이 할당은 아키텍처에있는 기존 논리 레지스터 수에 들어갈 수있는 가장 재사용되고 지연 시간이 긴 변수를 선택하려고하는 복잡한 알고리즘을 통해 수행됩니다 (피연산자가이 명령에 포함될 필요가있는 몇 가지 지침과 같은 다양한 제한 사항을 확인하는 동안 또는 그 레지스터).

일부 메모리 주소는 컴파일 타임에 알려지지 않은 방식으로 외부 포인터를 사용하여 별칭을 지정할 수 있습니다.이 경우 레지스터로 이동할 수 없습니다. 컴파일러는 대개 매우 조심스럽고 대다수는 위험한 최적화를 피할 것입니다 (그렇지 않으면 불쾌한 일을 피하기 위해 특별한 검사를해야합니다). 모든 그 후

는, 컴파일러는 여전히 조언 할 정도로 정중있는 변수 것이 중요하고 그가 그것을 놓친, 당신은 기본적으로 그에게 요구하고있는 register 키워드로 다음을 표시하여 경우에, 당신에게 중요한 레지스터를 사용할 수 있고 앨리어싱을 사용할 수 없다는 가정하에이 변수에 대해 레지스터를 사용하여이 변수를 최적화하려고 시도하십시오., 다음 코드를 가지고 두 번 같은 일을하지만 약간 다른 상황에 :

는 여기에 약간의 예제 내가 로컬

#include "stdio.h" 

int j; 
int main() { 
    int i; 
    for (i = 0; i < 100; ++i) { 
     printf ("i'm here to prevent the loop from being optimized\n"); 
    } 
    for (j = 0; j < 100; ++j) { 
     printf ("me too\n"); 
    } 
} 

주, j는 글로벌이다 (따라서 컴파일러는 '아무튼 실행 중에 다른 사람이 액세스 할 수 있는지 알지 못합니다.) -O3와 GCC에서 컴파일

주에 대해 다음 코드를 생성합니다 : 당신이 볼 수 있듯이, 첫 번째 루프 카운터는 EBX에 앉아, 각각의 반복에 증가 및 한계 비교됩니다

0000000000400540 <main>: 
    400540:  53      push %rbx 
    400541:  bf 88 06 40 00   mov $0x400688,%edi 
    400546:  bb 01 00 00 00   mov $0x1,%ebx 
    40054b:  e8 18 ff ff ff   callq 400468 <[email protected]> 
    400550:  bf 88 06 40 00   mov $0x400688,%edi 
    400555:  83 c3 01    add $0x1,%ebx   # <-- i++ 
    400558:  e8 0b ff ff ff   callq 400468 <[email protected]> 
    40055d:  83 fb 64    cmp $0x64,%ebx 
    400560:  75 ee     jne 400550 <main+0x10> 
    400562:  c7 05 80 04 10 00 00 movl $0x0,1049728(%rip)  # 5009ec <j> 
    400569:  00 00 00 
    40056c:  bf c0 06 40 00   mov $0x4006c0,%edi 
    400571:  e8 f2 fe ff ff   callq 400468 <[email protected]> 
    400576:  8b 05 70 04 10 00  mov 1049712(%rip),%eax  # 5009ec <j> (loads j) 
    40057c:  83 c0 01    add $0x1,%eax   # <-- j++ 
    40057f:  83 f8 63    cmp $0x63,%eax 
    400582:  89 05 64 04 10 00  mov %eax,1049700(%rip)  # 5009ec <j> (stores j back) 
    400588:  7e e2     jle 40056c <main+0x2c> 
    40058a:  5b      pop %rbx 
    40058b:  c3      retq 

.
그러나 두 번째 루프는 위험한 것이었고 gcc는 메모리를 통해 인덱스 카운터를 전달하기로 결정했습니다 (모든 반복에서 rax로로드). 이 예제는 레지스터를 사용할 때 얼마나 좋을지와 때로는 할 수없는 방법을 보여줍니다.

+0

감사합니다. 그것은 도움이되었다. –

관련 문제