

우리는 MIPS라고 불리는 특정한 ISA디자인에 대해 알아볼 것이다.
MIPS : Microprocesser without Interlocked Pipelined Stages
MIPS의 디자인 원칙의 지식에 기반으로 하여, 우리는 다른 ISA들을 이해할 수 있으며, 새로운 ISA를 만들어 낼 수 있다.
가장 중요한 디자인 원칙
단순성은 규칙적인 것을 선호한다.
정규화 : 모든 MIPS 산술 지시사항은 1개의 연산과 3개의 피연산자를 포함한다.

정규화는 구현을 더욱 단순하게 만든다.
정규화는 더 높은 성능을 더 낮은 비용으로 가능하게 만들어준다.
예제) 언어 번역
C 코드
f = (g+h) - (i+j)
MIPS 어셈블리 언어로 컴파일 했을 때
add t0, g, h
add t1, i, j
sub f, t0, t1
작은 것이 더 빠르다.
피연산자에 대한 MIPS의 산술 지시 사항은 작은 수의 레지스터를 선택해야 한다.
레지스터란?

왜 MIPS는 32개의 레지스터만 사용하냐?
MIPS 디자이너는 레지스터의 #과 clock period 사이에 균형을 맞추기 위해 노력했다.
32는 5비트를 나타낸다고 할 수 있다.(32 = 2^5)
디자인 원칙에 따라
1. 모든 MIPS 산술 지시사항은 단일 피연산자 & 3개의 피연산자를 포함하고 있다.
2. 산술 연산자의 피연산자는 32개의 레지스터들 중 하나에서 선택되어야 한다.
예제) 언어 번역
C 코드
f = (g+h) - (i+j); // f,g,h,i,j가 s4레지스터들에 할당되어 있다고 가정하자.
MIPS 어셈블리 어로 컴파일됬을때 코드
add $t0, $s1, $s2
add $t1, $s3, $s4
sub $s0, $t0, $t1
프로세서는 레지스터에 작은 양의 데이터를 저장해 놓을 수 있찌만, 우리의 실제 세계의 프로그램들은 더 많은 변수들을 가지고 있다. 그들 중 상당부는 복잡한 데이터 구조를 지니고 있다.(e.g. 배열...)
우리는 그런 남은 또는 복잡한 구조를 유지하기 위해 메모리를 사용한다.
레지스터 피연산자를 요구하는 MIPS 산술연산 지시사항을 위해 MIPS는 두 종류의 데이터 전송 지시사항을 제공한다.


정렬 제약
몇몇 데이터는 1또는 2 바이트를 이용한다(halfword). 그들은 1또는 2바이트 할당되어 있다.
Q1) A[]은 halfword의 배열이다. A의 base address는 0이다. 그럼 ,A[5]의 시작 주소는 무엇인가?
A는 halfword이므로 데이터의 크기는 2이다. A[0]의 시작 주소가 0이므로 A[5]의 시작 주소는 2X5 = 10이 된다.
a, b는 1바이트가 필요하므로 메모리에 a의 시작주소 0, b의 시작 주소 1로 2바이트 크기의 공간을 차지하고 있다. c는 word즉 4바이트 크기를 지니고 있으므로 시작 주소가 4의 배수여야 한다. 즉 2는 시작 주소가 될 수 없고 4가 시작 주소가 된다.

lw reg1 offset(reg2) : 32비트의 메모리 주소를 가리키는 주소값 (reg2의 값 + offset)에서의 값을 reg1으로 넣는다.(load명령어)
sw reg1 offset(reg2) : reg1의 값을 32비트의 메모리 주소를 가리키는 주소값 (reg2의 값 + offset)에 저장한다.(store 명령어)
이런 지시사항들은 byte addressing을 통해 word를 load하고 저장한다.
lh/sh와 lb/sb 지시사항은 halfwords(16비트), 8비트의 데이터를 load하고 저장한다.
예제
c코드
g = h + A[8]
A는 4바이트의 word이다.
g와 h의 값은 $s1과 $s2에 저장되어 있다.
A의 base 주소는 $s3이다.
MIPS 어셈블리어로 컴파일
lw $ t0, 32($s3) // A배열의 첫번째 주소 + offset(32)을 t0 레지스터에 넣어준다.
add $s1, $s2, $t0 // g에 h와 A배열을 더하여 저장해 준다.
A는 4바이트 words 배열이다.
h의 값은 $s2에 존재한다.
A의 base 주소는 $s3에 존재한다.
MIPS 어셈블리 언어로 컴파일한 코드
lw $ t0, 32($ s3)
add $ t0, $ s2, $ t0
sw $ t0, 48($s3)
f,g,h는 $s0, $s1, $s2에 각각 존재한다.
halfwords i와 j는 각각 메모리에 연속적으로 저장되어 있다.
i의 시작 주소는 $s3에 저장되어 있다.
MIPS 어셈블리어 컴파일 코드
add $ t0, $ s1, $ s2
lw $ t1, 0($ s3)
lw $ t2, 2($ s3)
add $ t3, $ t1, $ t2
sub $ s0, $ t0, $ t3
레지스터는 메모리보다 접근하기 빠르다.
메모리의 데이터 연산은 추가적인 지시사항을 요구한다.(load, store)
가장 좋은 성능을 위해선, 컴파일러는 레지스터를 가능한 많이 사용해야 한다.
-> 레지스터 최적화가 아주 중요하다.
일반적인 case를 빠르게 만든다.
Common case : 프로그램은 연산과정에서 상수를 많이 사용한다.
solution : 상수를 다루기 위한 16비트의 immediate 피연산자를 지원한다.(항상 메모리로 접근하는 것은 비효율적이다.)
solution : MIPS register 0($zero)는 상수 0을 가지고 있다.(0을 많이 쓰기 때문에)
예제
f = A[10] - i +4;
A는 1 바이트 배열이며 base address는 $s0에 저장되어 있다.
f와 i는 $s1과 $s2에 각각 저장되어 있다.
MIPS assembly언어 코드로 컴파일된 코드
lb $ t0, 10($ s0)
sub $ t1, $ t0, $ s2
addi $ s1, $ t1, 4
-> instruction 수를 줄여 성능을 늘린다.

