인라인 어셈블러
인라인 어셈블러 (Inline Assembler)
D는 시스템 프로그래밍 언어로서 인라인 어셈블러를 제공해요. 같은 CPU 제품군에서는 D 구현 간에 인라인 어셈블러가 표준화되어 있어, 예를 들어 Win32 D 컴파일러의 Intel Pentium 인라인 어셈블러는 Intel Pentium에서 도는 Linux의 인라인 어셈블러와 문법 호환이 돼요. 이 문서는 x86과 x86_64 구현을 설명해요.
출처: Inline Assembler
본문
D는 시스템 프로그래밍 언어로서 인라인 어셈블러를 제공해요. 인라인 어셈블러는 같은 CPU 제품군의 D 구현들 사이에서 표준화되어 있어요. 예를 들어 Win32 D 컴파일러의 Intel Pentium 인라인 어셈블러는 Intel Pentium에서 도는 Linux용 인라인 어셈블러와 문법 호환이 돼요.
서로 다른 아키텍처의 D 구현들은 메모리 모델, 함수 호출/반환 규약, 인자 전달 규약 등에서 자유롭게 혁신할 수 있어요.
이 문서는 인라인 어셈블러의 x86 및 x86_64 구현을 설명해요. 컴파일러가 제공하는 인라인 어셈블러 플랫폼 지원은 각각 D_InlineAsm_X86과 D_InlineAsm_X86_64 버전 식별자로 나타나요.
Asm statement
AsmStatement:
asm FunctionAttributesopt { AsmInstructionListopt }
AsmInstructionList:
AsmInstruction ;
AsmInstruction ; AsmInstructionList
어셈블러 명령은 asm 블록 안에 위치해야 해요. 함수처럼 asm 문장은 호출자와 호환되도록 적절한 함수 속성으로 주석을 달아야 해요. asm 문장의 속성은 명시적으로 정의해야 하며, 추론되지 않아요.
@safe는 컴파일러가 어셈블리 문장에 안전성 검사를 하지 않으므로 속성으로 허용되지 않아요 — 대신 @trusted를 사용하세요.
void ok() pure nothrow @safe @nogc
{
asm pure nothrow @trusted @nogc
{}
}
void error() @safe @nogc
{
asm @nogc // Error: asm statement is assumed to be @system - mark it with '@trusted' if it is not
{}
asm @safe @nogc // Deprecation: asm statement cannot be @safe, use @trusted instead
{}
}
Asm instruction
AsmInstruction:
Identifier : AsmInstruction
align IntegerExpression
even
naked
db Operands
ds Operands
di Operands
dl Operands
df Operands
dd Operands
de Operands
db StringLiteral
ds StringLiteral
di StringLiteral
dl StringLiteral
dw StringLiteral
dq StringLiteral
Opcode
Opcode Operands
Opcode:
Identifier
int
in
out
Operands:
Operand
Operand , Operands
Labels
어셈블러 명령은 다른 문장처럼 레이블을 붙일 수 있어요. goto 문장의 대상이 될 수 있어요. 예를 들어:
void *pc;
asm
{
call L1 ;
L1: ;
pop EBX ;
mov pc[EBP],EBX ; // pc now points to code at L1
}
align IntegerExpression
IntegerExpression:
IntegerLiteral
Identifier
어셈블러가 다음 어셈블러 명령을 IntegerExpression 경계에 정렬하도록 NOP 명령을 내보내게 해요. IntegerExpression은 컴파일 타임에 2의 거듭제곱인 정수로 평가되어야 해요.
루프 본문의 시작을 정렬하는 것은 실행 속도에 극적인 영향을 줄 때가 있어요.
even
어셈블러가 다음 어셈블러 명령을 짝수 경계에 정렬하도록 NOP 명령을 내보내게 해요.
naked
컴파일러가 함수 프롤로그와 에필로그 시퀀스를 생성하지 않게 해요. 즉, 그 책임이 인라인 어셈블리 프로그래머에게 있다는 뜻이며, 보통 함수 전체를 어셈블러로 작성할 때 사용돼요.
db, ds, di, dl, df, dd, de
이 의사 연산(pseudo op)들은 코드에 원시 데이터를 직접 삽입하기 위한 것이에요. db는 바이트, ds는 16비트 워드, di는 32비트 워드, dl은 64비트 워드, df는 32비트 float, dd는 64비트 double, de는 80비트 확장 real용이에요. 각각 여러 피연산자를 가질 수 있어요. 피연산자가 문자열 리터럴이면, 문자열의 문자 수가 피연산자 수인 것처럼 취급돼요. 각 피연산자당 한 문자가 사용돼요. 예를 들어:
asm
{
db 5,6,0x83; // insert bytes 0x05, 0x06, and 0x83 into code
ds 0x1234; // insert bytes 0x34, 0x12
di 0x1234; // insert bytes 0x34, 0x12, 0x00, 0x00
dl 0x1234; // insert bytes 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
df 1.234; // insert float 1.234
dd 1.234; // insert double 1.234
de 1.234; // insert real 1.234
db "abc"; // insert bytes 0x61, 0x62, and 0x63
ds "abc"; // insert bytes 0x61, 0x00, 0x62, 0x00, 0x63, 0x00
}
Opcodes
지원되는 오퍼코드 목록은 끝에 있어요.
다음 레지스터들이 지원돼요. 레지스터 이름은 항상 대문자예요.
Register:
AL
AH
AX
EAX
BL
BH
BX
EBX
CL
CH
CX
ECX
DL
DH
DX
EDX
BP
EBP
SP
ESP
DI
EDI
SI
ESI
ES
CS
SS
DS
GS
FS
CR0
CR2
CR3
CR4
DR0
DR1
DR2
DR3
DR6
DR7
TR3
TR4
TR5
TR6
TR7
ST
ST(0)
ST(1)
ST(2)
ST(3)
ST(4)
ST(5)
ST(6)
ST(7)
MM0
MM1
MM2
MM3
MM4
MM5
MM6
MM7
XMM0
XMM1
XMM2
XMM3
XMM4
XMM5
XMM6
XMM7
x86_64는 이 추가 레지스터들을 더해요.
Register64:
RAX
RBX
RCX
RDX
BPL
RBP
SPL
RSP
DIL
RDI
SIL
RSI
R8B
R8W
R8D
R8
R9B
R9W
R9D
R9
R10B
R10W
R10D
R10
R11B
R11W
R11D
R11
R12B
R12W
R12D
R12
R13B
R13W
R13D
R13
R14B
R14W
R14D
R14
R15B
R15W
R15D
R15
XMM8
XMM9
XMM10
XMM11
XMM12
XMM13
XMM14
XMM15
YMM0
YMM1
YMM2
YMM3
YMM4
YMM5
YMM6
YMM7
YMM8
YMM9
YMM10
YMM11
YMM12
YMM13
YMM14
YMM15
Special Cases
lock, rep, repe, repne, repnz, repz
이 접두 명령들은 접두하는 명령과 같은 문장에 나타나지 않고, 자신들의 문장에 나타나요. 예를 들어:
asm
{
rep ;
movsb ;
}
pause
이 오퍼코드는 어셈블러가 지원하지 않아요. 대신 사용하세요:
asm
{
rep ;
nop ;
}
위는 같은 결과를 만들어요.
floating point ops
명령 형식의 두 피연산자 형태를 사용하세요:
fdiv ST(1); // wrong
fmul ST; // wrong
fdiv ST,ST(1); // right
fmul ST,ST(0); // right
Operands
Operand:
AsmExp
AsmExp:
AsmLogOrExp
AsmLogOrExp ? AsmExp : AsmExp
AsmLogOrExp:
AsmLogAndExp
AsmLogOrExp || AsmLogAndExp
AsmLogAndExp:
AsmOrExp
AsmLogAndExp && AsmOrExp
AsmOrExp:
AsmXorExp
AsmOrExp | AsmXorExp
AsmXorExp:
AsmAndExp
AsmXorExp ^ AsmAndExp
AsmAndExp:
AsmEqualExp
AsmAndExp & AsmEqualExp
AsmEqualExp:
AsmRelExp
AsmEqualExp == AsmRelExp
AsmEqualExp != AsmRelExp
AsmRelExp:
AsmShiftExp
AsmRelExp < AsmShiftExp
AsmRelExp <= AsmShiftExp
AsmRelExp > AsmShiftExp
AsmRelExp >= AsmShiftExp
AsmShiftExp:
AsmAddExp
AsmShiftExp << AsmAddExp
AsmShiftExp >> AsmAddExp
AsmShiftExp >>> AsmAddExp
AsmAddExp:
AsmMulExp
AsmAddExp + AsmMulExp
AsmAddExp - AsmMulExp
AsmMulExp:
AsmBrExp
AsmMulExp * AsmBrExp
AsmMulExp / AsmBrExp
AsmMulExp % AsmBrExp
AsmBrExp:
AsmUnaExp
AsmBrExp [ AsmExp ]
AsmUnaExp:
AsmTypePrefix AsmExp
offsetof AsmExp
seg AsmExp
+ AsmUnaExp
- AsmUnaExp
! AsmUnaExp
~ AsmUnaExp
AsmPrimaryExp
AsmPrimaryExp:
IntegerLiteral
FloatLiteral
__LOCAL_SIZE
$
Register
Register : AsmExp
Register64
Register64 : AsmExp
DotIdentifier
this
DotIdentifier:
Identifier
Identifier . DotIdentifier
FundamentalType . Identifier
피연산자 문법은 대체로 Intel CPU 문서 규약을 따르는 편이에요. 특히 두 피연산자 명령에서 소스는 오른쪽 피연산자이고 목적지는 왼쪽 피연산자라는 규약이에요. 이 문법은 D 언어 토크나이저와 호환되고 파싱을 단순화하기 위해 Intel의 문법과는 조금 달라요.
seg는 심볼이 있는 세그먼트 번호를 로드한다는 뜻이에요. 이는 플랫 모델 코드에서는 관련이 없어요. 대신 관련 세그먼트 레지스터에서 이동(move)을 하세요.
점으로 구분된 표현식(dotted expression)은 컴파일 동안 평가되며, 상수를 주거나 대상 레지스터·변수에 맞는 더 높은 수준의 변수를 나타내야 해요.
Operand Types
AsmTypePrefix:
near ptr
far ptr
word ptr
dword ptr
qword ptr
FundamentalType ptr
피연산자 크기가 모호한 경우, 다음과 같이:
add [EAX],3 ;
AsmTypePrefix로 명확히 할 수 있어요:
add byte ptr [EAX],3 ;
add int ptr [EAX],7 ;
far ptr은 플랫 모델 코드에서는 관련이 없어요.
Struct/Union/Class Member Offsets
애그리게이트에 대한 포인터가 레지스터에 있을 때 애그리게이트의 멤버에 접근하려면, 멤버의 정규화된 이름의 .offsetof 속성을 사용하세요:
struct Foo { int a,b,c; }
int bar(Foo *f)
{
asm
{
mov EBX,f ;
mov EAX,Foo.b.offsetof[EBX] ;
}
}
void main()
{
Foo f = Foo(0, 2, 0);
assert(bar(&f) == 2);
}
또는 애그리게이트의 스코프 안에서는 멤버 이름만 필요해요:
struct Foo // or class
{
int a,b,c;
int bar()
{
asm
{
mov EBX, this ;
mov EAX, b[EBX] ;
}
}
}
void main()
{
Foo f = Foo(0, 2, 0);
assert(f.bar() == 2);
}
Stack Variables
스택 변수(함수에 지역이고 스택에 할당된 변수)는 EBP로 인덱스된 변수 이름으로 접근해요:
int foo(int x)
{
asm
{
mov EAX,x[EBP] ; // loads value of parameter x into EAX
mov EAX,x ; // does the same thing
}
}
[EBP]를 생략하면 지역 변수에는 가정돼요. naked를 사용하면 이는 더는 성립하지 않아요.
Special Symbols
$
다음 명령의 시작의 프로그램 카운터를 나타내요. 그래서:
jmp $ ;
는 jmp 명령 다음에 오는 명령으로 분기해요. $는 jmp 또는 call 명령의 대상으로만 나타날 수 있어요.
__LOCAL_SIZE
이것은 지역 스택 프레임의 지역 바이트 수로 대체돼요. naked가 호출되고 사용자 정의 스택 프레임을 프로그래밍할 때 가장 유용해요.
Opcodes Supported
| aaa | aad | aam | aas | adc |
|---|---|---|---|---|
| add | addpd | addps | addsd | addss |
| and | andnpd | andnps | andpd | andps |
| arpl | bound | bsf | bsr | bswap |
| bt | btc | btr | bts | call |
| cbw | cdq | clc | cld | clflush |
| cli | clts | cmc | cmova | cmovae |
| cmovb | cmovbe | cmovc | cmove | cmovg |
| cmovge | cmovl | cmovle | cmovna | cmovnae |
| cmovnb | cmovnbe | cmovnc | cmovne | cmovng |
| cmovnge | cmovnl | cmovnle | cmovno | cmovnp |
| cmovns | cmovnz | cmovo | cmovp | cmovpe |
| cmovpo | cmovs | cmovz | cmp | cmppd |
| cmpps | cmps | cmpsb | cmpsd | cmpss |
| cmpsw | cmpxchg | cmpxchg8b | cmpxchg16b | |
| comisd | comiss | |||
| cpuid | cvtdq2pd | cvtdq2ps | cvtpd2dq | cvtpd2pi |
| cvtpd2ps | cvtpi2pd | cvtpi2ps | cvtps2dq | cvtps2pd |
| cvtps2pi | cvtsd2si | cvtsd2ss | cvtsi2sd | cvtsi2ss |
| cvtss2sd | cvtss2si | cvttpd2dq | cvttpd2pi | cvttps2dq |
| cvttps2pi | cvttsd2si | cvttss2si | cwd | cwde |
| da | daa | das | db | dd |
| de | dec | df | di | div |
| divpd | divps | divsd | divss | dl |
| dq | ds | dt | dw | emms |
| enter | f2xm1 | fabs | fadd | faddp |
| fbld | fbstp | fchs | fclex | fcmovb |
| fcmovbe | fcmove | fcmovnb | fcmovnbe | fcmovne |
| fcmovnu | fcmovu | fcom | fcomi | fcomip |
| fcomp | fcompp | fcos | fdecstp | fdisi |
| fdiv | fdivp | fdivr | fdivrp | feni |
| ffree | fiadd | ficom | ficomp | fidiv |
| fidivr | fild | fimul | fincstp | finit |
| fist | fistp | fisub | fisubr | fld |
| fld1 | fldcw | fldenv | fldl2e | fldl2t |
| fldlg2 | fldln2 | fldpi | fldz | fmul |
| fmulp | fnclex | fndisi | fneni | fninit |
| fnop | fnsave | fnstcw | fnstenv | fnstsw |
| fpatan | fprem | fprem1 | fptan | frndint |
| frstor | fsave | fscale | fsetpm | fsin |
| fsincos | fsqrt | fst | fstcw | fstenv |
| fstp | fstsw | fsub | fsubp | fsubr |
| fsubrp | ftst | fucom | fucomi | fucomip |
| fucomp | fucompp | fwait | fxam | fxch |
| fxrstor | fxsave | fxtract | fyl2x | fyl2xp1 |
| hlt | idiv | imul | in | inc |
| ins | insb | insd | insw | int |
| into | invd | invlpg | iret | iretd |
| iretq | ja | jae | jb | jbe |
| jc | jcxz | je | jecxz | jg |
| jge | jl | jle | jmp | jna |
| jnae | jnb | jnbe | jnc | jne |
| jng | jnge | jnl | jnle | jno |
| jnp | jns | jnz | jo | jp |
| jpe | jpo | js | jz | lahf |
| lar | ldmxcsr | lds | lea | leave |
| les | lfence | lfs | lgdt | lgs |
| lidt | lldt | lmsw | lock | lods |
| lodsb | lodsd | lodsw | loop | loope |
| loopne | loopnz | loopz | lsl | lss |
| ltr | maskmovdqu | maskmovq | maxpd | maxps |
| maxsd | maxss | mfence | minpd | minps |
| minsd | minss | mov | movapd | movaps |
| movd | movdq2q | movdqa | movdqu | movhlps |
| movhpd | movhps | movlhps | movlpd | movlps |
| movmskpd | movmskps | movntdq | movnti | movntpd |
| movntps | movntq | movq | movq2dq | movs |
| movsb | movsd | movss | movsw | movsx |
| movupd | movups | movzx | mul | mulpd |
| mulps | mulsd | mulss | neg | nop |
| not | or | orpd | orps | out |
| outs | outsb | outsd | outsw | packssdw |
| packsswb | packuswb | paddb | paddd | paddq |
| paddsb | paddsw | paddusb | paddusw | paddw |
| pand | pandn | pavgb | pavgw | pcmpeqb |
| pcmpeqd | pcmpeqw | pcmpgtb | pcmpgtd | pcmpgtw |
| pextrw | pinsrw | pmaddwd | pmaxsw | pmaxub |
| pminsw | pminub | pmovmskb | pmulhuw | pmulhw |
| pmullw | pmuludq | pop | popa | popad |
| popf | popfd | por | prefetchnta | prefetcht0 |
| prefetcht1 | prefetcht2 | psadbw | pshufd | pshufhw |
| pshuflw | pshufw | pslld | pslldq | psllq |
| psllw | psrad | psraw | psrld | psrldq |
| psrlq | psrlw | psubb | psubd | psubq |
| psubsb | psubsw | psubusb | psubusw | psubw |
| punpckhbw | punpckhdq | punpckhqdq | punpckhwd | punpcklbw |
| punpckldq | punpcklqdq | punpcklwd | push | pusha |
| pushad | pushf | pushfd | pxor | rcl |
| rcpps | rcpss | rcr | rdmsr | rdpmc |
| rdtsc | rep | repe | repne | repnz |
| repz | ret | retf | rol | ror |
| rsm | rsqrtps | rsqrtss | sahf | sal |
| sar | sbb | scas | scasb | scasd |
| scasw | seta | setae | setb | setbe |
| setc | sete | setg | setge | setl |
| setle | setna | setnae | setnb | setnbe |
| setnc | setne | setng | setnge | setnl |
| setnle | setno | setnp | setns | setnz |
| seto | setp | setpe | setpo | sets |
| setz | sfence | sgdt | shl | shld |
| shr | shrd | shufpd | shufps | sidt |
| sldt | smsw | sqrtpd | sqrtps | sqrtsd |
| sqrtss | stc | std | sti | stmxcsr |
| stos | stosb | stosd | stosw | str |
| sub | subpd | subps | subsd | subss |
| syscall | sysenter | sysexit | sysret | test |
| ucomisd | ucomiss | ud2 | unpckhpd | unpckhps |
| unpcklpd | unpcklps | verr | verw | wait |
| wbinvd | wrmsr | xadd | xchg | xlat |
| xlatb | xor | xorpd | xorps |
Pentium 4 (Prescott) Opcodes Supported
| addsubpd | addsubps | fisttp | haddpd | haddps |
|---|---|---|---|---|
| hsubpd | hsubps | lddqu | monitor | movddup |
| movshdup | movsldup | mwait |
AMD Opcodes Supported
| pavgusb | pf2id | pfacc | pfadd | pfcmpeq |
|---|---|---|---|---|
| pfcmpge | pfcmpgt | pfmax | pfmin | pfmul |
| pfnacc | pfpnacc | pfrcp | pfrcpit1 | pfrcpit2 |
| pfrsqit1 | pfrsqrt | pfsub | pfsubr | pi2fd |
| pmulhrw | pswapd |
SIMD
SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2와 AVX가 지원돼요.
GCC syntax
GNU D Compiler는 인라인 어셈블러에 GCC 기반의 대안적 문법을 사용해요:
GccAsmStatement:
asm FunctionAttributesopt { GccAsmInstructionList }
GccAsmInstructionList:
GccAsmInstruction ;
GccAsmInstruction ; GccAsmInstructionList
GccAsmInstruction:
GccBasicAsmInstruction
GccExtAsmInstruction
GccGotoAsmInstruction
GccBasicAsmInstruction:
GccAsmStringExpression
GccExtAsmInstruction:
GccAsmStringExpression : GccAsmOperandsopt
GccAsmStringExpression : GccAsmOperandsopt : GccAsmOperandsopt
GccAsmStringExpression : GccAsmOperandsopt : GccAsmOperandsopt : GccAsmClobbersopt
GccGotoAsmInstruction:
GccAsmStringExpression : : GccAsmOperandsopt : GccAsmClobbersopt : GccAsmGotoLabelsopt
GccAsmStringExpression:
StringLiteral
( ConditionalExpression )
GccAsmOperands:
GccSymbolicNameopt GccAsmStringExpression ( AssignExpression )
GccSymbolicNameopt GccAsmStringExpression ( AssignExpression ) , GccAsmOperands
GccSymbolicName:
[ Identifier ]
GccAsmClobbers:
GccAsmStringExpression
GccAsmStringExpression , GccAsmClobbers
GccAsmGotoLabels:
Identifier
Identifier , GccAsmGotoLabels