AI 요약
2024년 4월 17일 공개된 이 아티클에서 저자는 현재 Rust의 'extern "Rust"' 호출 규약이 지나치게 보수적이며 비효율적이라고 비판합니다. Rust는 현재 LLVM의 C 호출 규약을 주로 활용하며 Clang이 생성할 법한 시그니처를 유지하려 하는데, 이는 디버거 호환성과 LLVM 버그 회피를 위한 선택이지만 실제로는 '형편없는 코드 생성(terrible codegen)'을 초래하고 있습니다. 구체적인 사례로 12바이트 크기의 배열([i32; 3])이 레지스터(rdi, rsi 등)를 통해 효율적으로 전달될 수 있음에도 불구하고, 현재 Rust는 이를 메모리 포인터로 전달하고 있습니다. 저자는 이러한 간극을 메우기 위해 Go의 레지스터 ABI를 참고한 새로운 호출 규약 설계를 제안하며, -Zcallconv 플래그를 통해 기존 방식(legacy)과 최적화 방식(fast)을 선택할 수 있는 구조를 제시합니다.
핵심 인사이트
- 데이터 처리 비효율성: 12바이트 너비의
[i32; 3]배열이 레지스터에 충분히 들어갈 수 있음에도 불구하고, 현재 Rust는 이를 포인터 방식으로 전달하여 성능 저하를 유발합니다. - Go 레지스터 ABI의 대안 제시: C ABI의 한계를 극복하기 위해 이미 성공적으로 레지스터 기반 인자 전달을 수행하고 있는 Go 언어의 방식을 롤모델로 지목했습니다.
- 플래그 기반 개선안: 사용자가
-Zcallconv=fast와 같은 컴파일 플래그를 통해 더 공격적이고 효율적인 호출 규약을 선택할 수 있도록 하는 아키텍처를 제안합니다. - 디버깅 환경의 유연성: ELF 기반 시스템에서 DWARF 포맷은 특정 ABI에 종속되지 않으므로, 호출 규약을 변경하더라도 디버깅 이슈가 크지 않음을 강조합니다.
주요 디테일
- 레지스터 활용 현황:
extern "C"요청 시[i32; 3]배열은rdi와rsi레지스터에 패킹되어 전달되지만, 기본 Rust 호출에서는 이 최적화가 제대로 이루어지지 않습니다. - LLVM과의 관계: Rust는 LLVM 버그를 자극하지 않기 위해 Clang과 유사한 코드 생성을 지향하지만, 저자는 오히려 Rust가 LLVM 버그를 유도하고 이를 수정해 나가는 것이 올바른 방향이라고 주장합니다.
- 범용 아키텍처 대응: 본문은 x86 어셈블리를 예시로 들고 있으나, 제안된 원칙은 ARM과 RISC-V 등 현대적인 CPU 아키텍처 전반에 적용 가능하도록 설계되었습니다.
- ABI 구성 요소: 호출 규약이 단순히 인자 전달뿐만 아니라 리턴 값 처리, 함수 프롤로그/에필로그, 언와인딩(unwinding) 방식을 모두 포함하는 개념임을 명시합니다.
향후 전망
- Rust 성능 최적화: 새로운 호출 규약이 도입될 경우, 복잡한 구조체를 빈번하게 주고받는 Rust 프로그램의 런타임 성능이 유의미하게 향상될 것으로 기대됩니다.
- 컴파일러 고도화:
rustc가 LLVM의 기본 설정에 의존하는 것을 넘어, 언어 특성에 최적화된 독자적인 ABI 생성 전략을 강화할 가능성이 높습니다.
출처:hackernews
