Assembly 제어 구조 및 프로그래밍
⭐⭐⭐ (핵심 섹션)
⭐⭐ (실용적인 내용)
⭐ (기초 이론)
⭐⭐ (실용적인 내용)
⭐ (기초 이론)
1. 제어 구조 ⭐⭐⭐
1.1 플래그 레지스터와 조건부 실행
RFLAGS 레지스터 심화
RFLAGS (64비트)
┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
│0│0│0│0│O│D│I│T│S│Z│0│A│0│P│1│C│
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
F E D C B A 9 8 7 6 5 4 3 2 1 0
핵심 플래그들:
• CF (Carry Flag, bit 0): 부호 없는 산술에서 오버플로우
• PF (Parity Flag, bit 2): 결과의 하위 8비트에서 1의 개수가 짝수
• AF (Auxiliary Flag, bit 4): BCD 연산에서 보조 캐리
• ZF (Zero Flag, bit 6): 결과가 0
• SF (Sign Flag, bit 7): 결과의 최상위 비트 (음수 여부)
• OF (Overflow Flag, bit 11): 부호 있는 산술에서 오버플로우
플래그 설정 패턴
; 다양한 연산이 플래그에 미치는 영향
mov rax, 0
add rax, 0 ; ZF=1, SF=0, CF=0, OF=0
mov rax, 0x7FFFFFFFFFFFFFFF
add rax, 1 ; ZF=0, SF=1, CF=0, OF=1 (부호있는 오버플로우)
mov rax, 0xFFFFFFFFFFFFFFFF
add rax, 1 ; ZF=1, SF=0, CF=1, OF=0 (부호없는 오버플로우)
; TEST 명령어 - 논리 AND 후 결과 버림
test rax, rax ; RAX가 0인지 확인 (ZF 설정)
test rax, 1 ; RAX의 최하위 비트 확인
test rax, 0x80000000 ; 특정 비트가 설정되었는지 확인
1.2 비교 명령어 상세
CMP 명령어의 내부 동작
; CMP dst, src는 내부적으로 SUB dst, src와 동일하지만 결과를 저장하지 않음
cmp rax, rbx ; 내부적으로 rax - rbx 계산
; 결과에 따른 플래그 설정:
; rax > rbx (부호없는): CF=0, ZF=0
; rax = rbx: ZF=1
; rax < rbx (부호없는): CF=1
; rax > rbx (부호있는): SF=OF, ZF=0
; rax < rbx (부호있는): SF≠OF
; 실제 예제
cmp rax, 100
; RAX = 150인 경우: CF=0, ZF=0, SF=0, OF=0 (150 > 100)
; RAX = 100인 경우: CF=0, ZF=1, SF=0, OF=0 (150 = 100)
; RAX = 50인 경우: CF=1, ZF=0, SF=1, OF=0 (50 < 100)
부호 있는 vs 부호 없는 비교
; 같은 값이라도 해석에 따라 결과가 다름
mov rax, 0xFFFFFFFFFFFFFFFF ; -1 (부호있는) 또는 큰 양수 (부호없는)
cmp rax, 0
; 부호 없는 비교로 해석하면
ja greater_unsigned ; 점프함 (큰 양수 > 0)
jb less_unsigned ; 점프 안함
; 부호 있는 비교로 해석하면
jg greater_signed ; 점프 안함 (-1 < 0)
jl less_signed ; 점프함
1.3 분기 명령어 체계
조건부 점프 명령어 전체 목록
; 부호 없는 비교 기준
ja / jnbe ; Jump if Above (CF=0 and ZF=0)
jae / jnb ; Jump if Above or Equal (CF=0)
jb / jnae ; Jump if Below (CF=1)
jbe / jna ; Jump if Below or Equal (CF=1 or ZF=1)
; 부호 있는 비교 기준
jg / jnle ; Jump if Greater (ZF=0 and SF=OF)
jge / jnl ; Jump if Greater or Equal (SF=OF)
jl / jnge ; Jump if Less (SF≠OF)
jle / jng ; Jump if Less or Equal (ZF=1 or SF≠OF)
; 등등 비교
je / jz ; Jump if Equal/Zero (ZF=1)
jne / jnz ; Jump if Not Equal/Not Zero (ZF=0)
; 개별 플래그 기준
jc ; Jump if Carry (CF=1)
jnc ; Jump if No Carry (CF=0)
jo ; Jump if Overflow (OF=1)
jno ; Jump if No Overflow (OF=0)
js ; Jump if Sign (SF=1)
jns ; Jump if No Sign (SF=0)
jp / jpe ; Jump if Parity Even (PF=1)
jnp / jpo ; Jump if Parity Odd (PF=0)
고급 분기 패턴
; 범위 체크 (0 ≤ value ≤ max)
bounds_check:
cmp rax, 0
jl out_of_bounds ; 음수인 경우
cmp rax, rdx ; rdx = 최댓값
ja out_of_bounds ; 최댓값 초과인 경우
; 정상 범위 처리
ret
out_of_bounds:
; 에러 처리
ret
; 3방향 비교 (comparable pattern)
three_way_compare:
cmp rax, rbx
jl first_less ; rax < rbx
jg first_greater ; rax > rbx
; rax == rbx인 경우
mov rcx, 0
ret
first_less:
mov rcx, -1
ret
first_greater:
mov rcx, 1
ret
1.4 반복문 구현 심화
고전적인 루프 패턴
; for (int i = 0; i < n; i++) 패턴
classic_for_loop:
mov rcx, 0 ; i = 0
.loop_start:
cmp rcx, r8 ; i < n (r8 = n)
jge .loop_end ; 조건 만족하지 않으면 종료
; 루프 바디
; ... 실제 작업들 ...
inc rcx ; i++
jmp .loop_start
.loop_end:
ret
; 최적화된 카운트다운 루프 (더 효율적)
optimized_countdown:
mov rcx, r8 ; rcx = n
.loop_start:
test rcx, rcx ; rcx가 0인지 확인 (CMP보다 빠름)
jz .loop_end ; 0이면 종료
; 루프 바디
; ... 실제 작업들 ...
dec rcx ; count--
jmp .loop_start ; 조건 체크 없이 바로 점프
.loop_end:
ret
고성능 루프 최적화 기법
; 루프 언롤링 (Loop Unrolling)
unrolled_copy_loop:
; 4개씩 처리하여 루프 오버헤드 감소
mov rcx, r8 ; 전체 개수
shr rcx, 2 ; 4로 나눔 (4개씩 처리)
.unrolled_loop:
test rcx, rcx
jz .handle_remainder
; 4개를 한 번에 처리
mov rax, [rsi]
mov [rdi], rax
mov rax, [rsi + 8]
mov [rdi + 8], rax
mov rax, [rsi + 16]
mov [rdi + 16], rax
mov rax, [rsi + 24]
mov [rdi + 24], rax
add rsi, 32
add rdi, 32
dec rcx
jmp .unrolled_loop
.handle_remainder:
; 나머지 처리 (0~3개)
mov rcx, r8
and rcx, 3 ; 나머지 계산
rep movsb ; 나머지 바이트들 복사
ret
; 브랜치 예측 최적화를 고려한 루프
branch_optimized_loop:
mov rcx, r8
.loop_start:
; 가장 가능성 높은 경우를 먼저 배치
dec rcx
jz .loop_end ; 루프 종료 조건을 마지막에
; 루프 바디 (가장 자주 실행되는 부분)
; ...
jmp .loop_start ; 무조건 점프 (예측하기 쉬움)
.loop_end:
ret
복잡한 반복 구조
; 중첩 루프 (이차원 배열 처리)
nested_loops:
; for (i = 0; i < rows; i++)
; for (j = 0; j < cols; j++)
; matrix[i][j] = i * cols + j
mov r9, 0 ; i = 0
.outer_loop:
cmp r9, r10 ; i < rows
jge .outer_end
mov r11, 0 ; j = 0
.inner_loop:
cmp r11, r12 ; j < cols
jge .inner_end
; 인덱스 계산: i * cols + j
mov rax, r9 ; i
imul rax, r12 ; i * cols
add rax, r11 ; i * cols + j
; 메모리 주소 계산 후 저장
mov [rsi + rax * 8], rax
inc r11 ; j++
jmp .inner_loop
.inner_end:
inc r9 ; i++
jmp .outer_loop
.outer_end:
ret
1.5 고급 제어 흐름 패턴
상태 머신 구현
; 간단한 문자열 파서 상태 머신
string_parser:
; 상태: 0=시작, 1=숫자읽기, 2=문자읽기, 3=종료
mov r8, 0 ; 현재 상태
mov r9, 0 ; 현재 인덱스
.parse_loop:
mov al, [rsi + r9] ; 현재 문자 읽기
test al, al ; 문자열 끝 확인
jz .state_end
; 상태별 점프 테이블
cmp r8, 3
jae .state_end
jmp [state_table + r8 * 8]
state_table:
dq .state_start ; 상태 0
dq .state_number ; 상태 1
dq .state_letter ; 상태 2
dq .state_end ; 상태 3
.state_start:
cmp al, '0'
jl .check_letter
cmp al, '9'
jle .to_number_state
.check_letter:
cmp al, 'A'
jl .parse_error
cmp al, 'Z'
jle .to_letter_state
jmp .parse_error
.to_number_state:
mov r8, 1
jmp .next_char
.to_letter_state:
mov r8, 2
jmp .next_char
.state_number:
; 숫자 상태에서의 처리
cmp al, '0'
jl .state_transition
cmp al, '9'
jle .next_char
jmp .state_transition
.state_letter:
; 문자 상태에서의 처리
cmp al, 'A'
jl .state_transition
cmp al, 'Z'
jle .next_char
.state_transition:
; 상태 전환 로직
mov r8, 0 ; 시작 상태로 돌아감
jmp .parse_loop
.next_char:
inc r9
jmp .parse_loop
.parse_error:
mov rax, -1 ; 에러 코드
ret
.state_end:
mov rax, 0 ; 성공
ret
점프 테이블을 이용한 스위치문
; switch-case 구문 최적화 구현
switch_optimized:
; RAX = switch value (0-7 범위 가정)
cmp rax, 7
ja .default_case
; 점프 테이블을 이용한 O(1) 분기
jmp [jump_table + rax * 8]
jump_table:
dq .case_0
dq .case_1
dq .case_2
dq .case_3
dq .case_4
dq .case_5
dq .case_6
dq .case_7
.case_0:
; case 0 처리
mov rbx, 100
jmp .switch_end
.case_1:
; case 1 처리
mov rbx, 200
jmp .switch_end
.case_2:
; case 2 처리
mov rbx, 300
jmp .switch_end
.case_3:
; case 3 처리 - fall through 예제
mov rbx, 400
; 다음 케이스로 계속 (break 없음)
.case_4:
; case 4 처리 (case 3에서도 실행됨)
add rbx, 50
jmp .switch_end
.case_5:
.case_6:
.case_7:
; 여러 케이스를 하나로 처리
mov rbx, 999
jmp .switch_end
.default_case:
; default case
mov rbx, -1
.switch_end:
ret
2. 함수와 스택 관리 ⭐⭐⭐
2.1 스택의 동작 원리 심화
스택 프레임의 완전한 구조
높은 주소
┌─────────────────┐ ← RSP (함수 호출 전)
│ 매개변수 n │
├─────────────────┤
│ 매개변수 7 │ (7번째 이후 매개변수들)
├─────────────────┤
│ 매개변수 6 │
├─────────────────┤
│ 반환 주소 │ ← call 명령어가 저장
├─────────────────┤ ← RSP (함수 진입 직후)
│ 이전 RBP │ ← push rbp로 저장
├─────────────────┤ ← RBP (새로운 베이스)
│ 지역변수 1 │
├─────────────────┤
│ 지역변수 2 │
├─────────────────┤
│ ... │
├─────────────────┤
│ 임시 저장소 │
├─────────────────┤ ← RSP (함수 실행 중)
│ (여유 공간) │
└─────────────────┘
낮은 주소
스택 오퍼레이션의 세부 사항
detailed_stack_operations:
; 함수 호출 전 스택 준비
push rax ; 인수 8 (오른쪽부터)
push rbx ; 인수 7
push rcx ; 인수 6
; 첫 6개 인수는 레지스터로: RDI, RSI, RDX, RCX, R8, R9
mov rdi, arg1
mov rsi, arg2
mov rdx, arg3
mov rcx, arg4
mov r8, arg5
mov r9, arg6
call complex_function
; 스택 정리 (caller가 책임)
add rsp, 24 ; 3개 * 8바이트 = 24바이트
ret
complex_function:
; 프롤로그 (Prologue)
push rbp ; 이전 베이스 포인터 저장
mov rbp, rsp ; 새로운 베이스 포인터 설정
sub rsp, 48 ; 지역변수용 공간 확보 (6개 * 8바이트)
; 호출자 저장 레지스터 보존 (필요한 경우만)
push rbx
push r12
push r13
; 매개변수 접근
mov rax, rdi ; 첫 번째 매개변수
mov rbx, rsi ; 두 번째 매개변수
mov rcx, [rbp + 16] ; 7번째 매개변수 (스택에서)
mov rdx, [rbp + 24] ; 8번째 매개변수
; 지역변수 접근
mov qword [rbp - 8], 100 ; 첫 번째 지역변수
mov qword [rbp - 16], 200 ; 두 번째 지역변수
; 함수 로직...
; 에필로그 (Epilogue)
pop r13 ; 보존된 레지스터 복원 (역순)
pop r12
pop rbx
mov rsp, rbp ; 스택 포인터 복원 (또는 add rsp, 48)
pop rbp ; 이전 베이스 포인터 복원
ret ; 반환 (RAX에 반환값)
2.2 호출 규약 (Calling Conventions) 상세
System V ABI (Linux/Unix)
; System V AMD64 ABI 완전 구현
sysv_function:
; 매개변수: RDI, RSI, RDX, RCX, R8, R9, 나머지는 스택
; 반환값: RAX, RDX (큰 값의 경우)
; 보존 레지스터: RBX, RSP, RBP, R12-R15
; 일시 레지스터: RAX, RCX, RDX, RSI, RDI, R8-R11
push rbp
mov rbp, rsp
sub rsp, 32 ; 지역변수 공간
; 보존해야 할 레지스터 저장
push rbx
push r12
; 부동소수점 매개변수는 XMM0-XMM7 사용
movsd xmm8, xmm0 ; 첫 번째 실수 매개변수 보존
; 함수 로직
mov rax, rdi ; 첫 번째 정수 매개변수
add rax, rsi ; 두 번째 정수 매개변수와 더함
; 정리
pop r12
pop rbx
mov rsp, rbp
pop rbp
ret ; 반환값은 RAX에
Microsoft x64 Calling Convention (Windows)
; Microsoft x64 호출 규약
windows_function:
; 매개변수: RCX, RDX, R8, R9, 나머지는 스택
; 반환값: RAX
; 보존 레지스터: RBX, RBP, RDI, RSI, RSP, R12-R15, XMM6-XMM15
; Shadow space: 32바이트 (caller가 할당)
push rbp
mov rbp, rsp
sub rsp, 64 ; 지역변수 + 정렬
; Shadow space는 이미 caller가 할당함
; 매개변수들을 shadow space에 저장 (디버깅용)
mov [rbp + 16], rcx ; 첫 번째 매개변수
mov [rbp + 24], rdx ; 두 번째 매개변수
mov [rbp + 32], r8 ; 세 번째 매개변수
mov [rbp + 40], r9 ; 네 번째 매개변수
; 보존 레지스터 저장
push rdi
push rsi
; 함수 로직
mov rax, rcx
add rax, rdx
imul rax, r8
; 정리
pop rsi
pop rdi
mov rsp, rbp
pop rbp
ret
2.3 고급 함수 패턴
재귀 함수 구현
; 팩토리얼 계산 (재귀)
factorial:
; 매개변수: RDI = n
; 반환값: RAX = n!
push rbp
mov rbp, rsp
push rbx ; n 값을 보존하기 위해
mov rbx, rdi ; n을 RBX에 저장
; 기저 조건: n <= 1이면 1 반환
cmp rdi, 1
jle .base_case
; 재귀 호출: factorial(n-1)
dec rdi ; n-1
call factorial ; factorial(n-1) 호출
; 결과: RAX = (n-1)!
imul rax, rbx ; RAX = n * (n-1)!
jmp .end
.base_case:
mov rax, 1
.end:
pop rbx
pop rbp
ret
; 꼬리 재귀 최적화 버전
factorial_tail_recursive:
; 매개변수: RDI = n, RSI = accumulator
; 반환값: RAX = n!
cmp rdi, 1
jle .return_acc
; accumulator = accumulator * n
imul rsi, rdi
; n = n - 1
dec rdi
; 꼬리 호출 (스택 프레임 재사용)
jmp factorial_tail_recursive
.return_acc:
mov rax, rsi
ret
가변 인수 함수
; printf 스타일 가변 인수 함수 (System V ABI)
my_printf:
; 첫 번째 매개변수(format string): RDI
; 가변 인수들: RSI, RDX, RCX, R8, R9, 그 다음은 스택
push rbp
mov rbp, rsp
sub rsp, 176 ; va_list 구조체용 공간
; va_list 구조체 초기화
mov dword [rbp - 176], 0 ; gp_offset (general purpose)
mov dword [rbp - 172], 48 ; fp_offset (floating point)
lea rax, [rbp + 16] ; overflow_arg_area
mov [rbp - 168], rax
lea rax, [rbp - 160] ; reg_save_area
mov [rbp - 160], rax
; 레지스터에 있는 인수들을 저장 영역에 백업
mov [rbp - 160], rsi ; 두 번째 인수
mov [rbp - 152], rdx ; 세 번째 인수
mov [rbp - 144], rcx ; 네 번째 인수
mov [rbp - 136], r8 ; 다섯 번째 인수
mov [rbp - 128], r9 ; 여섯 번째 인수
; XMM 레지스터들도 저장
movdqa [rbp - 96], xmm0
movdqa [rbp - 80], xmm1
movdqa [rbp - 64], xmm2
movdqa [rbp - 48], xmm3
movdqa [rbp - 32], xmm4
movdqa [rbp - 16], xmm5
; 실제 printf 로직은 여기에...
; format string 파싱하고 va_list에서 인수 추출
mov rsp, rbp
pop rbp
ret
함수 포인터와 콜백
; 함수 포인터 배열을 이용한 다형성
operation_table:
dq add_func
dq sub_func
dq mul_func
dq div_func
calculator:
; 매개변수: RDI = operation (0-3), RSI = a, RDX = b
; 반환값: RAX = result
push rbp
mov rbp, rsp
; 범위 체크
cmp rdi, 3
ja .invalid_op
; 함수 포인터 테이블에서 호출
push rsi ; 매개변수 보존
push rdx
call [operation_table + rdi * 8]
pop rdx ; 정리 (필요시)
pop rsi
jmp .end
.invalid_op:
mov rax, -1 ; 에러 코드
.end:
pop rbp
ret
add_func:
mov rax, rsi
add rax, rdx
ret
sub_func:
mov rax, rsi
sub rax, rdx
ret
mul_func:
mov rax, rsi
imul rax, rdx
ret
div_func:
mov rax, rsi
cqo ; RDX:RAX로 확장
idiv rdx
ret
2.4 스택 프레임 최적화
프레임 포인터 생략 최적화
; 프레임 포인터 사용 (일반적)
with_frame_pointer:
push rbp ; 8바이트
mov rbp, rsp ; 추가 명령어
sub rsp, 32 ; 지역변수 공간
mov [rbp - 8], rdi ; 지역변수 접근
mov [rbp - 16], rsi
add rsp, 32 ; 정리
pop rbp ; 8바이트 + 추가 명령어
ret
; 프레임 포인터 생략 (최적화)
without_frame_pointer:
sub rsp, 32 ; 지역변수 공간만 할당
mov [rsp + 24], rdi ; 직접 RSP 기준으로 접근
mov [rsp + 16], rsi
add rsp, 32 ; 간단한 정리
ret
; 주의: 프레임 포인터 생략 시 디버깅이 어려워질 수 있음
; 컴파일러 옵션: -fomit-frame-pointer
스택 할당 최적화
; 스택 탐지 회피 (Stack Probe Avoidance)
large_stack_allocation:
; 4KB 이상의 스택 할당 시 페이지 단위로 접근
mov rax, 8192 ; 8KB 할당 예정
.probe_loop:
sub rsp, 4096 ; 4KB씩 할당
mov [rsp], byte 0 ; 페이지 접근 (스택 가드 페이지 탐지)
sub rax, 4096
test rax, rax
jg .probe_loop
; 나머지 공간 할당
test rax, rax
jz .allocation_done
add rsp, rax ; 음수이므로 더하면 빼는 효과
.allocation_done:
; 큰 지역변수 사용...
add rsp, 8192 ; 전체 공간 해제
ret
3. 메모리와 데이터 구조 ⭐⭐⭐
3.1 배열 처리 심화
다차원 배열 접근
; 2차원 배열: matrix[rows][cols]
; 주소 계산: base + (row * cols + col) * element_size
matrix_access:
; 매개변수: RDI = base_addr, RSI = row, RDX = col,
; RCX = cols, R8 = element_size
; 반환값: RAX = element address
mov rax, rsi ; row
imul rax, rcx ; row * cols
add rax, rdx ; row * cols + col
imul rax, r8 ; (row * cols + col) * element_size
add rax, rdi ; base + offset
ret
; 3차원 배열: array[depth][rows][cols]
array_3d_access:
; 매개변수: RDI = base, RSI = d, RDX = r, RCX = c,
; R8 = rows, R9 = cols, [rsp+8] = element_size
push rbp
mov rbp, rsp
mov rax, rsi ; d
imul rax, r8 ; d * rows
add rax, rdx ; d * rows + r
imul rax, r9 ; (d * rows + r) * cols
add rax, rcx ; (d * rows + r) * cols + c
imul rax, [rbp + 16] ; * element_size
add rax, rdi ; + base
pop rbp
ret
; 고성능 행렬 곱셈 (캐시 최적화)
matrix_multiply_optimized:
; C = A * B (모두 NxN 행렬)
; 매개변수: RDI = A, RSI = B, RDX = C, RCX = N
push rbp
mov rbp, rsp
push rbx
push r12
push r13
push r14
push r15
mov r12, rdi ; A
mov r13, rsi ; B
mov r14, rdx ; C
mov r15, rcx ; N
; 블록 단위로 계산 (캐시 친화적)
mov r8, 0 ; i = 0
.i_loop:
cmp r8, r15
jge .i_done
mov r9, 0 ; j = 0
.j_loop:
cmp r9, r15
jge .j_done
; C[i][j] = 0으로 초기화
mov rax, r8
imul rax, r15
add rax, r9
mov qword [r14 + rax * 8], 0
mov r10, 0 ; k = 0
.k_loop:
cmp r10, r15
jge .k_done
; A[i][k] 주소 계산
mov rax, r8
imul rax, r15
add rax, r10
mov rbx, [r12 + rax * 8] ; A[i][k]
; B[k][j] 주소 계산
mov rax, r10
imul rax, r15
add rax, r9
mov rcx, [r13 + rax * 8] ; B[k][j]
; C[i][j] += A[i][k] * B[k][j]
imul rbx, rcx
mov rax, r8
imul rax, r15
add rax, r9
add [r14 + rax * 8], rbx
inc r10
jmp .k_loop
.k_done:
inc r9
jmp .j_loop
.j_done:
inc r8
jmp .i_loop
.i_done:
pop r15
pop r14
pop r13
pop r12
pop rbx
pop rbp
ret
3.2 포인터와 주소 연산
복잡한 포인터 조작
; 포인터의 포인터 (**ptr)
pointer_to_pointer:
; RDI = int** ptr
mov rax, [rdi] ; *ptr (int* 값)
mov rax, [rax] ; **ptr (int 값)
ret
; 함수 포인터 배열
function_pointer_array:
; RDI = 함수 포인터 배열, RSI = 인덱스
mov rax, [rdi + rsi * 8] ; 함수 주소 로드
call rax ; 간접 호출
ret
; 구조체 내 포인터 멤버 접근
struct_pointer_access:
; 구조체: { int* data; size_t count; char* name; }
; RDI = struct pointer, RSI = array index
mov rax, [rdi] ; struct->data
mov rbx, [rdi + 8] ; struct->count
; 범위 체크
cmp rsi, rbx
jae .out_of_bounds
mov rax, [rax + rsi * 4] ; data[index] (int는 4바이트)
ret
.out_of_bounds:
mov rax, -1
ret
동적 메모리 관리 패턴
; 간단한 메모리 풀 할당자
memory_pool:
.pool_start dq 0 ; 풀 시작 주소
.pool_size dq 0 ; 풀 크기
.current_pos dq 0 ; 현재 할당 위치
.allocated dq 0 ; 할당된 바이트 수
init_memory_pool:
; 매개변수: RDI = 풀 크기
push rbp
mov rbp, rsp
; mmap으로 메모리 할당
mov rax, 9 ; sys_mmap
mov rsi, rdi ; 크기
mov rdi, 0 ; 주소 (시스템이 결정)
mov rdx, 3 ; PROT_READ | PROT_WRITE
mov r10, 34 ; MAP_PRIVATE | MAP_ANONYMOUS
mov r8, -1 ; fd
mov r9, 0 ; offset
syscall
cmp rax, -1
je .alloc_failed
; 풀 정보 저장
mov [memory_pool.pool_start], rax
mov [memory_pool.pool_size], rsi
mov [memory_pool.current_pos], rax
mov qword [memory_pool.allocated], 0
mov rax, 0 ; 성공
jmp .end
.alloc_failed:
mov rax, -1 ; 실패
.end:
pop rbp
ret
pool_alloc:
; 매개변수: RDI = 할당할 크기
; 반환값: RAX = 할당된 주소 (실패시 0)
; 정렬 (8바이트 경계)
add rdi, 7
and rdi, -8
; 공간 충분한지 확인
mov rax, [memory_pool.allocated]
add rax, rdi
cmp rax, [memory_pool.pool_size]
ja .no_space
; 할당
mov rax, [memory_pool.current_pos]
add [memory_pool.current_pos], rdi
add [memory_pool.allocated], rdi
ret
.no_space:
xor rax, rax ; NULL 반환
ret
3.3 문자열 처리 고급
고성능 문자열 함수
; 최적화된 strlen (SSE 사용)
strlen_optimized:
; 매개변수: RDI = 문자열 주소
; 반환값: RAX = 길이
mov rsi, rdi ; 원본 주소 보존
; 16바이트 정렬 확인
mov rax, rdi
and rax, 15
jz .aligned
; 정렬되지 않은 부분 처리
.unaligned_loop:
cmp byte [rdi], 0
je .found_null
inc rdi
inc rax
cmp rax, 16
jne .unaligned_loop
.aligned:
; SSE를 이용한 16바이트씩 처리
pxor xmm0, xmm0 ; NULL 바이트 패턴
.sse_loop:
movdqa xmm1, [rdi] ; 16바이트 로드
pcmpeqb xmm1, xmm0 ; NULL 바이트 찾기
pmovmskb eax, xmm1 ; 비트마스크 생성
test eax, eax
jnz .found_in_chunk
add rdi, 16
jmp .sse_loop
.found_in_chunk:
; 정확한 위치 찾기
bsf eax, eax ; 첫 번째 설정된 비트 찾기
add rdi, rax
.found_null:
sub rdi, rsi ; 길이 계산
mov rax, rdi
ret
; 고성능 메모리 복사 (SIMD 최적화)
memcpy_optimized:
; 매개변수: RDI = dest, RSI = src, RDX = size
; 반환값: RAX = dest
push rdi ; 반환값용 dest 보존
; 작은 크기는 바이트 단위로 처리
cmp rdx, 32
jb .byte_copy
; 큰 크기는 SIMD로 처리
mov rcx, rdx
shr rcx, 5 ; 32바이트씩 처리할 횟수
.simd_loop:
movdqu xmm0, [rsi] ; 16바이트 로드
movdqu xmm1, [rsi + 16] ; 다음 16바이트
movdqu [rdi], xmm0 ; 16바이트 저장
movdqu [rdi + 16], xmm1 ; 다음 16바이트 저장
add rsi, 32
add rdi, 32
dec rcx
jnz .simd_loop
; 나머지 바이트 처리
and rdx, 31 ; 나머지 계산
.byte_copy:
mov rcx, rdx
rep movsb ; 바이트 단위 복사
pop rax ; dest 반환
ret
3.4 구조체와 공용체
복잡한 구조체 조작
; 구조체 정의 (매크로로)
%define PERSON_NAME_OFFSET 0
%define PERSON_AGE_OFFSET 64
%define PERSON_SALARY_OFFSET 68
%define PERSON_NEXT_OFFSET 76
%define PERSON_SIZE 84
; 연결 리스트 노드 구조체
; struct Person {
; char name[64]; // offset 0
; int age; // offset 64
; float salary; // offset 68
; struct Person* next; // offset 76
; };
create_person:
; 매개변수: RDI = name, RSI = age, XMM0 = salary
; 반환값: RAX = Person* (할당된 구조체 주소)
push rbp
mov rbp, rsp
push rdi
push rsi
sub rsp, 16
movss [rbp - 20], xmm0 ; salary 보존
; 메모리 할당
mov rdi, PERSON_SIZE
call malloc ; 시스템의 malloc 호출
test rax, rax
jz .alloc_failed
mov r8, rax ; 할당된 주소 보존
; name 복사
mov rdi, rax ; dest = struct
mov rsi, [rbp - 8] ; src = name parameter
mov rdx, 63 ; 최대 63문자 + null
call strncpy
; age 설정
mov rdi, [rbp - 16] ; age parameter
mov [r8 + PERSON_AGE_OFFSET], edi
; salary 설정
movss xmm0, [rbp - 20]
movss [r8 + PERSON_SALARY_OFFSET], xmm0
; next 포인터 초기화
mov qword [r8 + PERSON_NEXT_OFFSET], 0
mov rax, r8 ; 구조체 주소 반환
jmp .end
.alloc_failed:
xor rax, rax ; NULL 반환
.end:
mov rsp, rbp
pop rbp
ret
; 연결 리스트에 노드 추가
list_append:
; 매개변수: RDI = head*, RSI = new_person
; 반환값: RAX = new head
; 리스트가 비어있는 경우
cmp qword [rdi], 0
je .empty_list
; 마지막 노드 찾기
mov rax, [rdi] ; current = head
.find_last:
cmp qword [rax + PERSON_NEXT_OFFSET], 0
je .found_last
mov rax, [rax + PERSON_NEXT_OFFSET]
jmp .find_last
.found_last:
mov [rax + PERSON_NEXT_OFFSET], rsi
mov rax, [rdi] ; 기존 head 반환
ret
.empty_list:
mov [rdi], rsi ; head = new_person
mov rax, rsi ; new head 반환
ret
공용체 (Union) 활용
; 공용체: 같은 메모리 공간을 다른 타입으로 해석
; union Value {
; int64_t as_int;
; double as_double;
; void* as_ptr;
; char as_bytes[8];
; };
union_operations:
; 매개변수: RDI = union Value*, RSI = type (0=int, 1=double, 2=ptr)
cmp rsi, 0
je .handle_int
cmp rsi, 1
je .handle_double
cmp rsi, 2
je .handle_ptr
ret
.handle_int:
mov rax, [rdi] ; as_int로 읽기
; 정수 처리 로직
ret
.handle_double:
movsd xmm0, [rdi] ; as_double로 읽기
; 실수 처리 로직
ret
.handle_ptr:
mov rax, [rdi] ; as_ptr로 읽기
; 포인터 처리 로직
ret
4. 시스템 프로그래밍 ⭐⭐
4.1 시스템 호출 심화
Linux 시스템 호출 완전 가이드
; 주요 시스템 호출 번호들
%define SYS_READ 0
%define SYS_WRITE 1
%define SYS_OPEN 2
%define SYS_CLOSE 3
%define SYS_STAT 4
%define SYS_FSTAT 5
%define SYS_LSTAT 6
%define SYS_POLL 7
%define SYS_LSEEK 8
%define SYS_MMAP 9
%define SYS_MPROTECT 10
%define SYS_MUNMAP 11
%define SYS_BRK 12
%define SYS_RT_SIGACTION 13
%define SYS_RT_SIGPROCMASK 14
%define SYS_RT_SIGRETURN 15
%define SYS_IOCTL 16
%define SYS_PREAD64 17
%define SYS_PWRITE64 18
%define SYS_READV 19
%define SYS_WRITEV 20
%define SYS_ACCESS 21
%define SYS_PIPE 22
%define SYS_SELECT 23
%define SYS_SCHED_YIELD 24
%define SYS_MREMAP 25
%define SYS_MSYNC 26
%define SYS_MINCORE 27
%define SYS_MADVISE 28
%define SYS_SHMGET 29
%define SYS_SHMAT 30
%define SYS_SHMCTL 31
%define SYS_DUP 32
%define SYS_DUP2 33
%define SYS_PAUSE 34
%define SYS_NANOSLEEP 35
%define SYS_GETITIMER 36
%define SYS_ALARM 37
%define SYS_SETITIMER 38
%define SYS_GETPID 39
%define SYS_SENDFILE 40
%define SYS_SOCKET 41
%define SYS_CONNECT 42
%define SYS_ACCEPT 43
%define SYS_SENDTO 44
%define SYS_RECVFROM 45
%define SYS_SENDMSG 46
%define SYS_RECVMSG 47
%define SYS_SHUTDOWN 48
%define SYS_BIND 49
%define SYS_LISTEN 50
%define SYS_GETSOCKNAME 51
%define SYS_GETPEERNAME 52
%define SYS_SOCKETPAIR 53
%define SYS_SETSOCKOPT 54
%define SYS_GETSOCKOPT 55
%define SYS_CLONE 56
%define SYS_FORK 57
%define SYS_VFORK 58
%define SYS_EXECVE 59
%define SYS_EXIT 60
; 고급 파일 조작
advanced_file_operations:
push rbp
mov rbp, rsp
sub rsp, 144 ; stat 구조체용 공간
; 파일 열기 (O_RDWR | O_CREAT, 0644)
mov rax, SYS_OPEN
mov rdi, filename
mov rsi, 0x42 ; O_RDWR | O_CREAT
mov rdx, 0644o ; 권한
syscall
cmp rax, 0
jl .error
mov r12, rax ; 파일 디스크립터 보존
; 파일 상태 정보 가져오기
mov rax, SYS_FSTAT
mov rdi, r12
lea rsi, [rbp - 144] ; stat 구조체 주소
syscall
cmp rax, 0
jl .close_and_error
; 파일 크기 확인 (stat.st_size는 offset 48)
mov r13, [rbp - 96] ; st_size
; 메모리 매핑으로 파일 읽기
mov rax, SYS_MMAP
mov rdi, 0 ; 주소 (시스템이 결정)
mov rsi, r13 ; 크기
mov rdx, 1 ; PROT_READ
mov r10, 1 ; MAP_SHARED
mov r8, r12 ; 파일 디스크립터
mov r9, 0 ; 오프셋
syscall
cmp rax, -1
je .close_and_error
mov r14, rax ; 매핑된 주소
; 매핑된 메모리 사용 (예: 체크섬 계산)
mov rdi, r14 ; 데이터 주소
mov rsi, r13 ; 크기
call calculate_checksum
; 메모리 매핑 해제
mov rax, SYS_MUNMAP
mov rdi, r14
mov rsi, r13
syscall
; 파일 닫기
mov rax, SYS_CLOSE
mov rdi, r12
syscall
mov rax, 0 ; 성공
jmp .end
.close_and_error:
mov rax, SYS_CLOSE
mov rdi, r12
syscall
.error:
mov rax, -1 ; 실패
.end:
mov rsp, rbp
pop rbp
ret
filename db '/tmp/testfile', 0
4.2 프로세스 관리
fork와 exec 시스템 호출
process_management:
push rbp
mov rbp, rsp
; fork() 시스템 호출
mov rax, SYS_FORK
syscall
cmp rax, 0
jl .fork_error ; 에러
je .child_process ; 자식 프로세스
; 부모 프로세스 코드
.parent_process:
mov r12, rax ; 자식 PID 저장
; 자식 프로세스 대기
mov rax, SYS_WAIT4
mov rdi, r12 ; 자식 PID
mov rsi, 0 ; status (NULL)
mov rdx, 0 ; options
mov r10, 0 ; rusage (NULL)
syscall
; 부모 작업 계속...
jmp .end
.child_process:
; 새 프로그램 실행
mov rax, SYS_EXECVE
mov rdi, program_path
mov rsi, argv
mov rdx, envp
syscall
; execve가 성공하면 여기에 도달하지 않음
; 실패한 경우만 여기 도달
mov rax, SYS_EXIT
mov rdi, 1 ; 에러 코드
syscall
.fork_error:
; fork 실패 처리
mov rax, -1
.end:
pop rbp
ret
program_path db '/bin/ls', 0
argv:
dq program_path
dq ls_args
dq 0
ls_args db '-la', 0
envp dq 0
4.3 신호 처리 (Signal Handling)
신호 핸들러 구현
; 신호 핸들러 구조체
struc sigaction
.sa_handler resq 1 ; 핸들러 함수 포인터
.sa_flags resd 1 ; 플래그
.sa_restorer resq 1 ; 복원 함수
.sa_mask resb 128 ; 신호 마스크 (sigset_t)
endstruc
signal_handler_setup:
push rbp
mov rbp, rsp
sub rsp, sigaction_size
; sigaction 구조체 초기화
lea rdi, [rbp - sigaction_size]
mov rsi, 0
mov rdx, sigaction_size
call memset
; 핸들러 함수 설정
lea rax, [rbp - sigaction_size]
mov qword [rax + sigaction.sa_handler], signal_handler
mov dword [rax + sigaction.sa_flags], 0x04000000 ; SA_RESTORER
mov qword [rax + sigaction.sa_restorer], signal_restorer
; SIGINT (Ctrl+C) 핸들러 등록
mov rax, SYS_RT_SIGACTION
mov rdi, 2 ; SIGINT
lea rsi, [rbp - sigaction_size] ; new action
mov rdx, 0 ; old action (NULL)
mov r10, 8 ; sigset 크기
syscall
cmp rax, 0
jl .error
mov rax, 0 ; 성공
jmp .end
.error:
mov rax, -1
.end:
mov rsp, rbp
pop rbp
ret
; 신호 핸들러 함수
signal_handler:
; 신호 핸들러는 재진입 가능해야 함
; 최소한의 작업만 수행
push rax
push rdi
push rsi
push rdx
; 안전한 작업만 수행 (async-signal-safe 함수만)
mov rax, SYS_WRITE
mov rdi, 2 ; stderr
mov rsi, sig_msg
mov rdx, sig_msg_len
syscall
pop rdx
pop rsi
pop rdi
pop rax
; 신호 핸들러 종료
ret
signal_restorer:
; 신호 처리 후 시스템이 호출하는 복원 함수
mov rax, SYS_RT_SIGRETURN
syscall
sig_msg db 'Signal received!', 0xA
sig_msg_len equ $ - sig_msg
4.4 네트워크 프로그래밍
소켓 프로그래밍 기초
; 소켓 주소 구조체
struc sockaddr_in
.sin_family resw 1 ; 주소 패밀리 (AF_INET)
.sin_port resw 1 ; 포트 번호 (네트워크 바이트 순서)
.sin_addr resd 1 ; IP 주소
.sin_zero resb 8 ; 패딩
endstruc
tcp_server:
push rbp
mov rbp, rsp
sub rsp, sockaddr_in_size
; 소켓 생성 (AF_INET, SOCK_STREAM, 0)
mov rax, SYS_SOCKET
mov rdi, 2 ; AF_INET
mov rsi, 1 ; SOCK_STREAM
mov rdx, 0 ; protocol
syscall
cmp rax, 0
jl .error
mov r12, rax ; 소켓 디스크립터 저장
; 소켓 주소 설정
lea rdi, [rbp - sockaddr_in_size]
mov word [rdi + sockaddr_in.sin_family], 2 ; AF_INET
mov word [rdi + sockaddr_in.sin_port], 0x5000 ; 포트 80 (네트워크 바이트 순서)
mov dword [rdi + sockaddr_in.sin_addr], 0 ; INADDR_ANY
; 바인드
mov rax, SYS_BIND
mov rdi, r12 ; 소켓
lea rsi, [rbp - sockaddr_in_size] ; 주소
mov rdx, sockaddr_in_size ; 주소 크기
syscall
cmp rax, 0
jl .close_and_error
; 리슨
mov rax, SYS_LISTEN
mov rdi, r12 ; 소켓
mov rsi, 5 ; 백로그 큐 크기
syscall
cmp rax, 0
jl .close_and_error
.accept_loop:
; 클라이언트 연결 수락
mov rax, SYS_ACCEPT
mov rdi, r12 ; 서버 소켓
mov rsi, 0 ; 클라이언트 주소 (NULL)
mov rdx, 0 ; 주소 길이 (NULL)
syscall
cmp rax, 0
jl .accept_loop ; 에러시 다시 시도
mov r13, rax ; 클라이언트 소켓
; 클라이언트 처리
call handle_client
; 클라이언트 소켓 닫기
mov rax, SYS_CLOSE
mov rdi, r13
syscall
jmp .accept_loop
.close_and_error:
mov rax, SYS_CLOSE
mov rdi, r12
syscall
.error:
mov rax, -1
.end:
mov rsp, rbp
pop rbp
ret
handle_client:
; 매개변수: R13 = 클라이언트 소켓
push rbp
mov rbp, rsp
sub rsp, 1024 ; 버퍼
; 데이터 읽기
mov rax, SYS_READ
mov rdi, r13
lea rsi, [rbp - 1024]
mov rdx, 1023
syscall
cmp rax, 0
jle .client_end
; 응답 전송
mov rax, SYS_WRITE
mov rdi, r13
mov rsi, http_response
mov rdx, http_response_len
syscall
.client_end:
mov rsp, rbp
pop rbp
ret
http_response db 'HTTP/1.1 200 OK', 0xD, 0xA
db 'Content-Type: text/html', 0xD, 0xA
db 'Content-Length: 13', 0xD, 0xA
db 0xD, 0xA
db 'Hello, World!', 0
http_response_len equ $ - http_response
5. 실습 프로젝트와 최적화 ⭐⭐
5.1 종합 프로젝트: 간단한 텍스트 에디터
핵심 데이터 구조
; 텍스트 버퍼 구조
struc text_buffer
.data resq 1 ; 텍스트 데이터 포인터
.capacity resq 1 ; 전체 용량
.size resq 1 ; 현재 크기
.cursor_pos resq 1 ; 커서 위치
.gap_start resq 1 ; 갭 버퍼 시작
.gap_end resq 1 ; 갭 버퍼 끝
endstruc
; 갭 버퍼를 이용한 효율적인 텍스트 편집
text_editor:
push rbp
mov rbp, rsp
sub rsp, text_buffer_size
; 텍스트 버퍼 초기화
lea rdi, [rbp - text_buffer_size]
call init_text_buffer
; 메인 편집 루프
.edit_loop:
call get_key_input
cmp al, 27 ; ESC
je .exit
cmp al, 8 ; Backspace
je .handle_backspace
cmp al, 127 ; Delete
je .handle_delete
cmp al, 13 ; Enter
je .handle_enter
cmp al, 32 ; 일반 문자 (32 이상)
jae .handle_char
; 특수 키 처리
jmp .edit_loop
.handle_char:
lea rdi, [rbp - text_buffer_size]
mov rsi, rax ; 문자
call insert_char
jmp .update_display
.handle_backspace:
lea rdi, [rbp - text_buffer_size]
call delete_backward
jmp .update_display
.handle_delete:
lea rdi, [rbp - text_buffer_size]
call delete_forward
jmp .update_display
.handle_enter:
lea rdi, [rbp - text_buffer_size]
mov rsi, 10 ; newline
call insert_char
jmp .update_display
.update_display:
lea rdi, [rbp - text_buffer_size]
call render_buffer
jmp .edit_loop
.exit:
; 정리
lea rdi, [rbp - text_buffer_size]
call cleanup_text_buffer
mov rsp, rbp
pop rbp
ret
init_text_buffer:
; 매개변수: RDI = text_buffer*
push rbp
mov rbp, rsp
push rdi
; 초기 용량 할당 (4KB)
mov rax, SYS_MMAP
mov rdi, 0
mov rsi, 4096
mov rdx, 3 ; PROT_READ | PROT_WRITE
mov r10, 34 ; MAP_PRIVATE | MAP_ANONYMOUS
mov r8, -1
mov r9, 0
syscall
pop rdi
mov [rdi + text_buffer.data], rax
mov qword [rdi + text_buffer.capacity], 4096
mov qword [rdi + text_buffer.size], 0
mov qword [rdi + text_buffer.cursor_pos], 0
mov qword [rdi + text_buffer.gap_start], 0
mov [rdi + text_buffer.gap_end], rax
add qword [rdi + text_buffer.gap_end], 4096
pop rbp
ret
insert_char:
; 매개변수: RDI = text_buffer*, RSI = character
push rbp
mov rbp, rsp
; 갭 버퍼에 문자 삽입
mov rax, [rdi + text_buffer.gap_start]
mov [rax], sil
inc qword [rdi + text_buffer.gap_start]
inc qword [rdi + text_buffer.size]
inc qword [rdi + text_buffer.cursor_pos]
; 갭이 너무 작아지면 확장
mov rax, [rdi + text_buffer.gap_end]
sub rax, [rdi + text_buffer.gap_start]
cmp rax, 100 ; 최소 갭 크기
jg .end
call expand_gap_buffer
.end:
pop rbp
ret
5.2 성능 최적화 기법
CPU 캐시 최적화
; 캐시 친화적인 행렬 전치
cache_friendly_transpose:
; 매개변수: RDI = matrix, RSI = rows, RDX = cols
push rbp
mov rbp, rsp
push r12
push r13
push r14
push r15
mov r12, rdi ; matrix
mov r13, rsi ; rows
mov r14, rdx ; cols
; 블록 크기 (캐시 라인에 맞춤)
mov r15, 8 ; 8x8 블록
mov r8, 0 ; block_row = 0
.block_row_loop:
cmp r8, r13
jge .done
mov r9, 0 ; block_col = 0
.block_col_loop:
cmp r9, r14
jge .next_block_row
; 8x8 블록 전치
mov r10, 0 ; i = 0
.inner_row_loop:
cmp r10, r15
jge .next_block_col
mov rax, r8
add rax, r10 ; block_row + i
cmp rax, r13
jge .next_block_col
mov r11, 0 ; j = 0
.inner_col_loop:
cmp r11, r15
jge .next_inner_row
mov rbx, r9
add rbx, r11 ; block_col + j
cmp rbx, r14
jge .next_inner_row
; matrix[i][j]와 matrix[j][i] 교환
cmp rax, rbx
jle .skip_swap ; 대각선 아래만 처리
; 주소 계산
mov rcx, rax
imul rcx, r14
add rcx, rbx
lea rcx, [r12 + rcx * 8] ; matrix[i][j] 주소
mov rdx, rbx
imul rdx, r14
add rdx, rax
lea rdx, [r12 + rdx * 8] ; matrix[j][i] 주소
; 교환
mov rsi, [rcx]
mov rdi, [rdx]
mov [rcx], rdi
mov [rdx], rsi
.skip_swap:
inc r11
jmp .inner_col_loop
.next_inner_row:
inc r10
jmp .inner_row_loop
.next_block_col:
add r9, r15
jmp .block_col_loop
.next_block_row:
add r8, r15
jmp .block_row_loop
.done:
pop r15
pop r14
pop r13
pop r12
pop rbp
ret
SIMD 최적화
; AVX2를 이용한 벡터 덧셈
vector_add_avx2:
; 매개변수: RDI = a[], RSI = b[], RDX = result[], RCX = count
push rbp
mov rbp, rsp
; 32바이트씩 처리 (8개의 32비트 정수)
mov rax, rcx
shr rax, 3 ; count / 8
.avx_loop:
test rax, rax
jz .handle_remainder
vmovdqu ymm0, [rdi] ; a[i:i+7] 로드
vmovdqu ymm1, [rsi] ; b[i:i+7] 로드
vpaddd ymm2, ymm0, ymm1 ; 벡터 덧셈
vmovdqu [rdx], ymm2 ; result[i:i+7] 저장
add rdi, 32
add rsi, 32
add rdx, 32
dec rax
jmp .avx_loop
.handle_remainder:
; 나머지 원소들 처리
and rcx, 7 ; count % 8
.scalar_loop:
test rcx, rcx
jz .end
mov eax, [rdi]
add eax, [rsi]
mov [rdx], eax
add rdi, 4
add rsi, 4
add rdx, 4
dec rcx
jmp .scalar_loop
.end:
vzeroupper ; AVX 상태 정리
pop rbp
ret
분기 예측 최적화
; 분기 예측을 고려한 이진 탐색
binary_search_optimized:
; 매개변수: RDI = array, RSI = size, RDX = target
; 반환값: RAX = index (없으면 -1)
mov rax, 0 ; left = 0
mov rcx, rsi ; right = size
dec rcx
.search_loop:
cmp rax, rcx
jg .not_found
; 중간값 계산 (오버플로우 방지)
mov r8, rax
add r8, rcx
shr r8, 1 ; mid = (left + right) / 2
; array[mid] 로드
mov r9, [rdi + r8 * 8]
; 가장 가능성 높은 경우를 먼저 (보통 target < array[mid])
cmp rdx, r9
jl .search_left
jg .search_right
; 찾았음
mov rax, r8
ret
.search_left:
; right = mid - 1
mov rcx, r8
dec rcx
jmp .search_loop
.search_right:
; left = mid + 1
mov rax, r8
inc rax
jmp .search_loop
.not_found:
mov rax, -1
ret
5.3 메모리 최적화
커스텀 메모리 할당자
; 고정 크기 블록 할당자
fixed_size_allocator:
.pool_start dq 0
.pool_end dq 0
.block_size dq 0
.free_list dq 0 ; 자유 블록 연결 리스트
.total_blocks dq 0
.used_blocks dq 0
init_fixed_allocator:
; 매개변수: RDI = block_size, RSI = block_count
push rbp
mov rbp, rsp
; 블록 크기를 8바이트 경계로 정렬
add rdi, 7
and rdi, -8
mov [fixed_size_allocator.block_size], rdi
; 필요한 전체 메모리 크기 계산
imul rdi, rsi
mov [fixed_size_allocator.total_blocks], rsi
; 메모리 할당
mov rax, SYS_MMAP
mov rdi, 0
mov rsi, rdi ; 전체 크기
mov rdx, 3 ; PROT_READ | PROT_WRITE
mov r10, 34 ; MAP_PRIVATE | MAP_ANONYMOUS
mov r8, -1
mov r9, 0
syscall
cmp rax, -1
je .error
mov [fixed_size_allocator.pool_start], rax
add rax, rdi
mov [fixed_size_allocator.pool_end], rax
; 자유 블록 리스트 초기화
call init_free_list
mov rax, 0 ; 성공
jmp .end
.error:
mov rax, -1
.end:
pop rbp
ret
fixed_alloc:
; 반환값: RAX = 할당된 블록 주소 (실패시 0)
; 자유 블록이 있는지 확인
mov rax, [fixed_size_allocator.free_list]
test rax, rax
jz .no_blocks
; 첫 번째 자유 블록 제거
mov rdx, [rax] ; 다음 자유 블록
mov [fixed_size_allocator.free_list], rdx
; 사용 블록 수 증가
inc qword [fixed_size_allocator.used_blocks]
ret
.no_blocks:
xor rax, rax ; NULL 반환
ret
fixed_free:
; 매개변수: RDI = 해제할 블록 주소
; 유효성 검사
mov rax, [fixed_size_allocator.pool_start]
cmp rdi, rax
jl .invalid
mov rax, [fixed_size_allocator.pool_end]
cmp rdi, rax
jge .invalid
; 자유 리스트에 추가
mov rax, [fixed_size_allocator.free_list]
mov [rdi], rax ; 다음 포인터 설정
mov [fixed_size_allocator.free_list], rdi
; 사용 블록 수 감소
dec qword [fixed_size_allocator.used_blocks]
.invalid:
ret
5.4 프로파일링과 성능 측정
사이클 정확한 타이밍
; RDTSC를 이용한 정밀 타이밍
performance_timer:
.start_time dq 0
.end_time dq 0
start_timer:
; CPU 직렬화 (정확한 측정을 위해)
cpuid
; 타임스탬프 읽기
rdtsc
shl rdx, 32
or rax, rdx
mov [performance_timer.start_time], rax
ret
end_timer:
; 타임스탬프 읽기
rdtsc
shl rdx, 32
or rax, rdx
mov [performance_timer.end_time], rax
; CPU 직렬화
mov eax, 0
cpuid
ret
get_elapsed_cycles:
; 반환값: RAX = 경과 사이클 수
mov rax, [performance_timer.end_time]
sub rax, [performance_timer.start_time]
ret
; 성능 테스트 프레임워크
benchmark_function:
; 매개변수: RDI = 테스트할 함수 포인터, RSI = 반복 횟수
push rbp
mov rbp, rsp
push r12
push r13
push r14
mov r12, rdi ; 함수 포인터
mov r13, rsi ; 반복 횟수
xor r14, r14 ; 누적 시간
.benchmark_loop:
test r13, r13
jz .calculate_average
call start_timer
call r12 ; 테스트 함수 호출
call end_timer
call get_elapsed_cycles
add r14, rax ; 누적
dec r13
jmp .benchmark_loop
.calculate_average:
mov rax, r14
xor rdx, rdx
div rsi ; 평균 계산
pop r14
pop r13
pop r12
pop rbp
ret
핵심 포인트 요약
제어 구조 마스터리:
- 플래그 레지스터 완전 이해
- 조건부 분기 최적화 패턴
- 루프 언롤링과 성능 최적화
- 상태 머신과 점프 테이블 활용
함수와 스택:
- 호출 규약 완벽 숙지 (System V vs Microsoft)
- 스택 프레임 구조와 최적화
- 재귀 함수와 꼬리 재귀 최적화
- 가변 인수 처리
메모리 관리:
- 포인터 연산과 복잡한 데이터 구조
- 동적 메모리 할당과 관리
- 캐시 친화적 프로그래밍
- 메모리 정렬과 최적화
시스템 프로그래밍:
- 시스템 호출 활용
- 프로세스 관리와 IPC
- 신호 처리와 비동기 프로그래밍
- 네트워크 소켓 프로그래밍
성능 최적화:
- SIMD 명령어 활용 (SSE, AVX)
- 분기 예측 최적화
- 캐시 계층 고려한 알고리즘
- 프로파일링과 성능 측정