Inline assembler
Intro
Inline assembly에서 정해주어야 하는 것들은 다음와 같다.
- assembly 코드
- output 변수들
- input 변수들
- output외에 값이 바뀌는 레지스터들
-
__asm__
- 인라인 어셈블리 키워드.
- 이 키워드는 asm으로도 쓸 수 있지만
ansi
옵션으로 컴파일하게 되면asm
은 정의되어있지 않기 때문에__asm__
으로 쓰는 것이 좋다.
-
__volatile__
- 이 키워드는 해당하는 inline assembly를 optimization으로 제거하거나 변경하지 않게 고정한다.
- GCC manual에 따르면 side effect가 없다고 여겨지는 경우 assembly를 없애거나 loop의 밖으로 빼는 optimization을 할 수 있다고 한다.
- 예를 들어 output이 있지만 실제로 output으로 쓰인 변수가 그 이후로 한 번도 쓰이지 않았다면 그 inline assembly는 프로그램의 수행에 아무런 영향을 끼치지 않는다고 생각하고 없애버리는 것입니다. 물론 조건을 정확하게 정해주면 굳이
__volaitile__
을 붙이지 않더라도 제대로 작동하겠지만 가끔씩 엉뚱하게 되버리는 경우도 있기때문에 잘 생각해서 inline assembly를 쓰고__volatile__
을 붙여주는 것이 좋다.
-
asms
- 쌍따옴표로 둘러싸인 assembly 문자열. 문자열안에서
%n
형태로 input, output 인자들을 사용할 수 있으며 인자들이 치환된 후 그대로 컴파일 된 assembly에 나타난다.
-
output
- 쉼표로 구분된 "constraint"(variable)들의 리스트이며 각각이 inline assembly에서 쓰이는 output 인자를 나타낸다.
-
input
- output과 같은 형태이며 input인자들을 나타낸다.
-
clobber
- 쉼표로 구분되는 쌍따옴표로 둘러싸인 레지스터 이름들의 리스트이며 input, output에 나오진 않았지만 해당 assembly를 수행한 결과로 값이 바뀌는 레지스터들을 나타낸다.
참고로 ouput, input, clobber는 비어있다면 뒤에서 부터 생략될 수 있다. 하지만 앞에 오는 파라미터가 비어있을 때는 :
로 표시해야 한다.
Example
실제로 input, output이 쓰인 예는 아래와 같다.
int test_and_set_bit(int nr, volatile unsigned * addr)
{
int oldbit;
__asm__ __volatile__(
"lock; btsl %2,%1\n\tsbbl %0,%0"
:"=r" (oldbit),"=m" (*addr)
:"r" (nr));
return oldbit;
}
Simple example
불필요한 코드를 모두 제거한 아래와 같은 코드가 있다고 하자.
objdump1를 사용하여 asm 코드를 아래와 같이 확인할 수 있다.
_main:
0: 55 pushq %rbp
1: 48 89 e5 movq %rsp, %rbp
4: 31 c0 xorl %eax, %eax
6: c7 45 fc 00 00 00 00 movl $0, -4(%rbp)
d: c7 45 f8 0b 00 00 00 movl $11, -8(%rbp)
14: 5d popq %rbp
15: c3 retq
d
번째 라인을 보면 rbp
에 11을 삽입하는 코드를 확인할 수 있다.
a
값을 20
으로 수정하는 inline asm코드는 아래와 같이 적용할 수 있다.
#include <stdio.h>
int main()
{
int a = 11;
__asm__ __volatile__ ("movl $20, -8(%rbp)");
printf("%d", a);
return 0;
}
실행하면 20
이 출력되는 것을 확인할 수 있다.