
2025.04.04
WEEK04 :
동적 프로그래밍, 그리디 알고리즘
CSAPP 3장. 프로그램의 기계 수준 표현 (특히 3.4, 3.7, 3.8)
3.1 ~ 3.3 까지 공부
어셈블리어를 배워야 하는 이유
- 컴파일러 최적화에 대해 이해할 수 있음.
- 반복된 컴파일 과정 속에서 나오는 어셈블리의 성능을 측정해 성능을 극대화할 수 있다.
- 고급언어가 추상화하는 프로그램의 런타임 행동을 이해할 수 있음.
- 고급 언어의 쓰레드 패키지를 통해 동시 프로그래밍을 할 때 공유 자원이 오가는 것을 머신 레벨에서 파악 가능.
- 프로그램의 취약점을 방어할 수 있다.
무어(Moore)의 법칙
- 마이크로칩에 저장할 수 있는 데이터 분량이 18-24개월 마다 두 배씩 증가한다는 법칙
- 반도체 기술의 빠른 발전 속도를 나타냄.
gcc, x86-64 기준으로 설명함.gcc 명령
1. C 전처리기를 통해#include로 명시된 파일을 코드에 삽입,#define으로 선언된 매크로 확장
2. 컴파일러가 어셈블리어로 변경 (.s 파일 생성)
3. 어셈블러가 어셈블리 코드를 바이너리 목적코드(기계어 코드의 한 유형)로 변경(.o 파일 생성)
4. 링커가 오브젝트 코드들을 실제 코드가 구현된 라이브러리의 주소와 일치시켜서 마침내 실행 가능한 프로그램을 만듦
자세한 과정은 7장에서 배운다.
1. 프로그램의 포맷과 행동은 ISA(Instruction set architectue) 에 의해 결정됨.
2. 사용되는 메모리 주소는 모두 Virtual addresses 이다.
보여지는 프로세서 상태의 대부분은 C 프로그래머에게 감쳐줘 있음.
Program Counter (PC, %rip(x86_64): 다음 명령이 실행될 메모리 주소를 가리킴
정수 register file : 16개의 64비트 레지스터, C포인터와 대응하는 주소나 정수 값을 저장.
Condition code register 최근에 실행된 산술 혹은 논리 명령의 상태 정보를 담고 있음.(if. while문)
벡터 레지스터: 하나 이상의 정수나 부동소수점(float) 값을 저장
기계어 입장에서 메모리는 단순한 "바이트 배열".
C언어의 구조체나 포인터, 배열도 결국 이 바이트 배열 위에서 구현됨.
모든 메모리 접근은 가상 주소(Virtual Address) 를 통해 이뤄짐
x86-64에서 주소는 64비트로 표현되지만, 실제로는 상위 16비트는 0.
why?
기술적 이유 + 역사적 호환성 + 하드웨어 제약 때문:
- 모든 주소를 64비트로 사용하려면 페이지 테이블이나 메모리 관리 구조도 훨씬 더 커짐.
- 아직 대부분의 시스템은 그 정도 메모리를 쓰지 않기 때문에 낭비를 줄이기 위해 48비트만 사용.
- 페이지 테이블 구조도 4단계 (4-level page table) 로 설계 → 이것이 48비트와 잘 맞음.
즉, 유효 주소 범위는0 ~ 2⁴⁸ - 1
(optional) Canonical Address(정규 주소)
x86-64는 주소의 상위 비트(16비트) 가 하위 48비트의 최상위 비트(47번째 비트) 값을 따라야 한다는 규칙.
0x0000 7FFF FFFF FFFF 유효한 주소 (상위 16비트 = 0)
비트 는 오른쪽에서 왼쪽으로 번호를 매김0x0000 = 0000 0000 0000 0000 0x7FFF = 0111 1111 1111 1111 0xFFFF = 1111 1111 1111 1111 0xFFFF = 1111 1111 1111 1111
multstore:
pushq %rbx # ① %rbx 값을 스택에 저장
movq %rdx, %rbx # ② %rbx = %rdx (rdx는 결과 저장할 주소)
call mult2 # ③ mult2 함수 호출 → 결과는 %rax에 저장됨
movq %rax, (%rbx) # ④ *rbx = rax (rax 결과를 주소 rbx에 저장)
popq %rbx # ⑤ %rbx 원래 값 복원
ret # ⑥ 함수 종료
pushq,popq 레지스터 %rbx는 원래 어떤 값(주소든 숫자든)을 갖고 있었는데,
함수 안에서 잠깐 다른 용도로 써야 하니까
→ 스택에 저장(pushq)했다가
→ 함수 끝나기 전에 다시 복구(popq)하는 것
정수, 주소, 임시값 등 여러 용도로 자유롭게 사용할 수 있는 레지스터들
| 이름 | 크기 | 역할 (관습적으로) |
|---|---|---|
%rax | 64비트 | 연산 결과 저장 (Accumulator) |
%rbx | 64비트 | callee-saved, 임시값 저장 |
%rcx | 64비트 | 루프 카운터, 시프트 연산 |
%rdx | 64비트 | 입출력, 함수 인자 전달 |
%rsi | 64비트 | 함수 인자 (source index) |
%rdi | 64비트 | 함수 인자 (destination index) |
%rbp | 64비트 | 프레임 포인터 (stack frame 기준점) |
%rsp | 64비트 | 스택 포인터 (현재 스택 꼭대기 주소) |
%r8 ~ %r15 | 64비트 | 함수 인자/임시 저장 (x86-64에서 추가된 레지스터들) |

1byte == 8 bits
데이터 이동 인스트럭션에는 크게 4가지 존재
movb, movw, movl, movq
(바이트, 워드, 더블워드, 쿼드워드)
더블워드의 l은 32bit가 long word로 간주되기 때문에 사용됨.