

비트 단위 조작 지시사항
and 연산

OR연산

NOT 연산



결정을 내리는데 이용하는 지시사항
Conditional branch 연산


C 코드 :
if(i == j) k = i+j; else k = i-j; //i,j,k의 주소 $ s0,$ s1, $s2
MIPS 어셈블리어 코드:
bne $s0, $s1, ELSE
add $s2, $s0, $s1
j EXIT
ELSE:
sub $s2, $s0, $s1
EXIT:
C코드 :
while(A[i] != k) i += 1; // i, k의 주소 $s0, $s1, A는 words의 배열이고 base address가 $s2이다.
MIPS 어셈블리어 코드:
LOOP: sll $t0, $s0, 2 // 2비트 왼쪽으로 이동하므로 4를 곱한것과 같다고 볼 수있다. i 값을 4배 함으로써 int형 데이터 위치 A[i]를 찾으려고 하는 과정이다.
add $t0, $t0, $s2 // base address에 i*4의 값을 통해 A[i]를 구할 수 있다.
lw $t1, 0($t0)
beq $t1, $s1, EXIT
addi $s0, $s0, 1
j LOOP
EXIT:
slt $t0, $t1, $t2
bne $t0, $zero, LABEL // 만일 $t1이 $t2보다 작다면 LABEL로 가라
beq $t0, $zero, LABEL // 만일 $t1이 $t2보다 크다면 LABEL로 가라


Calling / Returning
1) caller는 input arguments과 return address(callee 이후 수행될 instruction의 주소)를 callee에게 전해준다.
2) caller는 callee에게 control을 준다.
3) callee는 주어진 input arguments로 연산을 수행한다.
4) callee는 caller에게 return 값을 전해준다.
5) callee는 caller에세 control을 준다.
6) caller는 저장된 것들을 다시 불러온다.
Q) input params, return address, return values를 어떻게 전달해 줄까?
-> 우리는 위 값들을 전달해주기 위해서 registers를 이용한다.

하지만 우리가 생각해봐야 할 점은 다른 procedure가 같은 register를 이용한다면 => register 값은 overwritten 될 것이다.
또한 MIPS는 작은 양의 레지스터만을 사용한다, 하지만 우리는 많은 arguments들을 pass하고 싶다.
Q) 우리는 이 문제들을 어떻게 처리해야 할까? & 누가 이 문제들을 처리할 것인가?

몇몇 레지스터들의 값은 call과정에서 보존되어야 한다.
해당 표는 call에서 보존되는 레지스터와 보존되지 않는 레지스터의 값들을 보여준다. (ex) func1 이 func2를 call하였을 때 func1의 $s0는 복구가능하지만 $t0는 복구 불가하다.)

callee가 보존되는 레지스터 값을 이용한다면, callee는 calling 이후에 그 레지스터에 있는 값을 stack에 저장하고, returning 이루에 저장되어 있던 값을 복구한다.
caller가 보존되지 않는 레지스터들에 값들을 보존해야 한다면, caller는 calling이전에 stack을 값을 저장하고, returning이루에 저장되어있던 값들을 복구한다.

C코드
foo(a)가 main()에서 call 되었을 때 a는 $s0에 저장되어 있다.
int foo(int a){
... 다른 function call을 한다
return -a;
}
int main{
...
return (a-a) + (a+a) + foo(a)
}
MIPS 어셈블리 언어 코드
main:
sub $t0, $s0, $s0
add $t1, $s0, $s0
add $t0, $t0, $t1
add $a0, $s0, $zero // 인수 passing을 위해서 $a0이용한다.
addi $sp, $sp, -4
sw $t0, 0($sp) // stack pointer의 top에 t0의 값 저장
jal foo // main의 다음 코드의 주소(lw...)를 $ra에 저장한 후에 foo로 jump한다.
lw $t0, 0($sp) // foo함수 진행이전에 $t0의 값을 다시 $t0로 돌려주기
addi $sp, $sp, 4
add $v0, $v0, $t0
foo:
addi $sp, $sp, -4
sw $ra, 0($sp) // stack pointer의 top에 main함수의 return address를 저장해준다.
... 다른 함수 call // (jal, $ra를 수정한다.)
sub $v0, $zero, $a0 // $v0는 return value pass를 위해 사용된다.
lw $ra, 0($sp) // $ra에 main함수의return address를 넣어준다.
addi $sp, $sp, 4
jr $ra


