← 모든 기록
C & Compiler · 2026.09.17

4주차 - 프로시저의 스택 할당과 지역 변수의 레지스터 승격

프로시저가 별도의 스택 공간을 할당하지 않는 경우와 GCC 최적화 수준에 따른 지역 변수의 저장 위치를 살펴보고, red zone과 레지스터 승격을 정리.

4주차 - 프로시저의 스택 할당과 지역 변수의 레지스터 승격 대표 이미지

프로시저와 스택 메모리 할당

프로시저(procedure) 는 특정 작업을 수행하는 코드 블록이다. 가상 메모리의 스택 영역에서 스택 메모리를 할당하는 기준은 프로시저이며, 각 프로시저는 필요한 만큼만 자기 스택 프레임을 할당한다.

그런데 프로시저인데도 스택 메모리 할당이 일어나지 않는 경우가 있다.

스택 할당이 한 번만 일어나는 경우

책의 그림 3.32 사례를 간단히 하면 다음과 같다.

  • P 함수가 Q 함수를 호출하므로 프로시저는 2개다.
  • 하지만 스택 공간을 할당하는 인스트럭션은 P 함수 상단에서 단 1회 실행된다.
subq $32, %rsp

피호출 함수 Q가 아래 두 조건을 만족하면 이렇게 된다.

  1. 다른 함수를 호출하지 않는 함수(리프 함수)이다.
  2. 필요한 데이터를 모두 레지스터에 담을 수 있다.

이 경우 Q에는 별도의 스택 메모리 할당(subq ..., %rsp)이 없다. Q는 레지스터만 사용해 연산을 수행하고 반환한다.

Q에 지역 변수가 있다면?

Q가 자신만의 지역 변수 t를 가지고 있다면 스택 메모리를 사용할까? 결론부터 말하면 컴파일러 최적화 레벨에 따라 달라진다.

long q(long x, long y) {
    long t = x + y;
    return t * 3;
}

gcc -O0로 컴파일한 경우

q:
    pushq %rbp
    movq  %rsp, %rbp
    movq  %rdi, -24(%rbp)     # x를 스택에 저장
    movq  %rsi, -32(%rbp)     # y를 스택에 저장
    movq  -24(%rbp), %rdx
    movq  -32(%rbp), %rax
    addq  %rdx, %rax
    movq  %rax, -8(%rbp)      # t를 스택에 저장
    movq  -8(%rbp), %rdx
    movq  %rdx, %rax
    addq  %rax, %rax
    addq  %rdx, %rax          # t * 3
    popq  %rbp
    ret
  • O0에서는 레지스터로 충분하더라도 인자와 지역 변수를 모두 스택 메모리에 저장한다. 변수마다 고정된 메모리 위치가 있어야 디버거에서 언제든 값을 확인할 수 있기 때문이다.

참고: 이 코드에는 subq ..., %rsp가 없다. q가 리프 함수라서 %rsp를 내리지 않고 그 아래 영역(red zone)에 바로 저장하기 때문이다. 스택 할당 명령어는 없지만 스택 메모리는 사용하고 있다.

gcc -Og로 컴파일한 경우

q:
    leaq  (%rdi,%rsi), %rax      # t = x + y  → t는 %rax
    leaq  (%rax,%rax,2), %rax    # t * 3
    ret

t를 위한 메모리 공간이 따로 없다. t의 값은 레지스터 %rax에 잠시 머물다가 곧바로 다음 연산에 사용되고, 덮어써지면서 사라진다.

레지스터 승격

이처럼 gcc -Og 이상의 최적화 레벨에서는 지역 변수로 선언한 값이 스택 메모리에 저장되지 않고 레지스터에 잠시 머물다 가는데, 이를 레지스터 승격이라고 한다.

C의 지역 변수는 소스 코드 수준의 이름일 뿐이다. 컴파일러는 이를 스택 메모리에 둘 수도, 레지스터에 둘 수도 있다.

그래도 스택에 저장되는 경우

모든 지역 변수가 레지스터로 승격되는 것은 아니다. CS:APP 3.7.4에서는 지역 변수가 메모리에 있어야 하는 경우를 세 가지로 든다.

경우이유
레지스터가 모자랄 때담을 레지스터가 없음 (register spill)
지역 변수에 & 연산자를 쓸 때주소가 필요한데 레지스터에는 주소가 없음
지역 변수가 배열이나 구조체일 때원소·필드를 주소 기반 참조로 접근해야 함

앞의 t는 이 중 어디에도 해당하지 않아서 레지스터로 승격될 수 있었다.

정리

  • 스택 메모리는 프로시저 단위로, 필요한 만큼만 할당된다.
  • O0에서는 모든 지역 변수가 스택 메모리에 저장된다.
  • Og 이상에서는 지역 변수를 기본적으로 레지스터에 두고, 위 세 가지 경우에만 스택에 저장한다.
  • 다른 함수를 호출하지 않고 데이터를 모두 레지스터에 담을 수 있는 함수는 스택 할당 없이 레지스터만으로 실행된다.

어셈블리 결과는 컴파일러 버전에 따라 조금 다를 수 있으니 Compiler Explorer에서 직접 확인해 보는 것을 추천한다.

이어 읽으면 좋은 기록