Output과 input은 "constraint" (variable)들의 쉼표로 구분된 리스트로 구성됩니다.
Constraint는 아래의 문자들과 몇가지 modifier들의 조합으로 허용되는 operand의 종류와 그 operand가 inline assembly에서 어떻게 사용되는지를 나타냅니다. 아래의 리스트는 완전하지 않습니다. Gcc 메뉴얼을 참조하세요.
Architecture가 일반적으로 지원하는 모든 addressing mode중 하나를 사용하는 memory operand.
범용 레지스터 operand.
레지스터를 적접 지정하는 constraint. Architecture마다 다른 문자들을 정의합니다. 'd', 'a', 'f'는 68000/68020에서 쓰는 문자들입니다.
정수 immediate operand. Assemble할 때가 되어야 알 수 있는 symbol값(주소)들도 해당됩니다.
값을 정확히 알고 있는 정수. Symbol값들은 해당되지 않습니다.
미리 정해진 범위안의 정수 immediate operand. 예를 들어 68000에서 'I'는 1에서 8사이의 immediate operand를 뜻하며 shift operation의 shift cound로 쓰입니다.
Compling machine과 target machine의 floating 표현 방식이 같을 때 immediate floating operand를 허용합니다.
Immediate floating operand.
Machine에 따라 정의되는 특정한 범위내의 immediate floating point operand.
범용 레지스터, memory, immediate operand 중 무엇이라도 됩니다.
어떠한 operand라도 허용합니다.
유효한 주소가 허용됩니다. Load address, push address등의 instruction에 쓰입니다.
Machine-dependent.
a, b, c, or d register
a, or d register (for 64-bit ints)
Floating point register
First (top of stack) floating point register
Second floating point register
a register
b register
c register
d register
di register
si register
Constant in range 0 to 31 (for 32bit shifts)
Constant in range 0 to 63 (for 64bit shifts)
0xff
0xffff
0, 1, 2, or 3 (shifts for lea instruction)
Constant in range 0 to 255 (for out instruction)
Standard 80387 floating point constant
Constraint modifier들은 그 변수가 어떻게 사용되는 지를 compiler에게 알려줍니다. 아래의 리스트는 완전하지 않습니다. Gcc 메뉴얼을 참조하세요.
변수의 값이 바뀜을 나타냅니다. Output들에 대해서는 이 modifier가 반드시 지정되어 있어야 합니다.
Early clobber. 다음 절에서 자세히 설명하겠습니다.
#include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { int a, sum; a = atoi(argv[1]); __asm__ __volatile__( "movl %1, %0 \n\t" "addl %2, %0 \n\t" "addl %3, %0 \n\t" "addl %4, %0 \n\t" "addl %5, %0 " : "=g" (sum) : "g" (a), "g" (a+1), "g" (a+2), "g" (a+3), "g" (a+4)); printf("a=%d, sum=%d\n", a, sum); return 0; } |
위의 프로그램을 컴파일하면
.file "early_clobber.c" .version "01.01" gcc2_compiled.: .section .rodata .LC0: .string "a=%d, sum=%d\n" .text .align 4 .globl main .type main,@function main: pushl %esi pushl %ebx movl 16(%esp),%eax pushl $0 pushl $10 pushl $0 pushl 4(%eax) call __strtol_internal movl %eax,%esi addl $16,%esp leal 1(%esi),%edx leal 2(%esi),%ebx leal 3(%esi),%ecx leal 4(%esi),%eax #APP movl %esi, %edx addl %edx, %edx addl %ebx, %edx addl %ecx, %edx addl %eax, %edx #NO_APP pushl %edx pushl %esi pushl $.LC0 call printf xorl %eax,%eax addl $12,%esp popl %ebx popl %esi ret .Lfe1: .size main,.Lfe1-main .ident "GCC: (GNU) 2.95.4 20010902 (Debian prerelease)" |
#APP와 #NOAPP 사이의 inline assembly에서 %0 (sum)에 %edx가 할당되었음을 알 수 있습니다. 그런데 #APP아래 두번째줄이 addl %edx, %edx로 %2 (a+1)도 %edx로 할당되었습니다. Gcc는 항상 모든 input 변수들이 다 사용된 후에 output 변수들이 쓰인다고 가정해서 input 변수와 output 변수를 같은 operand에 할당하기도 합니다. Input, output이 하나의 operand에 할당되고 위의 예처럼 input보다 output으로 먼저쓰이게 되면 틀린 결과가 나옵니다.
이런 경우에는 gcc에게 그 output 변수는 input의 값들이 모두 사용되기 전에 값이 바뀔 수 있다는 것을 알려주어야 합니다. 이것을 알려주기 위한 modifier가 early clobber modifier '&' 입니다. 위의 프로그램에서 output constraint "=g" (sum)을 "=&g" (sum)으로 바꾸고 다시 컴파일하면 다음과 같은 결과가 나옵니다.
.file "early_clobber.c" .version "01.01" gcc2_compiled.: .section .rodata .LC0: .string "a=%d, sum=%d\n" .text .align 4 .globl main .type main,@function main: pushl %edi pushl %esi pushl %ebx movl 20(%esp),%eax pushl $0 pushl $10 pushl $0 pushl 4(%eax) call __strtol_internal movl %eax,%esi addl $16,%esp leal 1(%esi),%ebx leal 2(%esi),%ecx leal 3(%esi),%edx leal 4(%esi),%eax #APP movl %esi, %edi addl %ebx, %edi addl %ecx, %edi addl %edx, %edi addl %eax, %edi #NO_APP movl %edi,%eax pushl %eax pushl %esi pushl $.LC0 call printf xorl %eax,%eax addl $12,%esp popl %ebx popl %esi popl %edi ret .Lfe1: .size main,.Lfe1-main .ident "GCC: (GNU) 2.95.4 20010902 (Debian prerelease)" |
Output 변수는 %edi로 할당되었고 어떤 input도 겹치게 할당되지 않았음을 알 수 있습니다.