인라인 어셈블러
Inline assembler컴퓨터 프로그래밍에서 인라인 어셈블러는 어셈블리 언어로 작성된 하위 수준의 코드를 프로그램 내에 포함시킬 수 있도록 하는 컴파일러의 기능입니다.이러한 코드는 C나 Ada와 같은 상위 수준의 언어에서 컴파일된 코드입니다.
모티베이션과 대안
어셈블리 언어 코드 삽입은 보통 다음 [1]중 하나의 이유로 이루어집니다.
- 최적화: 프로그래머는 어셈블리 언어 코드를 사용하여 프로그램 알고리즘의 가장 성능에 민감한 부분을 구현할 수 있습니다.이 코드는 컴파일러에 의해 생성될 수 있는 것보다 더 효율적입니다.
- 프로세서 고유의 순서에의 액세스:대부분의 프로세서는 세마포어 또는 기타 동기 및 잠금 프리미티브 구축에 사용할 수 있는 Compare and Swap 명령 및 Test and Set 명령과 같은 특별한 명령을 제공합니다.거의 모든 최신 프로세서에는 멀티태스킹 구현에 필요한 명령어가 포함되어 있습니다.특수 명령의 예는 SPARC VIS, 인텔 MMX 및 SSE, Motorola Altivec 명령 세트에 있습니다.
- 컴파일러가 아직 지원하지 않는 특수한 호출 규칙에 대한 액세스.
- 시스템 호출 및 인터럽트:고급 언어에는 임의의 시스템 호출을 직접 실행하는 기능이 거의 없기 때문에 어셈블리 코드가 사용됩니다.직접 인터럽트는 더욱 드물게 제공됩니다.
- 링커 또는 어셈블러에 대한 특별한 지시(예: 섹션 분할, 매크로 변경 또는 기호 별칭 만들기)를 내보냅니다.
한편, 인라인 어셈블러는 레지스터 [2]할당의 핵심 부분인 각 변수에 대해 수행되는 작업을 복잡하게 만들기 때문에 컴파일러 자체에 직접적인 문제를 일으킵니다.즉, 실제로 성능이 저하될 수 있습니다.인라인 어셈블러는 또한 미래의 프로그램 [1]이식 및 유지보수를 복잡하게 합니다.
컴파일러와 프로그래머의 작업을 단순화하는 방법으로 대체 설비가 제공되는 경우가 많습니다.대부분의 컴파일러는 특수 명령어를 위한 고유 함수를 제공하며 임의의 시스템 호출을 위한 C 함수 래퍼를 모든 Unix 플랫폼에서 사용할 수 있습니다.
구문
언어 기준
ISO C++ 표준 및 ISO C 표준(부속문서 J)은 인라인 어셈블러에 대해 조건부로 지원되는 구문을 지정합니다.
asm 선언에는 다음과 같은 형식이 있습니다.
asm-communications:
asm(문자열 구분);
asm 선언은 조건부로 지원되며 그 의미는 구현 [3]정의입니다.
단, 이 정의는 (해석에서는) 너무 자유롭고 (1개의 문자열 리터럴만을 사용하는 경우) 너무 제한적이기 때문에 실제 C에서는 거의 사용되지 않습니다.
실제 컴파일러에서는
실제로 값에서 작동하는 인라인 어셈블리는 자유 부동 코드로 독립 실행형인 경우가 거의 없습니다.프로그래머는 변수가 어떤 레지스터에 할당되어 있는지 예측할 수 없기 때문에 컴파일러는 일반적으로 변수를 확장으로 대체할 수 있는 방법을 제공합니다.
일반적으로 C/C++ 컴파일러에 의해 지원되는 인라인어셈블리에는 다음 2종류가 있습니다.
- GCC에서는 asm(또는 __asm__)이 사용됩니다.GCC는 ISO 규칙의 직접 확장을 사용합니다.어셈블리 코드템플릿은 문자열로 작성되며 입력, 출력 및 클로버 레지스터는 콜론 문자열 뒤에 지정됩니다.레지스터 이름은 문자열 리터럴로 [4]따옴표로 묶는 동안 C 변수는 직접 사용됩니다.
- MSVC(Microsoft Visual C++), Borland/Embarcadero C 컴파일러 및 하위 컴파일러의 __asm.이 구문은 ISO 규칙에 전혀 근거하지 않습니다.프로그래머는 C 구문에 준거할 필요 없이 블록 내에 ASM을 쓸 뿐입니다.변수는 레지스터인 것처럼 사용할 수 있으며 일부 C 식이 [5]허용됩니다.ARM 컴파일러에도 같은 [6]기능이 있었습니다.
두 확장 제품군은 인라인 조립을 처리할 때 분업에 대한 서로 다른 이해를 나타냅니다.GCC 폼은 언어의 전체적인 구문을 보존하고 컴파일러가 알아야 할 것, 즉 무엇이 필요하고 무엇이 변경되었는지를 구분합니다.컴파일러는 입력 요건을 처리하기 위해 레지스터 할당과 몇 가지 이동 연산만 대체하면 되기 때문에 컴파일러가 명령 이름을 이해할 필요가 없습니다.단, 클로버된 레지스터를 잘못 지정할 수 있습니다.임베디드 도메인 고유의 언어의 MSVC 형식은 쓰기 쉽지만, 컴파일러 자신이 opcode name과 그 clobbering 속성에 대해 알아야 하기 때문에 유지보수 및 [7]포팅에 각별한 주의가 요구됩니다.명령어 [8]세트를 알고 있으면 GCC 스타일의 어셈블리에서 클로버 오류를 확인할 수 있습니다.
GNAT(GCC 스위트의 Ada 언어 프론트 엔드) 및 LLVM은 GCC [9][10]구문을 사용합니다.D 프로그래밍 언어는 x86_64에 대해 공식적으로 [11]MSVC 확장과 유사한 DSL을 사용하지만 LLVM 기반의 LDC는 모든 [12]아키텍처에서 GCC 스타일의 구문을 제공합니다.MSVC는 32비트x86에서만 인라인어셈블러를 지원합니다.[5]
이후 Rust 언어는 LLVM(GCC 스타일) 버전보다 더 멀리 인라인 어셈블리 옵션을 추상화하는 구문으로 마이그레이션되었습니다.백엔드에서 임베디드 [7]어셈블리를 처리할 수 없는 경우 블록을 외부에서 조립된 함수로 변환할 수 있는 충분한 정보를 제공합니다.
예
GCC에서의 시스템콜
일반적으로 보호된 메모리를 사용하는 시스템에서는 운영체제를 직접 호출할 수 없습니다.OS는 사용자(사용자 모드)보다 특권 수준(커널 모드)에서 실행됩니다.운영체제에 대한 요구에는 (소프트웨어) 인터럽트가 사용됩니다.이것은 상위 언어의 기능이 아닌 경우가 드물기 때문에 시스템콜 래퍼 함수는 인라인어셈블러를 사용하여 작성됩니다.
다음 C코드 예는 GNU 어셈블리를 사용한 AT&T 어셈블러 구문의 x86 시스템콜 래퍼입니다이러한 콜은 보통 매크로를 사용하여 작성되며, 알기 쉽게 하기 위해 완전한 코드가 포함되어 있습니다.이 경우 래퍼는 3개의 오퍼랜드가 있는 발신자가 지정한 번호의 시스템콜을 실행하여 [13]결과를 반환합니다.
요약하자면, GCC는 기본 어셈블리와 확장 어셈블리를 모두 지원합니다.전자는 단순히 문자 그대로 어셈블러에게 전달하고, 후자는 레지스터 [4]위치를 대신합니다.
외부 인트 에러; 인트 시스템 3(인트 숫자, 인트 arg1, 인트 arg2, 인트 arg3) { 인트 인식하다; __asm__ ( "0x80달러 투입" /* OS에 요청 */ : "=a" (인식하다), /* 결과를 eax("a")*/로 반환 "+b" (arg1), /* arg1 in ebx ("b") [시스템에 의해 변경될 수 있으므로 "+" 출력으로]*/ '+c' (arg2), /* ecx ("c")의 arg2를 통과합니다.[ditto] * / "+d" (arg3) /* edx ("d") [ditto] */의 arg3를 통과시킵니다. : "a" (숫자) /* 시스템 콜 번호를 eax("a")*/로 전달합니다. : '메모리', "cc", /* 컴파일러에 메모리와 상태 코드가 변경되었음을 알립니다*/ "esi", "edi", "ebp"); /* 이 레지스터들도 [시스템 콜에 의해 제어됩니다]*/ /* 운영체제는 오류 시 음의 값을 반환합니다. * wrappers는 오류 시 -1을 반환하고 errno 글로벌 변수를 설정합니다.* / 한다면 (-125 <=> 인식하다 & & 인식하다 < > 0) { 에러 = -인식하다; 인식하다 = -1; } 돌아가다 인식하다; }
x86_64와 같은 "syscall" 아키텍처에서는 syscall을 사용해야 합니다.
입력은 설명서에 미리 설명되어 있습니다.
returns 값은 아키텍처에 따라 2개의 레지스터에 있을 수 있습니다.
#실패하다 <errno.h> 긴 인트 시스템 3(서명되어 있지 않다 인트 숫자, 인트 arg1, 인트 arg2, 인트 arg3){ 인트 리타, 리트비; 긴 인트 리트; asm ( "시콜\n\t" :"=a"(리타),"=d"(리트비) :"a"(숫자),"d"(arg3),'D'(arg1),'S'(arg2) :"cc",'메모리' ); 리트=리타; /* 여기, 1개만 */ 한다면 (-125 <=> 리트 & & 리트 < > 0) { 에러 = -리트; 리트 = -1; } 돌아가다 리트; }
D의 프로세서 고유의 명령
이 D 프로그래밍 언어의 인라인 어셈블리의 예는 x86의 FPU(x87) 명령을 사용하여 x의 탄젠트를 계산하는 코드를 보여줍니다.
// x의 탄젠트 계산 진짜 햇볕에 그을 띠다(진짜 x) { asm { fld x[EBP] ; // x를 로드 fxam ; // 홀수볼 값 테스트 fstsw AX ; 하드웨어 ; jc 트리거 ; // C0 = 1: x는 NAN, 무한 또는 비어 있음 // 387은 데노멀을 처리할 수 있습니다. SC18: fptan ; fstp 세인트(0) ; // 덤프 X(항상 1) fstsw AX ; 하드웨어 ; // if ( ! ( fp_status & 0x20 )goto Lret jnp 레트 ; // C2 = 1: x가 범위를 벗어났습니다. 인수 축소를 수행합니다. fldpi ; // pi 로드 fxch ; SC17: 프리미어 1 ; // 리마인더(리마인더) fstsw AX ; 하드웨어 ; JP SC17 ; // C2 = 1: 부분 리마인더, 루프 필요 fstp 세인트(1) ; // 스택에서 pi 제거 jmp SC18 ; } 트리거: 돌아가다 진짜.나노; 레트: // 값이 이미 FP 스택에 있으므로 수동으로 아무것도 반환할 필요가 없습니다. ; }
x87 프로그래밍에 익숙하지 않은 독자는 x87 FPU 상태 워드 비트 C0 및 C2에 액세스하기 위해 fstw-sahf에 이어 조건부 점프 idi를 사용한다.fstw는 상태를 범용 레지스터에 저장하고, sahf는 FLAGS 레지스터를 레지스터의 상위8비트로 설정하고, 점프는 응답 플래그가 발생하는지를 판단하기 위해 사용된다.nd는 FPU 상태 [16]비트입니다.
레퍼런스
- ^ a b "DontUseInlineAsm". GCC Wiki. Retrieved 21 January 2020.
- ^ Striegel, Ben (13 January 2020). ""To a compiler, a blob of inline assembly is like a slap in the face."". Reddit. Retrieved 15 January 2020.
- ^ C++, [DCL.asm]
- ^ a b "Extended Asm - Assembler Instructions with C Expression Operands". Using the GNU C Compiler. Retrieved 15 January 2020.
- ^ a b "Inline Assembler". docs.microsoft.com.
- ^ "Migration and Compatibility Guide: Inline assembly with Arm Compiler 6".
- ^ a b d'Antras, Amanieu (13 December 2019). "Rust RFC-2873: stable inline asm". Retrieved 15 January 2020.
However it is possible to implement support for inline assembly without support from the compiler backend by using an external assembler instead.
상태 추적용 풀 요청 - ^ "⚙ D54891 [RFC] Checking inline assembly for validity". reviews.llvm.org.
- ^ "LLVM Language Reference: Inline assembly expressions". LLVM Documentation. Retrieved 15 January 2020.
- ^ "Inline Assembly". Rust Documentation (1.0.0). Retrieved 15 January 2020.
- ^ "Inline Assembler". D programming language. Retrieved 15 January 2020.
- ^ "LDC inline assembly expressions". D Wiki. Retrieved 15 January 2020.
- ^ Linux 프로그래머 매뉴얼– 시스템 콜 –
- ^ "Linux System Call Table for x86 64 · Ryan A. Chapman".
- ^ "Syscall(2) - Linux manual page".
- ^ "FSTSW/FNSTSW — Store x87 FPU Status Word".
The FNSTSW AX form of the instruction is used primarily in conditional branching...