jun-wiki

View My GitHub Profile

Posts (Latest 10 updated) :
Read all
Contents:
  1. 3.6 제어
    1. 3.6.1 조건 코드
      1. 요점
    2. 3.6.2 조건 코드 접근 방법
      1. 1. set 명령어를 통한 0/1 저장
      2. 2. 조건부 점프(Conditional Jump)
      3. 3. 조건부 데이터 이동(Conditional Move)
      4. 요점
    3. 3.6.3 점프(jump) 명령어
      1. 1. 무조건 점프(Unconditional Jump)
      2. 2. 조건부 점프(Conditional Jump)
      3. 3. 직접 점프와 간접 점프
      4. 요점
    4. 3.6.4 점프 명령어의 인코딩
      1. 요점
    5. 3.6.5 조건 분기를 이용한 조건문 구현
      1. 요점
    6. 3.6.6 조건부 데이터 이동(Conditional Move)을 이용한 조건문 구현
      1. 요점
    7. 3.6.7 반복문(Loops)
      1. 1. do-while 루프
      2. 2. while 루프
      3. 3. for 루프
      4. 요점
    8. 간략 정리 (no cap)

csapp 3.6이다


마음 같아선 3.11 까지 세번 완독하고

백덤블링도 3바퀴 돌았는데

안타깝게도 몸이 따라주지 못해

오늘은 csapp 3.6까지만 하겠다


3.6 제어

이번에는 C언어에서 if문 loop문 같이 조건에 따라
실행 흐름이 달라지는 구조에 대해서 알아볼거다



3.6.1 조건 코드

cpu에 최근 수행된 산술 또는 논리 연산의 특성을 나타내는 비트가 있는데 조건 분기 사용할 때 쓴다
아래에는 유용한 조건 코드들이다


  • CF (Carry flag, 캐리 플래그):
    가장 최근의 연산에서 최상위 비트에서 캐리가 발생했을 때 설정. 부호 없는 연산의 오버플로우 감지에 사용


  • ZF (Zero flag, 제로 플래그):
    가장 최근의 연산 결과가 0일 때 설정


  • SF (Sign flag, 부호 플래그):
    가장 최근의 연산 결과가 음수일 때 설정


  • OF (Overflow flag, 오버플로우 플래그):
    가장 최근의 연산에서 2의 보수 오버플로우(양수/음수 방향 모두)가 발생했을 때 설정


leaq명령어는 주소만 계산해서 조건 코드 건드리지 않음



요점

연산 결과의 특성을 나타내는 조건 코드(CF, ZF, SF, OF)는 분기나 조건 명령의 핵심 기준이다



3.6.2 조건 코드 접근 방법

조건 코드는 보통 직접 읽거나 하다기 보다는
3가지 방법으로 관리한다

  1. 조건 코드 조합에 따라 1바이트 값을 0 또는 1로 설정

  2. 조건에 따라 프로그램의 다른 위치로 점프(분기) 수행

  3. 조건에 따라 데이터 이동 수행


1. set 명령어를 통한 0/1 저장

첫번째 경우로 조건 코드 값(예: ZF, SF 등)에 따라, 1바이트 레지스터나 메모리 위치에 0또는 1을 저장한다

보통 C의 비교 연산이나 연산 결과를 bool 값으로 사용하고 싶을때 쓴다

  • 예시:

    • sete %al ($ZF=1$일 때 %al에 1 저장, 아니면 0)

    • setl %al ($SF^{OF}=1$일 때 %al에 1 저장, 아니면 0)



2. 조건부 점프(Conditional Jump)

조건 코드에 따라 프로그램 실행 흐름(PC)를 다른 위치로 분기한다

주로 if, while 등 C의 제어문 구현에 쓰인다

  • 예시:

    • je label ($ZF=1$이면 label로 점프)

    • jl label ($SF^{OF}=1$이면 label로 점프)



3. 조건부 데이터 이동(Conditional Move)

조건 코드의 값에 따라 값을 복사(이동)할지 말지 결정하는 명령어(cmov, cmovz, cmovl 등)

