농담곰담곰이의곰담농

CSAPP ch 3 프로그램의 기계수준 표현 3.6

by 브이담곰

지금까지는 인스트럭션들이 하나씩 순차적으로 실행되는 직선적인 코드 동작만을 다루어왔다. C의 일부 구문인 반복문, 스위치문들은 데이터에 적용된 실험 결과에 따라 일련의 연산이 실행되는 조건부 실행이 요구된다. 
기계어 코드에서는 조건부 동작을 구현하기 위해 두 개의 기본적인 낮은 수준의 방법을 제공한다. 데이터 값을 실험해서 이 시험 결과에 따라 데이터흐름이나 제어흐름변경한다.

 

C와 기계여 코드의 인스트럭션들은 모두 프로그램에 나타나는 순서대로 순차적으로 실행된다.

하지만 기계어 인스트럭션들의 실행 순서는 점프Jump 인스트럭션으로 변경할 수 있다.

 

조건형 연산을 구현하는 두가지 방법

3.6.1 조건 코드

CPU = 정수 레지스터 + 단일비트 조건 코드로 구성된 레지스터 운영

 

조건부 분기를 수행하기 위해서 사용된다. 대표적으로 아래의 조건 코드들이 유용하다.

CF : 캐리 플래그 Carry flag 가장 최근의 연산에서 가장 중요한 비트로부터 받아 올림이 발생한 것을 표시. 비부호형 연산에서 오버플로우를 검출할 때 사용.
ZF : 영 플래그 Zero flag 가장 최근 연산의 결과가 0인 것을 표시
SF : 부호 플래그 Sign flag 가장 최근 연산이 음수를 생성한 것을 표시
OF : 오버 플로우 플래그 Overflow flag 가장 최근 연산이 양수/음수의 2의 보수 오버플로우를 발생시킨 것을 표시

 

정수 a,b,t 를 사용하는 C 할당문 t = a + b를 수행하기 위해 ADD 인스트럭션을 사용했다고 가정하자, 조건 코드는 아래의 C의 수식에 따라 설정된다.

CF (unsigned) t < (unsigned) a              # Unsigned overflow
ZF ( t == 0 )                               # Zero
SF ( t < 0 )                                # Negative
OF ( a < 0 == b < 0 ) && ( t < 0 != a < 0 ) # Signed overflow

CF

a + b를 했으니 당연히 t가 커야하지만, a가 더 큰 경우 t의 값이 오버플로우 되어 a보다  작아졌다고 판단한 후 CF로 조건 코드를 변경한다.

 

OF

두 피연산자(a,b)의  부호가 같고, 결과의 부호가 피연산자와 다를 때 조건 코드를 변경한다.

 

leaq 인스트럭션은 주소계산에 사용하기 위한 것이므로 조건코드를 변경하지 않지만, 아래의 표에 나열된 모든 인스트럭션들은 조건 코드 값을 변경한다. 또한 레지스터의 값도 변경된다. 

계산 된 값을 레지스터에 다시 저장하기 때문에!

더보기

ex ) XOR은 Carry와 Overflow 플래그가 0으로 설정된다.

 

비교 및 시험 인스트럭션

다른 레지스터 값은 수정하지 않은 채 조건 코드만 바꿔주는 인스트럭션

CMP

두 오퍼랜드 차에 따라 조건 코드 설정

SUB 인스트럭션과 같은 방법으로 동작

두 오퍼랜드 크기가 같으면 영 플래그를 1로 설정

* ATT 형식에서 오퍼랜더들은 역순으로 나열되기 때문에 코드를 읽기가 어려움

 

TEST

목적지 오퍼랜드를 변경하지 않고 조건 코드 설정

AND 인스트럭션과 같은 방법으로 동작

같은 오퍼랜드가 반복되거나, 오퍼랜드 중 하나는 시험할 비트를 가리키는 마스크

 

 

3.6.2 조건 코드 사용하기

 

1️⃣ 조건 코드의 조합에 따라 0 또는 1을 한 개의 바이트에 기록 : SET 인스트럭션
2️⃣ 조건에 따라 프로그램의 다른 부분으로 이동하는 방법
3️⃣ 조건에 따라 데이터를 전송하는 방법

 

SET 인스트럭션

- 서로 다른 접미어를 갖는다.

- 다른 조건 코드의 조합을 사용하여 서로 다른 동작을 함

- 조건 코드의 어떤 조합을 할 것인지를 나타냄 ( setl : set less, setb : set below )

- 바이트를 0이나 1로 기록 → 하위 단일 바이트 레지스터 or 바이트 메모리 주소 사용

 

EX) C에서 long a와 b사이에 수식 a<b를 계산하는 인스트럭션

int comp(data_t a, data_t b)
a in %rdi, b in %rsi
comp:
	cmpq %rsi, %rdi		# Compare a:b
    setl %al			# Set low-order byte of %eax to 0 or 1
    movzbl %al, %eax		# Clear rest of %eax (and rest of %rax )
    ret

💡movzbl 은 %eax의 상위 3바이트만 지우는 것이 아닌, 전체 레지스터인 %rax의 상위 4바이트도 0으로 지운다.

