3 minute read

복잡한 구조의 데이터 관리, 접근을 낮은 레벨에서는 어떻게 하는가? 실수는 어떤 레지스터를 사용하는가?

1. 배열

변수 여러 개를 한꺼번에 관리할 때 사용하는 구조이다.

1.1. 1차원 배열

c에서는 1차원 배열을 다음과 같이 만들 수 있다:

T A[L];
  • T는 타입, L은 배열의 길이이다.
  • A는 0번째 element의 포인터로 사용된다. 타입은 따라서 T*.

1.1.1. 배열의 접근

배열의 i번째 원소에 어떻게 접근하는지 예시를 통해서 알아보자.

#define ZLEN 5
typedef int zip_dig[ZLEN];

int get_digit(zip_dig z, int digit){
    return z[digit];
}
; %rdi = z, %rsi = digit
get_digit:
	movq (%rdi,%rsi,4), %rax

int가 4바이트이기 때문에 z[digit]을 담고 있는 실제 주소는 z + 4*digit이다.

1.1.2. For 문과 섞인 예시

void zincr (zip_dig z){
    size_t i;
    for (i=0; i < ZLEN; i++)
        z[i]++;
}
zincr:
	movq $0, %rax
.L4
	addq $1, (%rdi,%rax,4)
	addq $1, %rax
.L3:
	cmpq 4, %rax
	jbe .L4
	ret

1.2. 다차원 배열

T A[R][C];

1.2.1. 배열의 접근

#define ZLEN 5
typedef int zip_dig[ZLEN];
#define ZLEN2 4
typedef zip_dig zip_dig_pgh[ZLEN2];
zip_dig_pgh pgh = {...};
int get_pgh_digit (int index, int dig){
    return pgh[index][dig];
}
; %rdi = index, %rsi = dig
get_pgh_digit:
	leaq (%rdi,%rdi,$4), %rax
	addl $rax, %rsi					; rsi = 5*index + dig
	movl pgh(,%rsi,$4), %rax
	ret

zip_dig의 크기가 4*5 = 20바이트이기 때문에 pgh[index][dig]에 접근하려면 pgh+20index + 4dig의 값을 불러와야 한다.

1.3. 배열 포인터 배열

zip_dig cmu = {0,0,0,0,0};
zip_dig mit = {0,0,0,0,0};
zip_dif ucb = {1,1,1,1,1};

# define UCOUNT 3
int *univ[UCOUNT] = {mit,cmu,ucb};

int get_univ_digit(size_t index, size_t digit){
    return univ[index][digit];
}
; %rdi = index, %rsi = digit
; objective: data in [univ + 8*index] + 4*digit
get_univ_digit:
	salq $2, %rsi
	addq univ(,%rdi,8), %rsi
	movq (%rsi), %rax
	ret

univ[index]는 univ + 8index에 저장된 int *이고, univ[index][digit]에 접근하려면 univ+8index에 저장된 값에 4*digit을 더해서 dereferencing해야 한다.

1.4. n X n 배열

int var_ele(size_t n, int a[n][n], size_t i, size_t j){
    return a[i][j];
}
; %rdi = n, %rsi = a, %rdx = i, %rcx = j
; [a+(4*n)*i + 4*j] = [a + 4(ni+j)]
var_ele:
	imulq %rdx, %rdi
	addq %rdi, %rcx
	movq %rsi(,%rcx,$4), %rax
	ret

2. 구조체 Struct

struct rec{
    int a[4];				// 16바이트
    size_t i;				//  8바이트
    struct rec *next;		//	8바이트
}
// a - size_t - next 순서로 저장된다!

void set_val(struct rec *r, int val){
    while (r){
        int i = r -> i;
        r -> a[i] = val;
        r = r -> next;
    }
}
; %rdi = r, %rsi = val
set_val:
.L11:
	movslq 16(%rdi), %rax		; sign extension (%rdi+16), which is long.
	movl %esi, (%rdi,%rax,4)
	movq 24(%rdi), %rdi
	testq %rdi, %rdi
	jne .L11
	ret

2.1. Alignment

  • struct 내부의 각 데이터에 대해 그 데이터의 대표 타입이 K 바이트면, 시작 주소도 K의 배수여야 한다.
  • 전체 struct의 위치도 K의 배수여야 한다. 그 K는 모든 element의 가장 큰 alignment이다.

이런 특성으로 큰 데이터 타입을 앞에 두면 공간을 아낄 수 있다. 예를 들면

struct S4 {			// 총 12바이트
    char c;			// 1바이트
    int i;			// 4바이트
    char d;			// 1바이트
}
struct S5 {			// 총 8바이트
    int i;			// 4바이트
    char c;			// 1바이트
    char d;			// 1바이트
}

2.2. 구조체의 배열

그냥 응용이다.

struct S3 {			// 12바이트
    short i;		// 2바이트
    float v;		// 4바이트
    short j;		// 2바이트
} a[10];

short get_j (int idx){
    return a[idx].j;
}
get_j:
	leaq (%rdi,%rdi,2), %rax
	movzwl a+8(,%rax,4), %eax

3. 실수

  • XMM 레지스터가 사용된다. 각 16바이트씩 16개가 존재한다.
    • %xmm0, %xmm1, …
  • 한 레지스터 안에 여러 개의 수를 저장할 수 있다.
    • 8 16-bit integers
    • 4 single precision floats
    • 2 double precision floats
    • 1 single precision float

3.1. add 연산

  • addss %xmm0, %xmm1: 레지스터 앞쪽의 single precision float addition을 한 번 한다.
  • addps %xmm0, %xmm1: 모든 레지스터 영역에 걸쳐 single precision float addition을 한다.
  • addsd %xmm0, %xmm1: 레지스터 앞쪽의 double precision float addition을 한 번 한다.

adds로 시작하는 연산을 Scalar 연산, addp로 시작하는 연산을 SIMD 연산이라고 한다.

3.2. 기초

  • 인자는 %xmm0, %xmm1, … 을 통해 전달된다.
  • 리턴값은 %xmm0이다.
  • 모든 XMM 레지스터는 caller-saved 이다.
float fadd(float x, float y){
    return x+y;
}
; %xmm0 = x, %xmm1 = y
fadd:
	addss %xmm1, %xmm0
	ret

3.3. Memory Referencing

mov instruction은 정수 전용이다. 따라서 실수에 대해서는 다른 instruction이 적용된다.

double dincr(double *p, double v){
    double x = *p;
    *p = x+v;
    return x;
}
; %rdi = p, %xmm0 = v
dincr:
	movapd %xmm0, %xmm1;	; double 실수를 다른 레지스터에 그대로 복사할 때 사용되는 명령
	movsd (%rdi), %xmm0;
	addsd %xmm0, %xmm1;
	movsd %xmm1, (%rdi)
	ret

movapd는 aligned packed-doubles의 약자이다.

Leave a comment