C코드
int leaf(int a, b, c, d){
int e;
e = (a+b) - (c+d);
return e;
}
MIPS 어셈블리어 코드
leaf:
// 1. $s0를 스텍에 저장
addi $sp, $sp, -4
sw $s0, 0($sp)
// 2. 작업 진행
add $t0, $a0, $a1
add $t1, $a2, $a3
sub $s0, $t0, $t1
add $v0, $s0, $zero
// 3. $s0를 restore
lw $s0, 0($sp)
addi $sp, $sp, 4
// 4. Return
jr $ra
C코드
int sum(int n){
if(n == 0) return 0;
else return (n + sum(n-1));
}
MIPS 어셈블리어 코드
sum:
bne $a0, $zero, L1
add $v0, $a0, $zero
jr $ra
L1:
addi $sp, $sp, -8
sw $ra, 4($sp) // $ra는 preserved로서 callee가 스텍에 return address 저장
sw $a0, 0($sp) // $a0는 non-preserved로서 caller가 stack에서 저장해야 한다.
addi $a0, $a0, -1
jal sum
lw $ra, 4($sp)
lw $a0, 0($sp)
addi $sp, $sp, 8
add $v0, $v0, $a0
jr $ra




Q) 야 load/store instruction에서 address offset을 저장하는데 드는 비트수는 얼마인가?
A) I-form에서 opcode: 6비트, rs: 5비트, rt: 5비트, offset: 16비트로 offset을 표현하는데 드는 비트 수는 16비트이다.
Q) 그럼 우리가 메모리의 모든 부분을 어떻게 접근해야 되는거냐?
1. Immediate addressing(데이터가 instruction에 존재)

2. Register addressing(데이터가 register에 존재)

3. Base addressing(data가 메모리에 존재 -> load/store)
1) 레지스터에 접근한다.($ s0 = base address에 접근하는데) & immediate 피연산자(offset 접근하는데)
2) target address를 계산한다. (target address = base address + offset(16비트의 signed num))
3) target memory 주소의 데이터에 접근한다.

4. PC - relative addressing(현재 지시사항 주소 -> branch instruction)
가정 : 대부분 branch의 target은 현재 instruction과 근처에 존재한다.
HW 구현에서 PC는 현재 지시사항을 컴퓨팅하기 전에 4(MIPS instruction 32비트 = 4바이트)가 이미 증가해 있다.
1) immediate operand(offset) & PC에 접근한다.
2) target address를 계산한다. (taget address = PC + offset X 4 -> offset X 4 = offset << 2 더 많은 표현 가능)
3) target 메모리 주소의 instruction에 접근한다.


해당 코드에서 bne $ s0, $ s1, ELSE 부분의 코드의 주소가 0이라고 하자 add는 4, j는 8, ELSE는 12가 된다.
bne에서 target address(Else주소) = PC + offset X 4 = add instruction 주소 + (ELSE 주소 - (add instruction 주소)/4) X 4 = 12
5. Pseudo direct jump addressing(with branch instruction)
1) instruction & PC 주소로 접근
2) target address를 연산한다.(target address는 32비트 여야 하므로 target addr(32비트) = PC의 31...28(0000) 과 address X 4(28비트)를 병합한다. -> target address는 0000 ....으로 나옴)
3) target memory addr의 instruction에 접근한다.


EXIT의 address를 80000이라고 하자
PC는 79992값을 가지고 있다.
target address = PC31...28(0000) : address of Exit X 4
EXIT의 저장된 주소 = EXIT의 address /4 = 20000
Q) 만일 branch가 16비트의 offset을 가지고 표현하기 너무 멀다면 우리는 어떤식으로 code를 다시 작성해야 할까?

다음 beq에서 L1의 address가 2^17 보다 크다고 할 때
우리는 bne로 반대의 경우 코드를 작성한 후 jump를 진행한다.
가끔 우리는 32비트 상수를 이용할 일이 생긴다.
MIPS는 32비트의 immediate operand를 "lui"라는 특별한 instruction을 지원한다.
예시)
lui $ s0, 61 // 61(10진수) = 0000 0000 0011 1101(2진수)
# lui instruction이루 $s0에 저장되어 있는 레지스터의 값은
0000 0000 0011 1101 0000 0000 0000 0000(2진수) 가 된다. // 뒤의 값은 모두 0으로
# ori 명령어는 OR연산을 진행한다.
ori $s0, $s0, 2304 //2304(10진수) = 0000 1001 0000 0000(2진수)
# 이 두 명령을 진행하고 나서 $s0의 값은
0000 0000 0011 1101 0000 1001 0000 0000(2진수) = 4000000(10진수)
더 좋은 연산을 제공하는건 어떤가? (blt, bge..)
작업 복잡성이 늘어나면 위험하므로, 간단한 instruction만을 사용한다.
common ISA를 빠르게 만들어라!!

ISA는 컴퓨터 언어이다 & SW와 HW사이의 interface이다.
ISA는 다음을 정의한다.
MIPS ISA는 아래의 디자인 원칙을 통해 디자인 되었다.