# LLVM은 당신의 코드를 고쳐주지 못한다 – 컴파일러 한계와 올바른 디버깅·최적화 방법

---

# # 서론 – “컴파일러가 내 코드를 자동으로 고쳐줄까?”

최근 레딧 *r/programming*에 올라온 **“No, LLVM can't fix your code”** 스레드에서 많은 개발자들이 **LLVM**이 모든 버그를 자동으로 수정해줄 수 있다는 오해를 하고 있음을 확인했습니다. 실제로 LLVM은 강력한 **컴파일러 최적화** 엔진이지만, **논리적 오류**나 **설계 결함**을 스스로 고쳐주지는 못합니다. 특히 초보자라면 “컴파일러가 내 코드를 고쳐줄 거야”라는 기대에 좌절하기 쉽습니다. 이번 글에서는 LLVM이 할 수 있는 일과 할 수 없는 일을 명확히 구분하고, **코드 디버깅**과 **성능 최적화**를 스스로 수행할 수 있는 실전 가이드를 제공합니다.

---

# # 1️⃣ LLVM이란 무엇인가? – 기본 개념과 주요 역할

| 항목 | 설명 |

|------|------|

| **LLVM** (Low Level Virtual Machine) | 중간 표현(IR)을 기반으로 다양한 언어 프론트엔드와 백엔드를 연결하는 모듈식 컴파일러 프레임워크 |

| **주요 기능** | ① 코드 최적화 (인라인, 루프 전개, 데드 코드 제거 등) ② 다중 플랫폼 지원 (x86, ARM, WebAssembly 등) ③ 정적 분석 도구와 연동 (clang‑tidy, clang‑format) |

| **제한점** | 논리적 버그, 설계 결함, 런타임 오류 등 **프로그램 자체의 오류**는 감지·수정하지 못함 |

> **핵심 포인트**: LLVM은 **‘어떻게 코드를 더 빠르게 실행할지’**를 고민하지만, **‘무엇을 해야 하는지’**는 전혀 알지 못합니다.

---

# # 2️⃣ 흔히 저지르는 오해 3가지

1. **“컴파일러가 자동으로 최적화해준다”**

   - 실제로는 **가능한 경우에만** 최적화가 적용됩니다. 정의되지 않은 동작(UB)이 존재하면 최적화가 오히려 버그를 드러내기도 합니다.

2. **“경고가 없으면 코드가 완벽하다”**

   - 경고는 **가능한 문제**만을 알려줄 뿐, 논리적 오류는 전혀 표시되지 않을 수 있습니다.

3. **“LLVM이 메모리 누수를 방지한다”**

   - 메모리 관리 자체는 언어 수준(예: C++의 스마트 포인터)이나 런타임에 의존합니다. LLVM은 메모리 접근을 최적화하지만 누수를 자동으로 잡아주지는 않습니다.

---

# # 3️⃣ 초보자를 위한 실전 디버깅 단계

## # 3‑1. 컴파일러 경고를 반드시 확인하기

```
bash

clang++ -Wall -Wextra -Werror main.cpp

```

* `-Wall`·`-Wextra` : 대부분의 경고를 출력

* `-Werror` : 경고를 오류처럼 처리해 무시하지 못하게 함

## # 3‑2. 정적 분석 도구 활용

| 도구 | 특징 |

|------|------|

| **clang‑tidy** | 코드 스타일, 잠재적 버그, 현대 C++ 사용 권장 사항을 자동으로 검출 |

| **cppcheck** | 경량 정적 분석기로 빠른 피드백 제공 |

| **scan‑build** (clang) | LLVM 기반 정적 분석기로 복잡한 메모리 오류까지 탐지 |

> **Tip**: CI 파이프라인에 `clang‑tidy` 를 추가하면 PR 단계에서 자동으로 코드 품질을 검증할 수 있습니다.

## # 3‑3. 단위 테스트와 커버리지 확보

* **Google Test**, **Catch2** 등 프레임워크로 함수·클래스 별 테스트 작성

* `llvm-cov` 와 `llvm-profdata` 로 테스트 커버리지를 시각화하고, README에 배지 형태로 표시하면 신뢰도가 상승합니다.