예를 들면, setg(더 큰경우에 1을 저장)와 setnle(작거나 동일하지 않으면 1을 저장)은 동일한 기계어 인스트럭션을 의미. 컴파일러와 역 어셈블러는 어떤 이름을 사용할지 랜덤으로 결정한다.

 

모든 산술 및 논리연산이 조건코드를 설정할지라도

다른 SET 인스트럭션들에 대한 설명은

t = a - b 계산 후의 결과에 따라 조건 코드를 설정하는 비교 인스트럭션이 실행된 경우에 적용된다

3.6.3  점프 Jump 인스트럭션

프로그램이 완전히 새로운 위치로 실행을 전환하도록 함.

어셈블리 코드에서 점프의 목적지는 레이블(label)로 표시한다.

movq $0, %rax    	# Set %rax to 0
jmp .L1          	# Goto .L1
movq (%rax), %rdx	# Null pointer dereference (skipped)
.L1 :
popq %rdx			# Jump target

어셈블러는 모든 레이블이 붙은 인스트럭션들의 주소를 결정하고, 점프 인스트럭션의 일부분인 " 점프 목적지 jump target( 목적지 인스트럭션의 주소)"를 인코딩

 

(1) 직접 점프 : 점프 목적지가 인스트럭션의 일부로 인코딩되는 경우

      →  .L1과 같이 점프 대상을 레이블로 프로그램 내에서 작성함

(2) 간접 점프 : 점프 대상을 레지스터나 메모리 위치로부터 읽어들여야 하는 경우

      → '*' 와 메모리 오퍼랜드 중의 하나를 이용한 오퍼랜드 식별자를 합쳐서 작성.

조건부 점프는 직접 점프만 가능하다!

jmp *%rax %rax의 값을 점프 목적지로 이용한다.
jmp *(%rax) %rax에 저장된 값을 읽기 주소로 사용해 메모리에서 점프 목적지를 읽어들인다.
3.6.4 점프 인스트럭션 인코딩

점프를 인코딩 하는 방법은 여러가지 있다.

1️⃣PC 상대적 방법

     대상 인스트럭션과 점프 인스트럭션 바로 다음에 오는 인스트럭션 주소와의 차이를 인코딩한다. 

▶️ 이들 오프셋은 1,2, 또는 4바이트로 인코딩 될 수 있다.

branch.c 파일의 컴파일 과정에서 생긴 어셈블파일
어셈블러가 생성한 .o형식의 역어셈블 버전

PC-상대주소지정을 수행할 때 프로그램 카운터의 값은 점프 인스터럭션 자신의 주소가 아닌, 점프 다음에 나오는 인스트럭션의 주소가 된다.

2️⃣ 절대 주소

     대상을 직접 명시하기 위해 4바이트를 사용.

어셈블러와 링커는 점프 목적지를 인코딩하는 방법을 적절히 선택한다.

 

3.6.5 조건부 분기를 조건제어로 구현하기

C에서 조건부 수식과 문장을 기계어 코드로 번역하는 가장 일반적인 방법은 조건부 및 무조건 점프를 함께 사용하는 것이다!

 

3.6.6 조건부 이동으로 조건부 분기 구현하기

조건이 만족되면 프로그램의 한 가지 실행경로를 따르고, 아닌 경우에는 다른 경로를 따라가도록 하는 제어의 조건부 전환

→ 간단하지만 일반적인 프로세서들에서는 매우 비효율적일 수 있음!

 

따라서 조건부 전송을 통해 이를 해결해볼 수 있다.

조건부 전송은 조건부 동작의 산출물 모두를 계산하고 조건에 따라 하나만 선택하는 방식이다.

프로세서들은 각 인스트럭션을 일련의 단계로 처리하며, 이 단계들은 작은 부분들만을 실행하는 파이프라인을 통해 높은 성능을 얻는다.

 

만약에 프로세서가 조건부 점프를 만나게 되면, 프로세스는 분기 조건에 대한 계산이 완료될 때까지 어느쪽으로 분기를 결정할 수 없다. 만약 잘못 예측한다면 미래의 인스트럭션들을 위해 이미 실행한 작업 결과들을 버려야할 수 있고 정확한 위치에서 다시 인스트럭션들을 파이프라인에 채우는 작업을 해야한다.

 

조건부 이동 move 인스트럭션

두개의 오퍼랜드를 갖는 인스트럭션~!

- move 인스트럭션의 결과는 조건 코드 값에 따라 달라진다. 

- 소스 값은 메모리나 소스 레지스터로부터 읽히지만, 목적지에는 명시된 조건이 만족될 때만 복사됨.

- 어셈블러는 목적지 레지스터의 이름으로부터 조건부 이동 인스트럭션의 오퍼랜드 길이를 추정.

- 동일한 인스트럭션 이름이 모든 오퍼랜드 길이에 대해서 사용될 수 있다.

- 프로세서는 테스트의 결과를 예측하지않고서도 조건부 이동 인스트럭션을 실행할 수 있음.

블로그의 정보

농담곰담곰이의곰담농

브이담곰

활동하기