컴퓨터구조

[컴퓨터 구조]제 2장. 명령어(Instruction) : 컴퓨터의 언어 (2)

촙발자 2023. 4. 8. 20:00

Registers vs Memory

레지스터는 메모리에 비해 access 속도가 더 빠르다.

Risc-V에서 메모리에 있는 데이터는 arithmetic instructions에 바로 접근하지 못합니다.

→ 메모리 데이터는 실행을 위해 loadsstores가 필요합니다.

즉 더 많은 instruction이 실행되어야 하죠.

메모리는 실행을 위해 BUS를 통해야 한다.

 

컴파일러는 가능한한 레지스터를 통하여 변수를 처리하여야 한다.

즉 덜 자주사용 되는 변수는 memory로 spill 해야 하며, 이 과정을 Spilling Register이라고 합니다.

→ 그렇기에 Register Optimization은 중요합니다.

 

즉 덜 자주사용 되는 변수는 memory로 spill 해야 하며, 이 과정을 Spilling Register이라고 합니다.

→ 그렇기에 Register Optimization은 중요합니다.

 


Constant or Immediate Operands

많은 경우 프로그램은 상수를 사용합니다.

실제로, 절반 이상의 RISC-V arithmetic instruction이 operand로써 상수를 사용합니다.

 

이때마다, 상수를 메모리에서 레지스터로 load를 하는 것은 굉장히 비효율적입니다.

ex) x22 레지스터에 상수 4를 더하는 경우

ex) x22 레지스터에 상수 4를 더하는 경우

lw    x9 , AddrConstant4(x3)      //x9 = constant 4
add   x22 , x22 , x9              //x22 = x22 + x9 (where x9 == 4)

이런 식을

add가 아니라 addi (addi immediate)라는 특별한 타입을 만들어서 상수를 바로 더할 수 있게 만들었습니다.

  • 해당 Instruction을 사용하는 것은 상수가 메모리로부터 load 되는 것보다 더 빠르고 더 적은 에너지를 소비합니다.

이러한 개념에서 나온 것이 Hardwired Register입니다.

 

Register x0은 상수 0이 할당되어 있습니다.

x0은 굉장히 자주 쓰이고 실용적인데

예예를 들어 양수를 음수로 바꿀 때,

sub   x1 , x0 , x1 ---> x1 = -x1

이 됩니다.

 

x0은 0으로 고정되어 있기에, lw나 비슷한 명령어를 사용하더라도 무시됩니다.

 

 


Binary Numbers

  • 숫자들은 여러 진수들로 표현이 가능합니다.

ex) 123 base 10 = 1111011 base 2

  • 2진수에서 하나의 digit는 더 이상 쪼개질 수 없는 자릿수입니다.

[모든 정보들은 binary digit(bit)로 이루어져 있습니다.]

endian이랑 MSB & LSB를 혼동하면 안 됨

💡endian은 byte단위로 데이터를 저장하는 방식이고, MSB & LSB는 bit단위로 저장하는 방식.

 

Negation and Sign Extension

  • To negate the number
~x + 1 = -x       // "~"는 보수를 나타냅니다. ex) ~(0001)은 1110
  • Sign extension(부호 확장)은 더 많은 비트로 확장이 될 때 MSB의 수로 채워 넣는 것입니다.

ex) +2 : 0000 0010 → 0000 0000 0000 0010

      -2 : 1111 1110   → 1111 1111 1111 1110

sign extension은 프로그래머가 사용할지 말지 지정하는 것입니다.

lb(load byte), lh(load halfword), lw(load word) 같은 instruction로 지정.

 

 


Instruction Format

Instruction 은 기계어라 불리는 2진수로 Encode 됩니다.

(Instruction의 숫자버전은 기계어입니다.)

  • 모든 명령어는 디자인 규칙에 의하여 32비트로 제한되며, 이 32비트 안에 모든 것을 표현해야 합니다.
  • 이때 명령어를 여러 타입으로 나누어서 표현하게 만들었는데, 이를 Instruction Format이라고 표현합니다.

