※ processor 상태

실행되는 cpu의 상태정보를 register에 저장한다.

이때, test 결과를 의미하는 code를 Condition code라 부른다.

 

§ Condition Codes

- 모두 1bit로 0이나 1만 저장되며 flag register라고도 불린다.

- Condition code를 setting, 즉 flag register 값을 바꾸는 방법은 다음과 같다.

 

1. 산술연산 [간접 setting] (단, 이때 leaq명령어로는 값이 바뀌지는 않는다.)

- ex)  addq  Src, Dest  ↔  t = a + b

 

 

2. 비교 명령어 [직접 setting]

cmpq  Src2,  Src1   로 표기하며 이때, Src1 - Src2를 계산하여 비교한다.

(단, Src1 - Src2의 결과를 저장하지는 않는다.)

 

 

3. test 명령어 [직접 setting]

testq  Src2,  Src1   로 표기하며 이때, Src1 & Src2 연산을 진행하여 비교한다.

(단, Src1 & Src2의 결과를 저장하지는 않는다.)

 

 

§ Condition Codes값 읽어오기  (feat.  SetX 명령)

§  SetX  ByteReg 

- cmp 명령 실행 후에 적용된다.

- ByteReg의 하위 byte를  condition code (flag register)의  조합 X에 따라  0이나 1로 설정

- 이때, quad와 같은 경우 ByteReg의 나머지 7 byte는 변경이 없다.

즉, %rsp를 예로 들면 최하위 1byte인 %spl을 이용해 0이나 1로 설정한다.

Q. quad를 예로 들면, 그렇다면 1byte를 제외한 나머지 7byte는 어떻게 해야할까?
A. movzbl을 이용해 mov를 이용해 copy 후 나머지 나머지 7byte는 0으로 padding 해준다.

 

Ex. C code  ->  assembly

int gt (long x, long y) { 
    return x > y;
}

 

 

 

 

 

 

 

※ 점프 (jump)

§  jX  Label 

이때, Label은 assembly에서 위치를 나타내며 특정 label로 jump해서 fetch하게 해주는 것이다.

- 조건코드에 따라서 코드의 실행위치를 이동한다.

 

§ 조건부 분기예제  (Old Style)

long absdiff (long x, long y) {
    long result;
    if (x > y)
        result = x - y;
    else
        result = y - x;
    return result;
}

 

 

§ 조건부 분기예제  (Go to 코드 Style)

 

 

§ 조건부 이동명령  (cmovX)

3항 연산자와 같은 한문장으로 표현 가능한 if-else문에서 사용하는데, 위의 go-to보다 더욱 효율적이다.

- 분기문은 pipe라인의 instruction흐름을 매우 방해하지

- 조건부 이동명령은 제어의 이동이 필요가 없기 때문이다.

 

 

§ 조건문 컴파일  (Do-While  loop)

- 바꾸는 방법: do-while => Go to => assembly

이때, x>>=1의 경우, 등호를 통해 shift결과를 x에 save할 수 있게 된 것이다.

 

 

§ 조건문 컴파일 (While  loop)

- 바꾸는 방법: 조건문 => do-while => Go to => assembly

§ 일반적인 While문의 번역- 1

 

 

§ 일반적인 While문의 번역- 2

 

 

 

 

§ 조건문 컴파일  (for  loop)

- 바꾸는 방법: for => while => do-while => go to => assembly

for (init; test; update)
    Body
    

init;
while (test) {
    Body
    update;
}

 

 

 

 

§ 조건문 컴파일  (switch 문)

long switch_eg (long x, long y, long z) {
    long w = 1;
    switch (x) {
        case 1:
            w = y * z;
            break;
        case 2:
            w = y / z;
            /* Fall through */
        case 3:
            w += z;
            break;
        case 5:
        case 6:
            w -= z;
            break;
        default:
            w = 2;
    }
    return w;
}

§ jump table 구조

 

▶ table 구조

- 각 타겟은 8 byte를 필요로 하며  시작주소는 .L4 이다.

 

▶ jump 하기

▷ 직접 점프: jmp  .L8

- 점프 대상은 레이블 .L8로 표시한다.

 

▷ 간접 점프: jmp  *.L4(, %rdi, 8)

- 점프 테이블의 시작: .L4

- 8 byte 주소이기에 8의 배수로 증가해야 함

- 점프 target 유효주소는 .L4 + x * 8 로부터 얻어진다.  (0 ≤ x ≤ 6일 때, 성립)

 

 

(cf. intel의 x86-64 cpu의 assembly를 기준으로 진행.)

※  Assembly란? 

기계어와 1:1 대응관계를 갖는 명령어의 집합체로 low-level 프로그래밍 언어이다.

- 물론 고급언어의 경우, 안전하고 편리하며 컴파일러를 통해 더 훌륭한 어셈블리 프로그램생성이 가능할지도 모른다.

assembly High-level Language
-대형 프로그램 개발이 불편

-속도가 중요한 응용프로그램, 하드웨어 직접 제어 시 이용

-임베디드 시스템의 초기코드 개발 시 이용

-플랫폼에 따라 새롭게 작성해야 해 이식성이 매우 낮음

-하지만 많은 간접적인 응용이 있음

