본문 바로가기
Computer Architecture/ISA

[RISC-V] 컴퓨터구조 - Instruction to Machine Code

by FastBench 2024. 3. 11.

사전 참고

1. [RISC-V] 컴퓨터구조 - Instructions 개요  https://microelectronics.tistory.com/38

 

2.5 Representing Instructions in the Computer

이전 게시글에서 살펴본 명령어가 하드웨어적으로 인식이 되기위해서는 정해진 규칙에 맞게 이진수로 변환이 되어야 한다. 아래는 설명에 들어가기전 어셈블리어를 머신코드로 변환한 간단한 예시이다. 

이 미리 정해놓은 규격, 레이아웃을 instruction format 이라고 한다.

instruction format 에 맞게 명령어를 이진수로 변환시키면 그것이 바로 machine code 가 되는 것이다.

 

 

RISC-V Fields

 

R-type Instructions - Instructions using 3 register inputs

RISC-V 명령어들은 그 필드의 형태에 따라 다양한 타입으로 나뉘어 진다.

제일 먼저 확인해볼 타입은 R-Type 인데 각 필드가 의미하는 바는 아래와 같다.

  • opcode : 명령어의 기본 동작, 전통적 이름의 약어. (e.g load word -> lw)
  • rd: 목적지 레지스터 피연산자로 연산의 결과를 받는다.
  • funct3: 추가적인 opcode 필드.
  • rs1: 첫 번째 레지스터 소스 피연산자.
  • rs2: 두 번째 레지스터 소스 피연산자.
  • funct7: 추가적인 opcode 필드.

( funct3와 funct7 필드는 RISC-V 아키텍처에서 명령어의 세부 기능을 정의하고, 같은 opcode 내에서 다양한 연산을 구분한다.)

 

모든 명령어를 위와 같은 타입으로 구성할 수는 없다.

예를 들어 지난 게시글에서 다룬 load 명령어의 경우, base address register 와 destination register 그리고 offset 을 의미하는 상수가 필요하다.

그런데 위와 같은 타입(포맷)을 사용할 경우 상수의 크기는 31로 제한된다.(만약 rs 필드 중 하나를 상수값으로 쓴다면)

이는 충분하지 않다.

 

따라서 각 명령어의 용도에 맞게 포맷(레이아웃)은 다양하다.

 

I-type Instruction - instructions with immediates, loads

피연산자가 상수인 산술연산 명령어에 사용된다.

Immediate 값은 2의 보수가 적용되므로, -2^11 ~ 2^11 -1 범위의 상수가 표현 가능하다.

즉, load word 명령어 사용시 base address 로 부터 2048 바이트 떨어진 영역의 word 에 접근 가능하다.

 

 

S-type Instruction - store instructions

I-type 으로는 레지스터에 저장된 데이터를 특정 주소의 메모리에 저장할 수 없다.

 

왜냐하면 store 명령어에는 destination register 가 필요없고 소스레지스터가 두개 필요하기 때문이다.(rs1, rs2)

하나는 base address 그리고 나머지 하나는 저장할 데이터가 담긴 레지스터

 

 

 

지금 까지 다룬 3가지 타입의 명령어를 보면,  모두 공통으로 사용하는 필드의 경우 (e.g opcode, funct3) 같은 위치에 존재한다. 즉, LSB 부터 7,5,3,5... 로 각 필드의 width 가 일정하다.

RISC-V 명령어는 이런식으로 설계되어 하드웨어가 최대한 단순하게 설계될 수 있도록 하였다.

add 와 sub 의 경우, opode 그리고 funct3 은 같지만, funct7 에서 차이가 있다.

 

예제

 

2.6 Logical Operations

word 사이즈 데이터의 전체 연산보다, 개별 비트나 일부 필드에 대해 연산을 수행하는 것이 명백하게 유리할 때가 많다.

(특정 word 의 bit field 를 파싱하거나, 개별 비트를 검사하는 등의 경우)

