The Beautiful Future
어셈작성법 구조 본문
http://jake.dothome.co.kr/inline-assembly/
https://wiki.kldp.org/KoreanDoc/html/EmbeddedKernel-KLDP/app3.basic.html
** 작성 구조
__asm__ __volatile__(
asms
: output
: input
: clobber
);
__asm__
인라인 어셈블러가 시작됨을 알려준다.
ANSI 표준에는 asm 이정의 되어있지 않아서 __asm__으로 해주는것이 좋다.
__volatile__
컴파일러 최적화를 하지 말아라는 의미이다.
컴파일러에 의해서 의도한바와 다르게 프로그램이 해석될수 있음을 방지한다.
asms
실제 어셈블러가 작성되는 란이다. 따옴표로 둘러싸서 작성한다.
%를 사용해서 input, output파라미터를 참조할 수 있다.
output
결과 값을 출력하는 변수
input
어셈블러에 넘겨주는 파라미터를 적는다.
%0, %1등을 사용해 input, output 오퍼랜드를 나타낸다.
output에서 부터 시작해 input에 나열된 변수들의 순서 대로 %0, %1, ... 으로 번호가 매겨진다.
clobber
output, input에 명시되어있지 않지만 값이 변하는 것들을 젂는다.
ex)
static __inline__ int test_and_set_bit(int nr, volatile void * addr)
{
int oldbit;
__asm__ __volatile__( LOCK_PREFIX
"btsl %2,%1\n\t //
sbbl %0,%0"
:"=r" (oldbit),"=m" (ADDR)
:"Ir" (nr)
: "memory");
return oldbit;
}
input/output 설정법
""로 감싸져 있는 부분을 constraint라고 하며 input/output 의
속성을 설정할 수 있다. Arm Family Constraints는 아래와 같다.
f: floating point register
F: 0.0, 0.5, 1.0, 2.0, 3.0, 4.0, 5.0, 10.0 중의 하나를 나타내는 플로팅 포인트 상수
G: 음수인경우 F
I: 0~255 사이에서 2의 배수 값을 나타낸다.
J: -4095~4095 사이 정수
K: I를 만족하는 값을 1의 보수를 취한것
L: I를 만족하는 값을 음수로 취한 값(2의 보수)
M: 0~32 사이의 정수 값
Q: 한 레지스터에 담겨잇는 정확한 어드레스 메모리
R: constant pool 내에 담겨있는 아이템
S: 현재 파일의 텍스트 세그먼트 내의 심볼
strcpy 예제
static inline char * strcpy( char * dest, const char *src)
{
int d0, d1, d2;
__asm__ __volatile__(
"1:\tlodsb\n\t" // 1:은 레이블, al 레시스터에 es:esi의 내용을 읽어오고 자동 1증가
"stosb\n\t" // al의 값을 es:edi에 저장하고 자동 1증가
"testb %%al,%%al\n\t" // al의 내용이 0인지 테스트
"jne 1b" // 0이 아닌경우 1로 점프
: "=&S" (d0), "=&D" (d1), "=&a" (d2)
:"0" (src),"1" (dest)
: "memory");
return dest;
}
output
"=&S" (d0) : d0를 si 레지스터에 저장
"=&D" (d1) : d1을 di 레지스터에 저장
"=&a" (d2) : d2를 a 레지스터에 저장
이 output 설정은 어셈코드 종료후 값을 d0~2 값을 si, di, a 레지스터에 저장
기계어로 아래와 같이 재구성됨.
movl %esi,%ecx
movl %edi,%edx
movl %ecx,%ebx
input
"0" (src): src가 0번째 오퍼랜드인 d0과 같다라는 의미
위에서 d0는 si 레지스터에 할당되어있으니 si레지스터는 src로 초기화 된다.
"1" (dest): di 레지스터는 dest로 초기화 된다.
movl 12(%ebp),%esi
movl 8(%ebp),%edi
memory
: "memory" : 어셈에세 변경되는 메모리를 알려줌, 어셈 로드전/후에 레지스터에 저장
되어 있는 모든 변수 값을 갱신한다.
인라인 어셈블리의 문법은 다음과 같으며, ARM 아키텍처를 위주로 설명한다.
asm [volatile] ( AssemblerTemplate : OutputOperands [ : InputOperands [ : Clobbers ] ]) asm [volatile] goto ( AssemblerTemplate : : InputOperands : Clobbers : GotoLabels)
Qualifiers
volatile
- gcc는 성능 향상(optimization)을 목적으로 상황에 따라 명령을 무시하거나 명령 위치를 변경할 수 있는데 volatile을 사용하면 이러한 optimization을 하지 않게 한다.
- 참고: Volatile | 문c
goto
- 어셈블리 코드안에서 C에서 사용하는 라벨로 점프할 수 있게 한다.
- 이 때 OutputOperands는 사용할 수 없다.
Parameters(파라메터)
AssemblerTemplate(코드)
- 어셈블리 코드 문자열
- input/output 오퍼랜드와 %l(goto) 파라메터와 조합하여 사용한다.
- 예) ARM
- “mov r0, r0”
- “mov %0, #10”
- “ldr %0, [%1]”
- “bne %l[err1]”
- 어셈블리 명령 끝 처리
- “\n” 또는 “\n\t”을 사용하여 여러 개의 명령을 사용한다.
- 세미콜론(;)을 사용하여 명령을 구분할 수도 있는데 컴파일러에 따라 다르므로 “\n\t” 등이 권장된다.
- 예) ARM
- “mov r0, r0\n\t” “mov r1, r1”
- “mov r0, r0; mov r1, r1;”
- “%%”
- “%” Input/Output 오퍼랜드에서 사용하는 “%”와 혼동을 피하기 위하여 어셈블리 코드내에 x86 어셈블리와 같이 “%” 문자열을 사용해야 하는 경우 사용한다.
- 예) x86
- “movl %%eax, %0”
- Input/Output Operands 사용
- %n: n번째 인수에 매핑된 arm 32bit 레지스터를 지정한다.
- %Qn: n번째 64비트 인수 중 하위 비트에 매핑된 ARM 32bit 레지스터를 지정한다.
- %Rn: n번째 64비트 인수 중 상위 비트에 매핑된 ARM 32bit 레지스터를 지정한다.
- %Hn: n번째 64비트 인수 중 매핑된 2 개의 ARM 32bit 레지스터 중 레지스터 번호가 높은 레지스터를 지정한다.
- 예) ARM
- “mov %0, #10”
- “mov %Q0, %1, %2”
- “mov %R0, %R0, #0”
- “ldrd %H0, [%1]”
Input Operands(입력 인수) & Output Operands(출력 인수)
형식: [ [asmSymbolicName] ] constraint (C-expression)
- AssemblerTemplate(code)에 있는 명령에 의해 C 변수들에서(로) 입력/출력된다.
- 빈 항목도 허용한다.
- [ [asmSymbolicName] ]
- 생략 가능하고 지정하는 경우 %0, %1, …과 같이 인수의 순번을 사용하는 대신 심볼명을 사용할 수도 있다.
- 예) ARM
- asm (“add %[tmp], #2” : [tmp] “=r” (tmp));
- constraint
- 자주 사용하는 constraint 항목
- “r”
- C-expression을 범용 레지스터에 배정한다.
- “I”~”P”
- Immediate 위치로 C-expression이 상수이어야 한다.
- 아키텍처마다 상수 표현 크기가 다르다.
- ARM:
- “I”: 0~255 값을 2의 차수(2^N) 단위로 만들 수 있는 상수 값
- 예) 0x81(o), 0x8100_0000(o), 0x101
- “J”: -4095~4095 범위의 상수
- “I”: 0~255 값을 2의 차수(2^N) 단위로 만들 수 있는 상수 값
- “m”
- C-expression이 유효 메모리 주소이어야 한다.
- “[digits]”
- Input Operands에 사용되며 Output Operands의 순서와 똑같은 항목을 지칭한다. 이렇게 하면 컴파일러의 optimization이 Output Operands의 값이 수정된 것 처럼 속인다.
- 예) __asm__ (““ : “=r“(__ptr) : “0“(ptr));
- Input Operands에 사용되며 Output Operands의 순서와 똑같은 항목을 지칭한다. 이렇게 하면 컴파일러의 optimization이 Output Operands의 값이 수정된 것 처럼 속인다.
- “r”
- 메모리 access용 clobber를 지정할 수 있는 constraint 항목
- “Q”, “Qo”
- ARM clobber for memory
- C-expression은 단일 레지스터에서 유효한 메모리 레퍼런스 주소이다.
- gcc의 ARM용 clobber for memory로 input/output 오퍼랜드에서 메모리 access를 위해 사용한다.
- ARM에서는 메모리 영역을 access 할 경우 clobber lists에서 “memory” 대신 “Q”를 사용한다.
- “Qo”: optimization이 추가되어 코드가 일부 생략된다.
- 보통 메모리 주소를 가리키는 레지스터 즉 “r”레지스터가 별도로 사용되면서 “r”과 “Q”에 대해 각각의 레퍼런스를 계산하기 위한 코드가 사용된다. 만일 “r”과 “Q”에서 사용되는 메모리 주소가 서로 같은 곳을 보는 경우 “Qo”를 사용하면 한 번의 계산을 생략할 수 있다.
- 예) 아래와 같이 v->counter의 위치가 서로 같은 경우 “Qo”를 사용하여 코드를 절약할 수 있다.
- asm (“ldrd %0, %H0, [%1]” : “=&r” (result) : “r” (&v->counter), “Qo” (v->counter);
- “Q”, “Qo”
- constraint modifiers
- “=”
- OutputOperands에서 쓰기(write only)만 가능하다.
- “+”
- OutputOperands에서 읽고(read) 쓰기(write)가 가능하다.
- “&”
- early clobber modifier
- OutputOperands에서 레지스터 할당 순서를 먼저 할 수 있도록 요청한다.
- 보통 input operands에 레지스터를 할당하고 그 후 output operands의 레지스터를 사용하기 때문에 input operands에서 사용했던 레지스터를 output operands 레지스터로 배치하는 경우도 생기는데 그러면서 문제가 될 수 있는 곳에 “&”를 사용한다.
- 보통 Output operands에 “&”를 사용하여 먼저 하나의 레지스터를 할당받아 사용하면서 다른 레지스터로 사용될 일을 막는다.
- “=”
- 사용 예)
- “=r”
- 쓰기만 하는 목적으로 해당 C-expression을 범용 레지스터에 배정한다.
- “+rm”
- 메모리 주소를 읽고 쓰는 목적으로 해당 C-expression을 범용 레지스터에 배정한다.
- “Ir”
- immediate 오퍼랜드 위치에서 사용하기 위하여 해당 C-expression을 범용 레지스터에 배정한다.
- “=&r”
- 쓰기만 하는 목적으로 해당 C-expression을 범용 레지스터에 먼저(early) 배정한다.
- “+r”
- 읽고 쓰는 목적으로 C-expression을 범용 레지스터에 배정한다.
- “+Qo”
- 읽고 쓰는 목적으로 C-expression을 메모리에 배정한다. (ARM clobber for memory)
- “=r”
- 자주 사용하는 constraint 항목
- (C-expression)
- C 표현이 가능하다.
- 예)
- (var+10)
- (*var)
- (&var)
Clobbers
- AssemblerTemplate에 의해 변경되는 레지스터나 값들이다.
- 즉 InputOperands 와 OutputOperands가 C로 부터 영향을 받거나 주는 경우를 지정하였지만 Clobbers는 어셈블리 코드에서 영향을 주는 것을 의미한다.
- “cc”
- 플래그 레지스터를 수정할 수 있다.
- “memory”
- 메모리 주소를 변경시킬 수 있다.
- input/output operands에서 “Q” 또는 “Qo”를 사용하여 해당 항목에 사용할 수 있다. (최신 방법)
- 예) “r9”, “cc”, “memory”
- 어셈블리 코드로부터 r9 레지스터, 플래그 레지스터, 메모리가 수정되는 경우
Goto Labels
- 라벨로 점프를 하는 기능이며, 이 기능을 사용할 경우 OutputOperands를 사용할 수 없으므로 비워둬야 한다.
goto.c
01 | #include <stdio.h> |
02 |
03 | int sub( int cnt) |
04 | { |
05 | int x = 0; |
06 | asm goto ( "mov %0, %1\n\t" |
07 | "cmp %0, #10\n\t" |
08 | "bhi %l[err2]\n\t" |
09 | "1: subs %0, #1\n\t" |
10 | "bne 1b" |
11 | : |
12 | : "r" (x), "r" (cnt) |
13 | : "cc" |
14 | : err2); |
15 | printf ( "cnt=%d\n" , cnt); |
16 | return x; |
17 | err2: |
18 | printf ( "err: cnt=%d\n" , cnt); |
19 | return x; |
20 | } |
21 |
22 | int main() |
23 | { |
24 | sub(5); |
25 | sub(15); |
26 | } |
27 |
28 | $ ./ goto |
29 | cnt=5 |
30 | err: cnt=15 |
기타
Clobber for Memory (“Q”)
- Q”는 “memory”를 대신하여 사용되는 오퍼랜드 항목의 clobber for memory 이다.
qo.c
01 | #include <stdio.h> |
02 |
03 | int loop5( int * addr) |
04 | { |
05 | int i; |
06 | int tmp = 0; |
07 |
08 | for (i = 0; i < 10; i++) |
09 | { |
10 | asm ( "add %0, #2\n str %0, [%2]" |
11 | : "=r" (tmp), "=Qo" (*addr) |
12 | : "r" (addr)); |
13 | } |
14 | return tmp; |
15 | } |
16 |
17 | int loop6( int * addr) |
18 | { |
19 | int i; |
20 | int tmp = 0; |
21 |
22 | for (i = 0; i < 10; i++) |
23 | { |
24 | asm volatile ( "add %0, #2\n str %0, [%2]" |
25 | : "=r" (tmp), "=Qo" (*addr) |
26 | : "r" (addr)); |
27 | } |
28 | return tmp; |
29 | } |
30 |
31 | int main() |
32 | { |
33 | int l5 = 1; |
34 | int l6 = 1; |
35 |
36 | loop5(&l5); |
37 | loop6(&l6); |
38 | } |
- 위의 소스와 같이 “memory” clobber를 사용하지 않고 “Qo”를 사용하여 구현한 예이다.
- 메모리에 읽거나 쓰는 데이터는 레지스터가 아닌 값을 의미하므로 addr가 아닌 *addr이 된다.
- input operands에 사용된 addr은 읽기 용도로 변경되지 않으며 output operands에 사용된 *addr 값이 메모리에 기록되는 값이므로 “Qo” 앞에 기록 전용의 modifier인 “=”을 붙인다.
참고
- ARM inline asm secrets
- Extended Asm – Assembler Instructions with C Expression Operands | gnu.org
- ARM GCC Inline Assembler Cookbook
- Inline assembly, GCC.txt | dweinstein
- Using the GNU Compiler Collection (v5.3.0) | gnu.org
'arm assembly' 카테고리의 다른 글
명령어 정리 (0) | 2019.05.18 |
---|---|
ARM GCC Inline Assembler Cookbook (0) | 2019.03.10 |
cross product 분석 (0) | 2019.03.09 |
NEON Register 구조 (0) | 2019.03.08 |