- 가장 큰 바이트를 가장 처음에 넣는다.(가장 왼쪽에 위치 또는 가장 아래에 위치)
- 비트를 오른쪽에서 왼쪽으로 0,1,2,3... 숫자를 매긴다.(또는 위에서 아래로)
- 예를 들어 1000 1000 1010 1010 0000 0000 0101 0101(2)일때
n비트를 이용함으로써 우리는 0 ~ 2^n - 1 의 unsigned 숫자들을 표현할 수 있다.

그렇다면 우리는 부호가 있는 숫자들은 어떻게 표현할 것이냐?

sign + magnitude : MSB가 0일때는 양수/ 1이면 음수, 이후 자리수가 값이 된다.ex) 110 = -(11(2)) = -3
One's complement : MSB가 0일때는 양수/ 1이면 음수, 음수라면 모든 수를 반전시킨다. ex) 110 -> 001 -> -1
two's complement : MSB가 0일때는 양수/ 1이면 음수, 음수라면 모두 뒤집고 +1 ex) 110 -> 001 -> 010 -> -2
우리가 two's complement 방법을 사용한다면, 단순한 연산을 통해 연산 결과를 알 수 있다.(추가적인 연산이 필요없음) 또, 위의 표에서 보듯이 다른 방법과는 다르게 two's complement방식은 0의 개수가 1개이다.



- 가끔 우리는 n비트의 숫자를 n비트보다 더 많은 비트를 이용하여 표현해야 할 수도 있다.
- 16비트 immediate는 연산을 위해 32비트로 변환되어야 한다.
- lb/lh 지시사항은 메모리 공간에서 byte/halfword 를 load하고 32비트 레지스터에 저장한다.
- sign bit를 왼쪽으로 반복시킨다.
ex)
addi $30, $t0, 16
해당 코드에서 $t0은 word로 4바이트 크기다. 16은 2바이트 halfword이므로 signed extention이 필요하다.
데이터와 같이 지시사항 또한 이진수로 인코딩/표현 된다.
우리는 인코딩된 지시사들을 machine instructions라고 부른다.(non-encoded 지시사항 : 어셈블리어 , ex) add $t0, $t1, $t2)
지시사항을 표현하기 위해 ISA는 instruction 포멧(지시사항의 나열)을 정의한다.
ex) 인코딩된 비트의 연속에서 어떤 부분이 무엇을 위해 이용될것인가?
문제 : 모든 지시사항을 표현하기 위해서 우리는 많은 instruction format을 필요로 할 것이다 + 다른 포멧을 디코딩하기 위해서 우리는 복잡한 컴퓨터구조를 필요로한다.
compromise(타협점) : 모든 지시사항을 같은 길이로 일관되게 유지한다.(MIPS 지시사항들은 32비트의 instruction words로 인코딩되어 있다.)
이를 바탕으로, MIPS는 포멧을 가능한 유사하게 유지한다.(일정성)

op(opcode) : 기본적인 지시사항의 작동(지시사항이 무엇을 하는지)
rs : 첫번째 소스 레지스터 피연산자
rt : 두번째 소스 레지스터 피연산자
rd : 대상 레지스터 피연산자
shamt : shift 양(shift 작업을 위해 이용된다.)
funct : 함수 코드(작동의 구체적인 변화)
ex) add(op) $t0(rd), $t1(rs), $t2(rt)
shift 양(0~31양 만큼 shift가능)
0000 .... 0001
0000 .... 0010
Q) MIPS는 얼마나 많은 레지스터를 이용하냐?
-> 32개의 레지스터를 사용한다.


op : 기본적인 지시사항의 작동(R-formet과 유사하다.)
rs : 첫번째 소스 레지스터 피연산자
rt : 두번째 소스 레지스터 피연산자
상수 또는 주소(memory space를 거치지 않음)
Q) immediate operand를 위해 MIPS가 얼마나 많은 바이트를 사용하냐?
-> 32비트 -> 4바이트

위의 디자인 원칙4에서 MIPS는 가능한 유사한 포멧을 지닌다.(R-포멧과 I-포멧은 32비트 instruction word)