slli, srli 의 경우 I-type 이지만 immediate 12 bit field 를 온전히 다 사용할 필요가 없으므로 (32비트 시스템 기준 5비트만 필요함) , 남는 부분은 funct 7 을 위해 사용한다.

 

RISC-V 는 세번째 타입의 shift 도 제공한다.

srl, srli 와 비슷하지만 왼쪽의 비워진 비트를 0으로 채우는 대신, sign bit 의 복사본으로 채운다.

이 방식은 음수 값을 우측으로 시프트할 때, 최상위 비트(부호 비트)가 1로 유지되어야 함을 보장하므로, 음수의 경우에도 올바른 산술 결과를 얻을 수 있다.

 

이외에도 AND, OR, XOR, NOR 등 익숙한 논리 연산이 명령어로 제공된다.

 

2.7 Instructions for Making Decisions

컴퓨터 프로그램이 의사결정을 하기 위해서는 branch 가 반드시 존재해야 한다.

  • conditional branches

L1 은 '라벨'을 의미하는데, 여기서는 어셈블리 코드에서 사용되는 특정 명령어의 '주소/위치' 정도로 생각해두면 된다.

두가지 코멘트가 있는데

beq 를 하지 않은 이유는, 컴파일러에 따라 다르겠지만 보통의 경우 값이 같지 않으므로 추가적인 점프를 피하기 위해, bne 를 하는 것이 더 유리하기 때문이다.

unconditional branch 는 무조건 점프를 하는 상황을 의미한다. RISC-V 에서는 이러한 방식으로 프로세서가 분기문을 따를 수 밖에 없도록 한다.

 

아래는 loop 문에 대한 예시이다.

( 이러한 경우와 반대로, 분기가 없는 명령어의 연속으로 이루어진 코드 부분을 'basic block' 이라고 한다.
basic block 은 코드 분기가 없기 때문에, 컴파일러가 불필요한 연산 제거와 같은 최적화를 더 쉽게 적용할 수 있다.)

 

더 다양한 분기조건을 위해, 아래와 같은 추가 분기 명령어가 존재한다.

  • branch if less than (blt)
  • branch if greater than or equal (bge)
  • branch if less than, unsigned (bltu)
  • branch if greater than or equal, unsigned (bgeu)

다른 ISA 의 경우, 이러한 추가 분기 명령어를 제공하는 대신 다른 방식으로 해결한다.

MIPS : 먼저 비교를 하고, 그 값을  beq, bne 로 분기한다. -> 하드웨어적으로 아주 약간 단순해지겠지만, 프로그램을 표현하는데 더 많은 명령어가 사용 된다.

ARM : 명령어 실행 중 발생한 것을 기록하는 추가 비트를 운영한다. 플래그라고도 부른다. 

 

Bounds Check Shortcut

signed number 를 unsigned number 처럼 처리하면, 단일 비교 연산으로 두 가지 조건(음수 검사와 최대값 검사)을 동시에 처리할 수 있다. 예를 들어  0 ≤ x < y 를 한번에 처리할 수 있다.

 

위와 같은 조건은 배열의 인덱스 범위 조건과 같은데, (x[5] 의 경우 정상 인덱스는 0부터 4까지이다)

이러한 오류를 런타임에 검사하는 코드를 Index out-of-bounds check 코드라고 한다.

 

즉, 아래와 같은 방식으로 런타임 성능을 향상 시킬 수 있다.

 

Case/Switch Statement

대부분의 프로그래밍 언어는 단일 값에 따라 여러 대안중 하나를 선택하는 case/switch 문을 가진다.

if then else 을 연속적으로 사용하여 switch 문을 구성할 수 있지만,

 

코드 내 라벨에 해당하는 주소를 포함하는 배열인, branch address table 을 사용한다면 더 효율적으로 코드내의 특정 위치로 직접 점프할 수 있다.

 

이러한 상황을 지원하기 위해, RISC-V 는 indirect jump 를 위한  jump-and-link-register (jalr) 명령어를 포함한다.

jalr 에 대한 자세한 내용은 다음 게시글에서 본격적으로 다룬다.

 


Reference

댓글