Instruction Format은 대표적으로 3가지 타입으로 분류됩니다.

  • R-Type(Register Type) : most arithmetic and logical instruction (except for "immediate")
  • I-Type(Immediate Type) : data transfer (load), arithmetic with Immediate
  • S-Type(Store Type) : data transfer (store)

 

R-TYPE

R타입은 대부분의 산술 그리고 논리연산에 사용되는 instruction format입니다. (”immediate” 제외)

opcode와 funct3, funct7로 어떤 명령을 할지 결정하고,

해당 명령을 rs1과 rs2에 적용한 값을 rd에 저장합니다.

 

예를 들어, 아래 식이 있다고 가정합시다.

add , x9, x20, x21
  1. funct7 : 0000000
  2. rs2 : 10101 (21의 2진수)
  3. rs1 : 10100
  4. funct3 : 000
  5. rd : 01001
  6. opcode: 0110011

이 됩니다.

따라서 해당 명령어는 아래 사진처럼 됩니다.

 

I-TYPE (Immediate의 ‘I’)

I-TYPE은 load 혹은 constant 연산을 위해 존재하는 instruction format입니다.

I-TYPE은 R-TYPE과 format이 다릅니다.

예를 들어, load를 하고자 하는 아래식에선

ld      x14 , 8(x2)

000000001000/00010/011/01110/0000011 이 됩니다.

  1. imm : 000000001000 (8)
  2. rs1 : 00010 (x2 의미)
  3. funct3 : 011
  4. rd : 01110 (x14의미)
  5. opcode : 0000011

또 다른 예시로 addi를 하고자 할 때엔

addi  x9 , x3 , 4

4가 imm에 들어가게 될 것입니다.

 

여기서 나온 Design Principle3이 있습니다.

Design Principle 3 : Good design demands good compromises

좋은 설계를 위해서는 좋은 타협이 필요하다.

다른 복잡한 디코딩 형식이어도 일정한 32-bit instruction을 따라야 하며 가능한 비슷한 format을 유지해야 합니다.

그렇기에 R-TYPE의 rs2와 funct7이 합쳐져서 I-TYPE의 immediate가 된 것입니다.

S-TYPE (store의 ‘s’)

S- TYPE은 Store을 위한 Instruction Format입니다.

해당 format에선 rs2가 다시 나타나고 rd가 아닌 imm [4:0]이 나타난 것을 볼 수 있습니다.

imm [11:5]와 imm [4:0]이 합쳐져서 하나의 offset을 만듭니다.

이는 rs1, rs2의 field를 동일하게 나두기 위함과, opcode, func3을 동일한 위치에 놓기 위해 쪼개 놓았습니다.

여기서 바로 위의 Design Principle3을 볼 수 있습니다.

 

예를 들어,

sd    x14 , 8(x2) 

는 여기선 제가 아직 제대로 이해 못 한 걸 수도 있는데 r-type과는 rs2 rs1 넣는 위치가 달랐습니다. (이는 4장을 공부하면 이해가 됩니다.)

0000000 01110 00010 011 01000 0100011

입니다.

  1. 첫 번째 imm이 0000000 (8의 12비트 중 7개)
  2. rs2: 01110(x14를 의미)
  3. rs1: 00010(x2를 의미)
  4. funct3: 011
  5. 두 번째 imm : 01000
  6. opcode: 0100011

 


Logical Operations

RISC-V는 logical operations를 위한 instructions를 제공합니다.

Shift instructions는 word의 비트들을 모두 왼쪽 혹은 오른쪽으로 옮기는 instructions입니다. (e.g., sll/i, srl/i, sra/i)

예를 들어,

slli  x11 , x19 , 4

는 reg x11에 x19를 << 4 bits 만큼 옮긴 값을 넣어라 라는 의미입니다.

 

shift left에서 sll, slli로 나뉘어 있는데, sll은 R-Type으로 rs2에 얼마 정도 옮길지의 값이 저장되어 있습니다. 근데 보통은 slli로 사용한다고 하네요.

아무튼 slli를 보면 특이합니다.

기존의 I-Type과는 다른 형태를 가지고 있습니다.