분기하는 대신 전부 계산 후 조건에 따라 두 값중 하나 선택

분기 예측 실패 예방 ㄱㄴ

  • 예시:

    • cmovge %rdx, %rax (x >= y일 때만 %rdx값을 %rax에 복사)



요점

조건 코드 값에 따라 1/0을 저장하는 set 명령, 조건부 점프, 조건부 데이터 이동이 구현된다



3.6.3 점프(jump) 명령어

조건 코드와 더불어, 점프 명령어는 프로그램의 실행 흐름(PC)을 변경하는 핵심 도구다
조건부와 무조건 점프를 적절히 사용해 C의 if/else, 반복문 등 제어 구조가 어셈블리로 구현된다


1. 무조건 점프(Unconditional Jump)

조건 없이 무조건 지정한 위치(레이블)로 실행 흐름을 이동한다
함수 호출 전후, 루프 진입/탈출 등에서 활용

  • 예시:

    • jmp label (바로 label로 이동)

    • jmp *%rax (레지스터 값이 가리키는 주소로 이동, 간접점프)


2. 조건부 점프(Conditional Jump)

조건 코드 값에 따라 특정 레이블로 분기한다
C의 if, while, for, switch 등 조건 제어 구현의 기본 도구
다양한 조건(==, !=, <, > 등)에 따라 jump 명령어가 준비되어 있음

  • 예시:

    • je label ($ZF=1$일 때 label로 점프, Equal/Zero)

    • jne label ($ZF=0$일 때 label로 점프, Not Equal)

    • jl label ($SF^{OF}=1$일 때 label로 점프, Less Than)

    • jg label ($(SF^{OF}=0$이고 $ZF=0$)일 때 label로 점프, Greater Than)


3. 직접 점프와 간접 점프

  • 직접 점프: jump 대상이 어셈블리 코드의 레이블로 직접 지정

    • 예: jmp .L1
  • 간접 점프: jump 대상이 레지스터나 메모리의 값(주소)

    • 예: jmp *%rax, jmp *(%rsp)


요점

조건부/무조건 점프 명령어로 프로그램 흐름을 바꿀 수 있다



3.6.4 점프 명령어의 인코딩

기계어에서 점프 명령어(jmp, je, jne 등)는 점프 대상 주소(어디로 이동할지)를 반드시 명령어 내부에 인코딩해야 한다
이때 주로 사용하는 방법이 PC(Program Counter)-상대 오프셋 방식이다

즉,

  • 점프 명령어 다음 명령어의 주소와 점프 타겟 명령어의 주소의 차이(오프셋)를 저장한다.

  • 이 오프셋은 1, 2, 4 바이트 등 다양한 길이로 저장 가능하며, 프로그램이 메모리 어디에 적재되든 인코딩이 변하지 않는다.

  • 절대 주소(Absolute address) 방식도 있지만, 대부분은 PC-relative 방식이 쓰인다.


예시)

jmp .L2      # .L2까지 몇 바이트 건너뛸지 오프셋으로 저장
  • 실제 기계어 바이트에서는, jmp 다음 바이트에 오프셋이 저장됨

  • 이 오프셋 값 + “다음 명령어 주소” = 점프 목적지

(실제 예: 0x3에서 eb 03 ⇒ 3 + 3 = 0x6, 즉 0x8 주소로 점프)



요점

점프 명령의 대상 주소는 주로 PC-상대 오프셋(다음 명령 기준)으로 인코딩된다



3.6.5 조건 분기를 이용한 조건문 구현

여기서 부턴 좀 간략히 쓰겠다

피곤하고 눈 아파서 괴롭다

요점

조건문(if-else)은 조건 검사와 분기(jump) 명령의 조합으로 구현된다

  • C의 if-else, switch 등 조건문은 결국 비교 명령어(cmp 등)와 조건부 점프 명령어(jge, jl 등)를 이용해 각 분기(then/else 블록)로 흐름을 나눈다

  • 어셈블리에서의 분기 구조는 C의 goto 문과 매우 비슷하며, 실제 실행 흐름이 눈에 보이도록 나타난다



