3.6.5 - 조건부 분기를 조건 제어로 구현하기 (Implementing Conditional Branches with Conditional Control)
🎯 조건부 분기를 조건 제어로 구현한다는 건?
우리 눈에 보이는 C 코드에서는 if, else 같은 조건문이 있다. 컴퓨터는 이걸 어셈블리 코드에서 점프(jump) 명령어를 써서 구현한다.
👨🏫 예시 C 코드
long absdiff_se(long x, long y) {
if (x < y) {
lt_cnt++;
return y - x;
} else {
ge_cnt++;
return x - y;
}
}
- 여기서 lt_cnt랑 ge_cnt는 "x가 y보다 작을 때"와 "크거나 같을 때"를 센다는 의미다.
🧱 이걸 어셈블리로 바꾸면?
컴파일러는 이렇게 바꾼다.
cmpq %rsi, %rdi ; x < y 인지 비교
jge .L2 ; x >= y면 .L2로 점프
addq $1, lt_cnt(%rip); lt_cnt++
movq %rsi, %rax
subq %rdi, %rax ; result = y - x
ret
.L2:
addq $1, ge_cnt(%rip); ge_cnt++
movq %rdi, %rax
subq %rsi, %rax ; result = x - y
ret
🧠 컴파일러는 어떻게 결정하는가?
- 먼저 비교를 한다. cmpq로 x와 y 비교.
- 조건이 맞으면 (jge) 점프해서 else 코드 실행.
- 조건이 안 맞으면 그냥 다음 줄 실행해서 if 코드 실행.
이런 방식은 C 코드의 if-else를 아주 똑같이 구현할 수 있다.
🔁 C 코드 버전의 점프 스타일 (goto 코드)
컴퓨터가 이해하기 쉬운 방식으로 바꿔 보면:
long gotodiff_se(long x, long y) {
if (x >= y)
goto x_ge_y;
lt_cnt++;
return y - x;
x_ge_y:
ge_cnt++;
return x - y;
}
goto는 "이 줄로 바로 점프하자!"는 의미로. jmp랑 비슷하다.
3.6.6 조건부 이동으로 조건부 분기 구현하기
💡 조건부 분기와 조건부 이동의 차이점
기존 방식: 조건부 분기(jump)
우리가 전에 배운 if문을 컴퓨터는 조건 검사 → 점프(jump)로 처리했다.
예:
if (x < y) result = y - x;
else result = x - y;
이걸 어셈블리에서는 cmp + jge + jmp 등을 써서 "이 조건이면 저기로 가자!" 식으로 처리한다.
그런데 이 점프는 잘못 예측하면 컴퓨터가 일을 다시 하게 돼서 느려질 수 있다 😢
새로운 방식: 조건부 이동(move)
그래서 나오는 게 바로 조건부 이동(conditional move)이다. 이건 점프 없이도 “조건에 맞으면 이 값으로 바꾸자”라고 할 수 있다.
💡 조건부 이동이란?
조건부 이동은 어떤 조건이 맞을 때만 값을 복사하는 명령어다.
조건이 맞지 않으면? 그냥 무시하고 지나간다.
형식은 이렇게 생겼다.
cmovXX src, dest
- XX는 조건을 나타내는 문자들이다.
- src의 값을 dest로 복사한다 단, 조건이 맞을 때만 복사한다.
🧾 주요 조건부 이동 인스트럭션들
명령어 | 뜻 (조건) | 동작 조건 |
cmove 또는 cmovz | 같으면 이동 (equal) | ZF = 1 |
cmovne 또는 cmovnz | 다르면 이동 (not equal) | ZF = 0 |
cmovl | (signed) 작으면 이동 | SF ≠ OF |
cmovle | (signed) 작거나 같으면 이동 | ZF = 1 or SF ≠ OF |
cmovg | (signed) 크면 이동 | ZF = 0 and SF = OF |
cmovge | (signed) 크거나 같으면 이동 | SF = OF |
cmova | (unsigned) 크면 이동 | CF = 0 and ZF = 0 |
cmovae | (unsigned) 크거나 같으면 이동 | CF = 0 |
cmovb | (unsigned) 작으면 이동 | CF = 1 |
cmovbe | (unsigned) 작거나 같으면 이동 | CF = 1 or ZF = 1 |
📌 예시 코드
cmpq %rsi, %rdi ; x - y 비교
movq %rdi, %rax ; 기본값: x
cmovl %rsi, %rax ; 만약 x < y (signed)면, y를 복사
이 코드는 “x가 y보다 작으면, 결과에 y를 넣어라”는 의미다
🔁 예제: 절댓값 차이를 구하는 함수
👨🏫 원래 C 코드:
long absdiff(long x, long y) {
long result;
if (x < y)
result = y - x;
else
result = x - y;
return result;
}
✨ 조건부 이동을 쓴 C 코드:
long cmovdiff(long x, long y) {
long rval = y - x;
long eval = x - y;
long ntest = x >= y;
if (ntest) rval = eval; // 조건부 이동 느낌!
return rval;
}
이걸 컴파일하면 어셈블리에서는 이렇게 된다:
movq %rsi, %rax ; rval = y
subq %rdi, %rax ; rval = y - x
movq %rdi, %rdx ; eval = x
subq %rsi, %rdx ; eval = x - y
cmpq %rsi, %rdi ; x >= y ?
cmovge %rdx, %rax ; 맞으면 rval = eval
ret
여기서 핵심은 cmovge라는 명령어다.
조건이 맞을 때만 %rdx 값을 %rax에 복사한다.
✅ 장점은?
- 점프가 없음 → 예측 실패로 인한 느려짐이 없다
- 파이프라인 흐름 깨지지 않음 → 빠르고 효율적이다.
⚠️ 주의할 점
조건부 이동은 두 개의 계산 결과를 모두 미리 계산해야 한다.
예:
long rval = y - x;
long eval = x - y;
둘 다 계산하고 나서 조건에 따라 하나를 선택한다.
그래서 만약 계산 중 하나가 오류나 부작용이 있으면 위험하다. 예를 들어 포인터를 따라가거나 나눗셈을 할 때는 조심해야 한다.
🧠 요약
항목 | 조건 분기 | 조건 이동 |
방식 | 점프 사용 | 데이터 복사 |
속도 | 예측 실패 시 느림 | 더 빠름 |
사용 조건 | 일반적으로 가능 | 계산이 안전할 때만 가능 |
✅ 요약
- cmov 계열은 조건이 맞을 때만 이동(복사) 한다.
- 점프 없이 조건문을 구현할 수 있어서 효율적이다.
- 다만 두 값을 미리 계산해야 한다는 점은 조심해야 한다. (비용 증가 가능성 있음).
3.6.7 반복문(Loops)
🔁 반복문이란?
반복문은 같은 코드를 여러 번 실행할 때 쓰는 도구이다.
예를 들어 "1부터 10까지 숫자를 차례로 더해라" 같은 걸 반복문으로 쉽게 할 수 있다.
C에는 대표적으로 3가지 반복문이 있다.
- do-while
- while
- for
1️⃣ do-while 루프
- 먼저 실행 → 나중에 조건 검사
- 최소 1번은 실행됨
📘 C 코드
do {
body-statement;
} while (test-expr);
🔧 어셈블리 스타일 구조
loop:
body-statement
t = test-expr
if (t) goto loop
🧪 예: 팩토리얼 계산
long fact_do(long n) {
long result = 1;
do {
result *= n;
n = n - 1;
} while (n > 1);
return result;
}
2️⃣ while 루프
- 조건 먼저 검사 → 조건이 참일 때만 실행
📘 C 코드
while (test-expr) {
body-statement;
}
🔧 어셈블리 스타일 구조 (jump-to-middle 방식)
goto test;
loop:
body-statement
test:
t = test-expr
if (t) goto loop
3️⃣ for 루프
- 세 부분으로 나눠짐: 초기화, 조건검사, 증가
📘 C 코드
for (init-expr; test-expr; update-expr) {
body-statement;
}
🔧 어셈블리 스타일 구조
init-expr;
goto test;
loop:
body-statement
update-expr
test:
t = test-expr
if (t) goto loop
✅ 핵심 요약
루프 종류 | 조건 검사 시점 | 특징 |
do-while | 뒤에서 검사 | 무조건 한 번 실행 |
while | 앞에서 검사 | 조건이 맞아야 실행 |
for | 앞에서 검사 | 변수 초기화, 조건, 증가 다 포함 |
3.6.8 Switch 문 (Switch Statements)
🌀 Switch 문이 무엇인가?
C 언어에서 switch 문은 한 변수의 값에 따라 여러 다른 경우(case)를 실행하게 해주는 도구이다.
예를 들어:
switch (n) {
case 1: ...
case 2: ...
default: ...
}
이런 식으로 n 값에 따라 다른 코드 블록을 실행한다
💡 컴퓨터는 이걸 어떻게 구현할까?
컴퓨터는 이걸 효율적으로 실행하기 위해 점프 테이블(jump table)이라는 걸 사용한다.
🔢 점프 테이블이란?
- 마치 “주소가 들어있는 배열” 같은 것이다.
- switch 값(n)을 인덱스로 사용해서, 배열에서 그 값에 맞는 주소로 점프한다.
- 즉, n = 2라면 jump_table[2]에 있는 주소로 점프한다.
📘 예시 C 코드
void switch_eg(long x, long n, long *dest) {
long val = x;
switch (n) {
case 100: val *= 13; break;
case 102: val += 10;
case 103: val += 11; break;
case 104:
case 106: val *= val; break;
default: val = 0;
}
*dest = val;
}
🧠 컴파일된 코드에서 일어나는 일
- 먼저 n - 100 해서 index를 만든다 (0~6 범위로 조정).
- 그 index가 범위를 벗어나면 → default로 점프
- 아니면 → 점프 테이블에서 index에 해당하는 주소로 점프한다
🧭 점프 테이블의 예시
index | case 값 | 점프 위치 |
0 | 100 | loc_A |
1 | 101 | loc_def (default) |
2 | 102 | loc_B |
3 | 103 | loc_C |
4 | 104 | loc_D |
5 | 105 | loc_def |
6 | 106 | loc_D |
이런 식으로 각 값에 따라 이동할 위치가 정해져 있다.
🔁 Fall-through (떨어지는 경우)
- case 102:에는 break가 없지다. 그래서 바로 다음 case 103: 코드도 같이 실행된다.
- 이걸 fall-through라고 한다.
✅ 장점?
- if-else를 계속 비교하는 것보다 빠르다.
- case가 많을수록 효과가 커진다.
- 컴파일러(gcc)는 case가 4개 이상이고 값 범위가 작으면 점프 테이블을 자동 사용한다.
'크래프톤 정글 (컴퓨터 시스템: CSAPP) > 3장 프로그램의 기계수준 표현' 카테고리의 다른 글
컴퓨터 시스템 : CSAPP 3장 정리 - 3.7 프로시저 Part.2 (0) | 2025.04.06 |
---|---|
컴퓨터 시스템 : CSAPP 3장 정리 - 3.7 프로시저 Part.1 (0) | 2025.04.06 |
컴퓨터 시스템 : CSAPP 3장 정리 - 3.6 장 제어문 Part.1 (0) | 2025.04.05 |
컴퓨터 시스템 : CSAPP 3장 정리 - 3.5 장 산술 및 논리 연산 (0) | 2025.04.05 |
컴퓨터 시스템 : CSAPP 3장 정리 - 3.4 장 정보 접근하기 Part.3 (0) | 2025.04.05 |