2 minute read

3.6 Control

지금까지는 명령어들이 순차적으로 실행되는 “직선적 코드”의 동작만 다뤘다. 하지만 C언어의 조건문, 반복문, switch문과 같은 구조들은 조건부 실행(conditional execution)을 필요로하며, 이는 데이터에 대한 테스트 결과에 따라 실행되는 연산의 순서가 달라지는 경우를 의미한다.

기계어는 이러한 조건부 동작을 구현하기 위해 두 가지 저수준 메커니즘을 제공한다.

  • 데이터 값을 테스트
  • 테스트 결과에 따라 제어 흐름(control flow) 또는 데이터 흐름(data flow)을 변경

이 중 데이터 기반 제어 흐름(data-dependent control flow)이 더 일반적이고 널리 사용된다. 일반적으로 C언어의 명령문과 기계어 명령어는 프로그램에 작성된 순서대로 순차적으로 실행된다. 그러나 점프 명령(jump instruction)을 사용하면 실행 순서를 변경할 수 있으며, 이는 특정 테스트 결과에 따라 프로그램의 다른 부분으로 제어를 넘길 수 있다.

컴파일러는 이러한 저수준 메커니즘을 활용하여 C언어의 조건부 구조를 구현하기 위한 명령어 시퀀스를 생성한다. 이후 설명에서는 조건부 연산을 구현하는 두 가지 방법과 반복문, switch문을 구현하는 방법에 대해 살펴보곘다.

3.6.1 Condition Codes

CPU는 정수 레지스터(integer refisters) 외에도, 최근의 산술 또는 논리 연산의 속성을 나타내는 1비트 크기의 조건 코드 레지스터 집합을 유지한다. 이 조건 코드들은 조건부 분기(conditional branch)를 수행하기 위해 테스트된다.

  • 주요 조건 코드(Condition Code)
    • CF(Carry Flag, 캐리 플래그)
      • 최근 연산에서 가장 상위 비트에서 캐리가 발생했음을 나타낸다.
      • 부호 없는 연산에서 오버플로우를 감지하는 데 사용된다.
    • ZF(Zero Flag, 제로 플래그)
      • 최근 연산의 결과가 0임을 나타낸다.
    • SF(Sign Flag, 부호 플래그)
      • 최근 연산의 결과가 음수임을 나타낸다.
    • OF(Overflow Flag, 오버플로우 플래그)
      • 최근 연산이 2의 보수(two’s-complement) 오버플로우를 발생시켰음을 나타낸다.
      • 양수 또는 음수 오버플로우 모두 감지한다.
  • 예제
    • C언어에서 t = a + b와 같은 연산을 수행한다고 가정하면, 변수 a,b,t는 정수이며, 조건 코드는 다음과 같이 설정된다.
     CF  (unsigned) t < (unsigned) a     Unsinged Overflow
     ZF  (t == 0)                        Zero
     SF  (t < 0)                         Negative
     OF  (a<0 == b<0) && (t<0 != a<0)    Signed Overflow
    
  • 조건 코드의 동작
    • leaq 명령어 : 조건 코드를 변경하지 않는다.(주소 계산에 사용되기 때문)
    • 그 외의 명령어 : 조건 코드를 설정한다.
      • 논리 연산 : 캐리(CF)와 오버플로우(OF) 플래그를 0으로 설정한다.
      • 시프트 연산
        • 캐리 플래그(CF) : 마지막으로 이동된 비트를 저장한다.
        • 오버플로우 플래그(OF) : 항상 0으로 설정된다.
      • inc 및 dec 명령어
        • 오버플로우(OF)와 제로(ZF) 플래그를 설정하지만, 캐리 플래그(CF)는 변경하지 않는다.
  • 조건 코드만 설정하는 명령어

Alt text

  • cmp 명령어
    • 두 피연산자의 차이에 따라 조건 코드를 설정한다.
    • 동작 방식 : sub 명령어와 동일하지만, 차이를 계산한 후 결과를 저장하지 않고 조건 코드만 설정한다.
    • ATT 문법에서는 피연산자의 순서가 반대로 표시되어 읽기 어렵다.
  • test 명령어
    • 두 피연산자의 비트 AND 결과에 따라 조건 코드를 설정한다.
    • 동작 방식 : and 명령어와 동일하지만, 결과를 저장하지 않고 조건 코드만 설정한다.
    • 일반적인 사용 사례 :
      • 동일한 피연산자를 반복적으로 사용(testq %rax, %rax)하여 %rax가 음수, 0 또는 양수인지 확인
      • 피연산자 중 하나를 마스크(mask)로 사용하여 특정 비트를 테스트

3.6.2 Acessing the Condition Codes

  • 조건 코드를 활용하는 세 가지 방법
    1. 단일 바이트를 0 또는 1로 설정하기
      • 특정 조건 코드 조합을 기반으로 단일 바이트 값을 0 또는 1로 설정할 수 있다.
      • 이때 사용되는 명령어를 set 명령어라고 하며, 조건 코드에 따라 서로 다른 접미사(suffix)를 가진다.
      • 예를 들어, setl은 “set less”, setb는 “set below”를 의미한다.
    2. 조건부 점프(Conditional Jump)
      • 조건 코드에 따라 프로그램의 다른 부분으로 점프할 수 있다.
      • ex. jl(jump if less), jb(jump if below) 등
    3. 조건부 데이터 이동(Conditional Data Transfer)
      • 조건 코드에 따라 데이터를 이동시킬 수 있다.
      • ex. cmovl(conditional move if less), cmovb(conditional move if below) 등
  • set 명령어의 특징
    • set 명령어는 낮은 바이트 레지스터(low-order single-byte register) 또는 단일 바이트 메모리 주소를 대상으로 한다.
    • 결과로 0 또는 1을 설정한다.
    • 32비트 또는 64비트 연산을 수행하려면 상위 비트를 반드시 클리어해야 한다.

Alt text