## # 3‑4. 프로파일링으로 병목 찾기

```
bash

clang++ -O2 -g -fprofile-instr-generate -fcoverage-mapping main.cpp -o app

LLVM_PROFILE_FILE='default.profraw' ./app

llvm-profdata merge -sparse default.profraw -o default.profdata

llvm-cov show ./app -instr-profile=default.profdata

```

* `-O2`·`-g` : 최적화와 디버깅 심볼 동시 제공

* `llvm-profdata`·`llvm-cov` 로 실행 시간과 코드 커버리지를 분석해 **성능 최적화 포인트**를 정확히 파악

---

# # 4️⃣ LLVM 최적화를 올바르게 활용하는 법

## # 4‑1. 최적화 레벨 선택

| 레벨 | 특징 |

|------|------|

| **-O0** | 디버깅에 최적, 최적화 없이 원본 코드 그대로 |

| **-O1 / -O2** | 일반적인 최적화 (인라인, 루프 전개 등) |

| **-O3** | Aggressive 최적화 (벡터화, 루프 언롤링) – 디버깅 어려움 가능 |

| **-Os** | 코드 크기 최소화 – 임베디드 환경에 유용 |

## # 4‑2. 특정 최적화 패스 제어

```
bash

clang++ -O2 -mllvm -instcombine -mllvm -loop-unroll

```

* `-mllvm` 옵션으로 개별 패스를 켜거나 끌 수 있어 **컴파일 시간**을 단축하고, 디버깅 시 불필요한 변형을 방지합니다.

## # 4‑3. 프로파일 가이드 최적화 (PGO)

1. 실제 워크로드로 프로그램 실행 → 프로파일 수집 (`-fprofile-generate`)

2. 수집된 프로파일을 이용해 재컴파일 (`-fprofile-use`)

* PGO는 **실제 사용 패턴**을 반영해 최적화 효율을 크게 높입니다.

---

# # 5️⃣ 코드 품질을 높이는 습관

1. **명확한 네이밍** – 변수·함수는 `snake_case`, 클래스는 `CamelCase` 로 일관성 유지

2. **주석과 문서화** – “왜 이렇게 구현했는가?”를 기록하면 팀 리뷰 시 큰 도움이 됩니다.

3. **코드 리뷰** – 동료와의 리뷰를 통해 LLVM이 놓친 논리적 오류를 조기에 발견

4. **버전 관리** – Git 브랜치를 활용해 **실험적인 최적화**를 별도 브랜치에서 진행하고, CI에서 자동 검증 후 메인에 병합

---

# # 결론 – 스스로 코드를 고치는 능력을 키우자

LLVM은 **강력한 컴파일러 최적화** 도구이지만, **버그를 자동으로 고쳐주지는 않는다**는 점을 명심해야 합니다. 초보 개발자는 다음 세 가지를 실천하면 **코드 품질**과 **성능**을 동시에 끌어올릴 수 있습니다.

1. **컴파일러 경고와 정적 분석을 절대 무시하지 않기**

2. **단위 테스트·커버리지·프로파일링을 꾸준히 수행하기**

3. **LLVM 최적화 옵션을 목적에 맞게 세밀하게 조정하기**

> **지금 바로 실행해 보세요!**

> * 프로젝트에 `-Wall -Wextra -Werror` 플래그를 추가하고, `clang-tidy` 로 첫 번째 정적 분석을 수행해 보세요.

> * 간단한 테스트 케이스를 하나 작성하고, `llvm-cov` 로 커버리지를 확인하면 **코드 품질이 눈에 보이게 향상**되는 것을 체감할 수 있습니다.

이러한 습관이 자리 잡히면, LLVM이 제공하는 **컴파일러 최적화**와 **코드 디버깅** 기능을 최대한 활용하면서도 스스로 버그를 찾아 고치는 **자립형 개발자**가 될 수 있습니다.

---

*읽어 주셔서 감사합니다. 궁금한 점이나 직접 적용해 본 결과가 있다면 댓글로 공유해 주세요!*
