GNU C는 x86 아키텍쳐를 매우 잘 지원하며, C 프로그램에 어셈블리코드를 삽입할 수 있다. 레지스터 할당은 직접 지시하거나 GCC에 맡겨둘 수 있다. 물론, 어셈블리 명령어는 아키텍쳐마다 다르다.
asm 명령어를 사용하여 어셈블리 명령어를 C나 C++ 프로그램에 삽입할 수 있다. 예를 들어:
~cpp
asm ("fsin" : "=t" (answer) : "0" (angle));
는 다음 C 문장을 x86 식으로 코딩한 것이다:
~cpp
answer = sin(angle);
일반적인 어셈블리코드 명령어와 달리 asm 문장은 C 문법으로 입력과 출력 연산수를 지정할 수 있다. Asm 문장은 아무때나 사용하면 안된다. 그러면 언제 사용해야 하나?
Asm 문장은 프로그램이 컴퓨터 하드웨어에 직접 접근하게 한다. 그래서 빨리 실행되는 프로그램을 만들 수 있다. 하드웨어와 직접 상호작용하는 운영체제 코드를 작성할때 사용할 수 있다. 예를 들어, /usr/include/asm/io.h에는 입출력 포트를 직접 접근하기위한 어셈블리 명령어가 있다.
또, 인라인 어셈블리 명령어는 프로그램의 가장 안쪽 반복문의 속도를 빠르게한다. 예를 들어, 어떤 같은 각도에 대한 sine과 cosine은 fsincos x86 명령어로 얻을 수 있다. 아마도 아래 두 목록은 이 점을 잘 이해하도록 도와줄 것이다.
#목록 11
#이름 : bit-pos-loop.c
#설명 : 반복문을 사용하여 비트 위치 찾기
~cpp
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
long max = atoi (argv[1]);
long number;
long i;
unsigned position;
volatile unsigned result;
for (number = 1; number <= max; ; ++number) {
for (i=(number>>1), position=0; i!=0; ++position)
i >>= 1;
result = position;
}
return 0;
}
#목록 12
#이름 : bit-pos-asm.c
#설명 : bsrl을 사용하여 비트 위치 찾기
~cpp
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
long max = atoi(argv[1]);
long number;
unsigned position;
volatile unsigned result;
for (number = 1; number <= max; ; ++number) {
asm("bsrl %1, %0" : "=r" (position) : "r" (number));
result = position;
}
return 0;
}
다음과 같이 최상의 최적화로 두 코드를 컴파일한다:
~cpp
$ cc -O2 -o bit-pos-loop bit-pos-loop.c
$ cc -O2 -o bit-pos-asm bit-pos-asm.c
최소한 몇 초동안 실행되도록 큰 값을 명령행 아규먼트로 주고 time 명령어를 사용하여 두 코드의 실행시간을 잰다.
~cpp
$ time ./bit-pos-loop 250000000
and
~cpp
$ time ./bit-pos-asm 250000000
결과는 컴퓨터마다 다를 것이다. 그러나 인라인 어셈블리를 사용한 코드가 매우 빠르게 실행됨을 확인할 수 있다.
GCC의 최적화는 asm 표현이 있더라도 실행시간을 최소화하기위해 프로그램 코드를 재배열하고 재작성하려고 시도한다. asm의 출력값을 사용하지 않는다고 판단하면, asm과 아규먼트 사이에 키워드 volatile이 없는 한 최적화는 명령어를 생략한다. (특별한 경우로 GCC는 출력 연산수가 없는 asm을 반복문 밖으로 옮기지 않는다.) asm은 예측하기 힘든 방식으로, 심지어 호출간에도, 옮겨질 수 있다. 특별한 어셈블리 명령어 순서를 보장하는 유일한 방법은 모든 명령어를 모두 같은 asm에 포함하는 것이다.
컴파일러가 asm의 의미를 모르기때문에 asm을 사용하면 컴파일러의 효율성을 제한할 수 있다. GCC는 어떤 최적화를 막을 수 있는 보수적인 추측을 하게 된다.