그라운드 임베디드 유튜브 구독

노트 / 기계어와 어셈블리

C와 어셈블리 대조: 최적화 옵션이 바꾸는 것

간단한 C 함수를 디스어셈블해서 한 줄씩 맞춰 보고, -O0과 -O2가 같은 코드를 얼마나 다르게 만드는지 비교합니다.

GROUND EMBEDDED · 정리 노트기계어 04C와 어셈블리 대조 1arm-none-eabi-objdump -d -S firmware.el…

디스어셈블 보는 방법

arm-none-eabi-objdump -d -S firmware.elf > fw.lst

-S를 붙이면 C 소스와 명령어가 섞여 나옵니다. STM32CubeIDE에서는 디버그 중 Disassembly 창을 열면 됩니다.

같은 함수, 다른 결과

int sum(const int *a, int n) {
    int s = 0;
    for (int i = 0; i < n; i++) s += a[i];
    return s;
}
  • -O0: 변수 s, i를 매번 스택에 저장하고 다시 읽습니다. 디버깅하기 좋지만 느립니다.
  • -O2: s, i를 레지스터에만 두고, 포인터를 직접 증가시키며 루프를 돕니다. 명령어 수가 절반 이하로 줄어듭니다.

최적화가 드러내는 버그

-O2에서만 동작이 이상해진다면 대개 다음 중 하나입니다.

  • 인터럽트와 공유하는 변수에 volatile이 없음 → 루프 밖으로 읽기가 빠짐
  • 정의되지 않은 동작(부호 있는 오버플로, 초기화 안 된 변수)
  • 지연 루프 for (i = 0; i < 1000; i++);가 통째로 사라짐

"최적화하면 망가진다"가 아니라 "최적화가 원래 있던 버그를 드러냈다"로 봐야 합니다.