[컴퓨터구조론] RISC-V 명령어

티라노·2025년 3월 16일

컴퓨터구조론

목록 보기
2/18

RISC의 연산자

덧셈과 뺄셈

add a, b, c -> a = b + c
sub a, b, c, -> a = b - c
destination operand를 가장 앞에 둔다.

디자인 규칙

기능을 디자인할 때는 단순한 표준을 따른다.
예를 들어 add명령어와 sub명령어를 비슷한 포맷으로 제작해야 이해하기 쉬워진다. 또한 기능을 작은 단위로 나누어 한 명령 당 한 기능을 수행하게 한다.

Register operands

어셈블리 명령어는 레지스터에 있는 데이터만 연산할 수 있다. RISC-V에서 각 레지스터는 32비트의 크기를 지닌다.
ex) add x5, x6, x7에서 오퍼렌드는 모두 레지스터를 뜻하며 각각 32비트 크기이다.
이 경우 명령어는 x6과 x7레지스터에 할당된 값을 더해서 x5메모리에 적재한다고 해석할 수 있다.

데이터 크기에 따른 명칭
32-bit : word
64-bit : doubleword

  • 메모리 주소는 byte단위로 저장된다. 따라서 1, 2, 3...이 아니라 0, 4, 8...순서이다. 하지만 RISC-V에서는 예외적으로 1, 5, 9...로 저장될 수도 있다. (이 경우 성능 하락 가능성 있음)

명령어가 DRAM(메인 메모리) 대신 32비트로 크기가 작은 레지스터를 이용하는 이유는 속도가 빠르기 때문이다.

데이터 이동
Load(lw) : 메모리의 데이터를 사용하기 위해 레지스터로 이동
Store(sw) : 레지스터의 결과값을 메모리에 저장

따라서 메모리 상에 있는 데이터를 이용해 연산하려면 다음 절차가 필요하다.

요구하는 식 : x22[12] = x9 + x22[8]
기능하는 명령어 :
lw x9, 32(x22)
add x9, x21, x9
sw x9, 48(x22)

코드를 수행하는 동안 x22레지스터의 8번 인덱스(접근하기 위해 8*4=32 오프셋 필요)의 값을 load하고 덧셈한 뒤 x22레지스터의 12번 인덱스에 store하는 작용이 일어났다.

레지스터의 메모리

컴퓨터가 연산을 수행하려면 레지스터가 필요하지만 대부분의 프로그램에는 32개보다 많은 변수가 존재한다. 따라서 당장 사용하지 않는 데이터는 DRAM에 적재해야 하는데 이것을 spilling이라고 부른다. DRAM에 접근하는 것 자체가 속도 저하를 초래하기 때문에 spilling을 최소화하는 것이 좋다.


컴퓨터의 숫자 표현

Unsigned Binary Integers

n-bit 숫자가 있을 때 각 자리에 해당하는 weight를 곱해 나타낸다.

예를 들어 1011이라는 binary number가 있다면,
12^3 + 02^2 + 02^1 + 02^0 으로 변환할 수 있다.

2s-Complement Signed Integers

  • 첫 번째 비트가 0이면 양수, 1이면 음수이다.
  • 0은 양수가 아니지만 양수 범위의 한 자리를 차지하기 때문에(첫 비트가 0으로 시작) 일반적으로 양수와 음수의 표현 범위가 다르다.
  • 가장 작은 숫자는 1000 0000 ... 0000, 가장 큰 숫자는 0111 1111 ... 1111 이다.

계산법
2의 보수에서 덧셈하다가 비트 수가 넘치면 무시해도 된다.
뺄셈은 음수를 더하는 식으로 진행한다.

2의 보수에서 부호를 바꾸는 방법

모든 비트를 만대로 바꾼 뒤에 1을 더한다.

Sign Extention

  • 8비트에서 16비트
    sign bit를 숫자 왼쪽에 8개 추가한다.
    ex) 4는 8비트 상으로 0000 0100이므로 16비트로 나타낼 때는 0000 0000을 붙인다.

Immediate Operands

addi x22, x22, 4

Immediate 명령어를 사용하면 레지스터 값에 곧바로 연산한다.
메모리에 접근하는 명령어를 하나 줄일 수 있다. 명령어가 늘어나면서 하드웨어 동작이 복잡해질 수 있지만, 레지스터에 작은 상수를 더하는 동작은 워낙 자주 쓰이기 때문에 addi를 사용하면 성능을 개선할 수 있다.

그럼 상수끼리의 연산은?
상수+상수 정도의 간단한 연산은 하드웨어 단까지 오지 않고 컴파일 단계에서 처리된다.

교과서 2.8 문제

1. addi x30, x10, 8
여기에서 x10이 배열 A의 base address라면, x10+8은 A의 두 번째 요소(의 주소)를 불러오라는 의미이다.
-> x30 = &A[2]
2. addi x31, x10, 0
&A = &A[0]
-> x31 = &A
3. sw x31, 0(x30)
-> A[2] = &A
4. lw x30, 0(x30)
-> x30 = &A (=A[2])
5. add x5, x30, x31
-> x5 = &A + &A = 2(&A)

C식 코드 -> 어셈블리로 변환

// 코드
int *A, *B;
B = A + 1;

// 어셈블리로 변환
addi x11, x10, 4

0개의 댓글