- 배열이나 구조체 없이 메모리에서 연속적 byte들로 표시
-대형 프로그램 개발하기 매우 편함

-이식성이 높음 (high portability)

-비효율적 실행파일의 생성가능성이 존재

-대형 실용 응용프로그램 개발 시 이용

 

 

※  x86 processor

1978년부터 시작되어 점점 새로운 기능들을 추가하였지만, 예전의 기능들을 그대로 유지해 접근이 가능하다.

이런 intel의 철학은 바로 CICS (Complex Instruction Set Computer)로 다양한 형태의 명령어를 갖는다.

- EIP: 다음 명령어를 fetch해오는 주소 program counter

- Register File: Register의 집합

- Condition Code: 가장 최근 연산의 결과의 상태정보를 저장

- Memory: byte주소 지정가능한 배열로 명령어, data가 저장되며 stack이 존재한다.

 

 

※  x86-64의 정수 register

x86-64 processor(CPU)는 64-bit 값을 저장할 수 있는 16개의 register가 존재한다.

이들은 정수 데이터와 포인터를 저장하는 데, 포인터의 경우 주소를 저장하는데 사용합니다.

rsp, rbp와 같이 p로 끝나는 register의 경우가 바로 포인터를 사용해 주소를 저장하는 register이다.

이 16개의 레지스터는 모두 %r로 시작하는 이름을 갖는다.

위 사진을 보면 %rxx 레지스터의 절반의 크기에 %exx라는이름이 붙어있는 것을 확인할 수 있다.

%exx%rxx의 하위 4바이트에 해당하며 이와 같은 방식으로 아래 사진처럼  2바이트, 1바이트에 각각 접근가능!

이때, esp와 ebp는 모두 pointer로 주소를 저장하는 register이다.

이는 예전에 1바이트 단위 접근방식을 남겨두어 예전 의 기능을 그대로 유지해 하위 호완성을 유지하기 위해 과거의 레지스터들의 이름이 남아있는 것이다.

전체 16개의 레지스터의 하위 바이트들은 바이트, 워드(16비트), 더블워드(32비트), 쿼드워드(64비트)씩 접근할 수 있습니다.

 

 

※  데이터의 이동 (Moving Data):

§  movq  Source, Dest

위의 뜻은 Source를 읽어 Destination에 복사를 한다는 의미이다.

 

§  Operand의 유형 

 

▶ Immediate : $로 시작하는 형태,  상수 정수 data

ex) $0x400,  $-503Register : %로 시작하는 형태,  16개 register지만 %rsp는 stack접근에만 이용한다.

ex) %rax,  %r13

Memory : (%register)의 형태, register로 지정되는 주소에 저장된 8개의 연속적 메모리 byte

  - 이때, Memory주소지정을 다음과 같은 방식으로 할 수 있다.

    i) 일반형 (R)  =  Mem[Reg[R]]                  ex) movq (%rcx) , %rax

   ii) 변위형 D(R)  =  Mem[Reg[R] + D]         ex) movq  8(%rbp) , %rdx 

  iii) 가장 일반적인 형태  D(Rb, Ri, S)  =  Mem[Reg[Rb] + S*Reg[Ri] + D] 

       - D: 상수 변위 (1, 2 or 4 bytes)

       - Rb: base register : 16개 register 모두 가능

       - Ri: index register : %rsp를 제외한 모두 가능

       - S: 배율 (1, 2, 4  or 8 bytes)

이때, Dest에는 Immediate 즉, 상수값은 올 수 없다! (상수값에 대입할 수 없어서)

또한, Source와 Dest 모두 Memory인 memory-memory 데이터 이동은 한개의 명령으로 불가능하다.

 

Ex.  아래 함수에 대해 Assembly과정을 이해해보자.

<C code>

void swap (long *xp, long *yp) {
    long t0 = *xp;
    long t1 = *yp;
    *xp = t1;
    *yp = t0;
}

 

<Assembly>

swap:	
    movq	(%rdi), %rax	# t0 = *xp
    movq	(%rsi), %rdx	# t1 = *yp
    movq	%rdx, (%rdi)	# *xp = t1
    movq	%rax, (%rsi)	# *yp = t0
    ret

 

 

 

 

 

※  주소 계산 명령어 (Load Effective Address):

§  leaq  Src, Dst

- Src: 주소 모드 식 (Memory)

- Dst: 수식으로 표현된 주소값 저장 (Register)

 

용도: 메모리를 참조하지 않고 "주소만 계산"할 때  (C에서 &연산자, 곱셈과 같은 경우)

Ex.  아래 함수에 대해 Assembly과정을 이해해보자.

<C code>

long m12 (long x) {
    return x * 12;
}

<Assembly>

leaq (%rdi, %rdi, 2) , %rax		# t <- x + x*2
salq $2,  %rax				# return t << 2

leaq (%rdi, %rdi, 2) , %rax 부분을 통해 %rdi * 3과 같은 결과를 얻고

salq  $2  (shift  arithmetic  left  2자리)연산을 이용해 (3 * %rdi) * 4로 최종적으로 12가 곱해지는 C code와 동일한 결과를 얻는다.

 

 

 

 

 

 

 

+ Recent posts