3.6.6 조건부 데이터 이동(Conditional Move)을 이용한 조건문 구현

요점

조건부 데이터 이동(cmov)은 분기 없이, 조건에 따라 값만 선택할 때 효과적이다

  • 분기 대신 cmov 명령어를 사용하면, 비교 결과에 따라 특정 레지스터 값만 덮어쓸 수 있다

  • 파이프라인 효율이 좋아지고 분기 예측 실패에 따른 성능 손실이 없으나, 부작용/에러 가능성이 있는 코드는 사용에 주의해야 한다

  • 주로 if문의 각 블록이 간단한 대입일 때, 그리고 양쪽 결과를 모두 미리 계산해도 문제가 없을 때 활용된다



3.6.7 반복문(Loops)

C의 반복문(while, do-while, for)은 기계어에서는 조건 검사와 점프 명령의 조합으로 모두 구현된다
컴파일러는 코드 구조와 최적화 수준에 따라 다양한 루프 패턴(jump-to-middle, guarded-do 등)으로 변환한다


1. do-while 루프

  • 형태:
do {
    body;
} while (test);


  • 동작:

    • body를 최소 1회 실행하고, test가 참이면 반복
  • goto 변환:

loop:
    body
    if (test) goto loop;


  • 기계어:

    • 루프 끝에서 조건 검사 후, 참이면 loop로 점프



2. while 루프

  • 형태:
while (test) {
    body;
}


  • jump-to-middle 방식:

    • 루프 처음에 test 검사로 건너뛰기
goto test;
loop:
    body
test:
    if (test) goto loop;


  • guarded-do 방식:

    • test 먼저 검사, 실패시 루프 자체 건너뜀
if (!test) goto done;
loop:
    body
    if (test) goto loop;
done:



3. for 루프

  • 형태:
for (init; test; update)
    body;


  • while 변환:
init;
while (test) {
    body;
    update;
}


  • jump-to-middle 방식:
init;
goto test;
loop:
    body;
    update;
test:
    if (test) goto loop;
  • guarded-do 방식도 while과 유사



요점

반복문(while, do-while, for)은 조건 검사와 점프 명령의 조합으로 기계어로 구현된다



간략 정리 (no cap)

  • 3.6.1 CPU는 연산 결과마다 캐리(CF), 제로(ZF), 부호(SF), 오버플로우(OF) 등의 1비트 조건 코드를 남긴다. 이후 조건 분기, 비교, set 명령 등에서 이 값을 사용한다.

  • 3.6.2 set 계열 명령어(sete, setl 등)는 조건 코드에 따라 바이트 값을 0/1로 저장, 조건부 분기(jump), 조건부 데이터 이동에도 활용된다.

  • 3.6.3 무조건(jmp) 및 조건부(je, jne, jg 등) jump 명령어를 통해 코드 실행 위치를 동적으로 바꿀 수 있다. 조건부 jump는 주로 비교 이후 조건 코드 조합을 사용한다.

  • 3.6.4 점프 주소는 명령어 포인터(PC) 기준 상대 오프셋으로 주로 저장되어, 프로그램의 재배치 및 효율적 실행이 가능하다.

  • 3.6.5 if-else문은 비교 후 조건부/무조건 점프로 흐름을 나누어 구현한다. C의 goto 코드와 어셈블리 분기 구조가 거의 일치한다.

  • 3.6.6 cmov 명령 등 조건부 이동은 분기 대신 조건에 따라 값만 선택하며, 분기 예측 실패 시 페널티 없이 효율적으로 처리할 수 있다. 단, 양쪽 결과를 항상 계산하므로 부작용/에러 가능성이 있는 경우 사용하지 않는다.

  • 3.6.7 반복문은 본문(body) 실행, 조건 검사, 점프를 조합해 구현하며, do-while/while/for 모두 jump-to-middle, guarded-do 등 패턴으로 기계어로 변환된다.