asm 선언
asm 선언 (인라인 어셈블리)
C++ 코드 안에 어셈블리어 소스를 그대로 심고 싶을 때 쓰는 게 asm 선언이에요. 다만 이 선언은 조건부 지원(conditionally-supported)이라, 환경에 따라 없을 수도 있고 의미가 정해져 있지 않을 수도 있어요. 이 페이지에서 asm 선언의 문법과 동작 방식을 정리해 볼게요.
출처: cppreference
본문
asm 선언은 C++ 프로그램 안에 어셈블리어 소스 코드를 내장할 수 있게 해줘요. 이 선언은 조건부 지원이며(C++11부터는) 구현 정의(implementation-defined)라서, 없을 수도 있고, 구현이 제공하더라도(C++11부터) 고정된 의미를 가지지 않아요.
문법
attr (optional) asm ( string-literal ) ; (C++26 이전)
attr (optional) asm ( balanced-token-seq ) ; (C++26부터)
설명
balanced-token-seq는 보통, 이 선언이 실행될 때마다 실행되는 어셈블리어로 쓰인 짧은 프로그램을 나타내는 문자열 리터럴이에요. 서로 다른 C++ 컴파일러는 asm 선언에 대한 규칙이 매우 다르고, 주변 C++ 코드와의 상호작용에 대한 관례도 달라요.
다른 블록 선언처럼 이 선언은 블록 안(함수 본문이나 다른 복합문)에 나타날 수 있어요. 다른 모든 선언처럼 블록 바깥에도 나타날 수 있어요.
주의
asm 선언은 환경에 크게 의존해서, 이식성 있는 코드에서는 거의 쓰지 않아요. 쓴다면 보통 대상 플랫폼과 컴파일러의 어셈블리 문법을 구체적으로 알아야 해요.
키워드
asm
예시
GCC/Clang 컴파일러가 제공하는 두 종류의 인라인 어셈블리 문법을 보여줘요. 이 프로그램은 Linux의 x86_64 플랫폼에서만 제대로 동작해요.
#include <iostream>
extern "C" int func(int x);
// func의 정의는 어셈블리어로 작성돼 있음
// raw string literal이 아주 유용할 수 있음
asm(R"(
.globl func
.type func, @function
func:
.cfi_startproc
movl %edi, %eax /* x는 RDI에 있음, x86-64 호출 규약 참고 */
addl $1, %eax
ret
.cfi_endproc
)");
int main()
{
int n = func(0110);
// 예전에는 비표준 인라인 어셈블리였지만 P2361R6으로 표준화됨
asm ("leal (%0,%0,4),%0"
: "=r" (n)
: "0" (n));
std::cout << "73*5 = " << n << std::endl; // flush는 의도적
// 표준 인라인 어셈블리
asm ("movq $60, %rax\n" // Linux의 exit syscall 번호
"movq $2, %rdi\n" // 이 프로그램은 2를 반환
"syscall");
}
출력:
73*5 = 365
func(0110)에서 0110은 팔진수 리터럴이에요(십진수 72). func가 인자에 1을 더해서 73이 되고, 인라인 어셈블리 leal (%0,%0,4),%0이 n * 5를 계산해 365가 되는 거예요.
더 알아보기
- 어셈블리와 관련된 바이너리·링크 규약은 C++ ABI 문서에서 볼 수 있어요.
- asm 선언은 컴파일러 확장이 많으므로, 쓰는 컴파일러의 인라인 어셈블리 문서를 함께 봐야 해요.
- cppreference의 asm 선언 원문에서 결함 보고 기록을 더 볼 수 있어요.