위의 식을 다시 사용하면

I-Type은 imme를 위해 12비트를 할당했습니다.

하지만 저희는 32 bit가 word이기 때문에 사실 31번 이상 shift를 하는 것은 의미가 없습니다.

그렇기 때문에 imm 비트는 5개로 제한하고, 남은 비트를 func으로 재활용합니다

 

위의 표에선 (sra, srai / xori) 두 개만 네모가 되어 있는데

sra, srai와 srl, srli의 차이점

  • sra, srai 함수는 비트를 오른쪽으로 옮기면서 자동으로 Sign Extension이 일어납니다

 

 


Instructions for Making Decision

  • 컴퓨터와 단순한 계산기를 구별하는 것은 바로 to make decision ability입니다.

(Decision making은 보통 if문을 사용하는가의 여부로 나타냅니다.)

  • 조건이 참일 경우에 labeled instructuon으로 branch(분기)합니다.
  • 조건이 참이 아닐 경우에는 그냥 다음으로 넘어갑니다.
  • beq (branch if equal)
  • bne (branch if not equal)

위 두 instruction을 conditional branches라고 부릅니다.

예를 들어, 아래의 식은

beq   rs1 , rs2 , L1

rs1과 rs2가 같다면 L1으로 이동하란 의미입니다.

bne   rs1 , rs2 , L1

rs1과 rs2가 다르다면 L1으로 이동하란 의미입니다.

 

Compiling if-then-else into Conditional Branches

예를 들어 if-else문이 c언어로 있다고 합시다.

if (i==j) 
	f = g+h; 
else 
	f= g-h;

이것을 저희가 f, g, h, i, j가 각각 x19 x20 x21 x22 x23에 있다고 가정하고 컴파일했다고 하면

			bne   x22 , x23 , Else // go to Else if i!=j
			add   x19 , x20 , x21  // f = g + h (skipped if i!=j)
			beq   x0 , x0 , Exit   // if 0 == 0 , go to Exit
Else: sub   x19 , x20 , x21  // f= g - h (skipped if i==j)
Exit:

Compiling a while Loop in C

예를 들어 while문이 c언어로 있다고 합시다.

while (save[i]==k)
	i+=1;

이것을 i , k 그리고 base of the array save가 각각 x22 x24 x25에 있다고 가정하면

Loop :      slli x10, x22, 2   //Temp reg x10 = i*4
			add  x10, x10, x25 //x10 = address of save[i]
			lw   x9, 0(x10)    //Temp reg x9 = save[i]
			bne  x9, x24, Exit //go to Exit if save[i] != k
			addi x22, x22, 1   //i=i+1
			beq  x0, x0, Loop  //goto Loop
Exit:

위의 첫 번째 줄은 i가 몇 번째 index인지 나타나게 하기 위함입니다.

주소는 i*4이기 때문이고 위의 2는 slli x10, x22, 2에서 2는 2의 2 제곱이란 뜻입니다. word(4byte)가 아니라 doubleword(8byte) 였다면 2가 아닌 3 이였겠죠?

 

 

More Conditional Operators

branch instruction에는 두 가지 종류가 있습니다.

그중 첫 번째 Conditional Branch ( 조건이 참일 때에만 실행)

Branch by comparison (blt/bltu and bge/bgeu)

  • blt (branch if less than)
blt  rs1, rs2, L1

만약 rs1이 rs2보다 작다면 L1으로 이동하란 의미입니다.

  • bge (branch if greater than or equal)
bge  rs1, rs2, L1

만약 rs1이 rs2보다 크거나 같다면 L1으로 가란 의미입니다.

bge가 등호가 있다는 것 주의!!

 

Signed and unsigned comparison

부호가 있음과 없음을 주의!

blt는 Sign Number를 비교 bltu는 Unsigned Number를 비교한다.

Case/switch statement

스위치문은 Branch address table란 것을 사용합니다.

이는 프로그램이 table에 인덱싱 하고 적절한 곳으로 branch 되게 합니다.

(table은 간단하게 2차원 배열 정도라고 생각하시면 됩니다)