Conditional Operations
프로그래밍 언어에서는 보통 if문장으로 판단 기능을 표현한다.
beq reg1, reg2, L1
reg1과 reg2의 값이 같으면 L1에 해당하는 statement로 가라는 뜻이다.
beq는 branch if equal을 의미한다.
bne reg1, reg2, L1
reg1과 reg2의 값이 같지 않으면 L1으로 가라는 뜻이다.
bne는 branch if not equal을 의미한다.
beq와 bne 두 명령어를 조건부 분기(conditional branch)라 부른다.
Compiling if Statements
다음 코드에서 f, g, h, i, j는 변수이고, 각각은 레지스터 $s0부터 $s4까지에 해당한다.
if (i==j) f = g+h;
else f = g-h;
다음은 MIPS 코드가 해야 할 일을 보여 주는 순서도이다.
첫 번째 부분 i == j? 은 같은지 비교하는 것이므로 beq를 사용하면 될 것처럼 보인다.
그러나 실제로는 조건을 반대로 검사해서 then 부분을 건너뛰게 하는 것이 더 효율적이므로 bne를 사용한다.
bne $s3, $s4, Else
그다음 연산 하나를 실행하는 것이므로 피연산자가 모두 레지스터에 있다면 명령어 하나로 번역된다.
add $s0, $s1, $s2
이 명령을 실행한 후에는 if 문의 끝부분으로 가야 한다. 이것은 unconditional branch라는 새로운 종류의 branch 명령으로 해결한다. 이 명령어는 프로세서에게 항상 분기하라고 말한다.
j Exit
else 부분의 치환문도 명령어 하나로 번역된다. 단 이 명령어에는 Else라는 레이블을 붙여야 한다.
Else: sub $s0, $s1, $s2
• Compiled MIPS code:
bne $s3, $s4, Else
add $s0, $s1, $s2
j Exit Else: sub $s0, $s1, $s2
Exit: …
Hardware/Software interface
컴파일러가 소스 프로그램에는 없는 분기 명령이나 레이블을 만들어 내는 경우가 많이 있다. 필요한 레이블과 분기 명령을 일일이 표시하지 않아도 되는 것이 상위 수준 프로그래밍 언어의 장점 중 하나이며, 상위 수준 언어를 사용하면 코딩이 더 빨라지는 이유이기도 하다.
Compiling Loop Statements
while (save[i] == k) i += 1;
i와 k가 레지스터 $s2, $s5에 할당되었고 배열 save의 시작 주소가 $s6에 저장되어 있다고 할 때 MIPS 어셈블리 코드는?
첫 번째로 할 일은 save[i]를 임시 레지스터로 가져오는 것이다. save[i]를 임시 레지스터에 적재하려면 먼저 그 주소를 구해야 한다. 바이트 주소를 사용하므로 인덱스 i에 4를 곱해서 save의 시작 주소에 더해야 주소가 만들어진다. 2비트씩 왼쪽 자리이동을 하면 4를 곱한 것과 같으므로 sll 연산을 사용할 수 있다. 순환의 끝에서 처음 명령어로 되돌아갈 수 있도록 Loop라는 레이블을 추가한다.
Loop: sll $t1, $s3, 2 # Temp reg $t1 = i * 4
save[i]의 주소를 계산하기 위하여 $t1 값에다 $s6에 있는 save의 시작 주소 값을 더한다.
add $t1, $t1, $s6 # $t1 = address of save[i]
이 주소를 이용해서 save[i]를 임시 레지스터에 넣을 수 있다.
lw $t0, 0($t1) # Temp reg $t0 = save[i]
반복하여 save[i] != k이면 반복문에서 빠져나가는 부분이다.
bne $t0,$s5, Exit # go to Exit if save[i] ≠ k
다음에 i에 1을 더하는 명령어이다.
addi $s3,$s3,1 # i = i + 1
반복문의 끝에서 맨 앞의 while 조건 검사로 되돌아가야 한다. 그리고 이 명령어의 다음에 Exit 레이블을 두면 끝난다.
j Loop # go to Loop
Exit:
전체 MIPS 코드
Loop: sll $t1, $s3, 2 add $t1, $t1, $s6
lw $t0, 0($t1) //lw $t0, $t1, 0
bne $t0, $s5, Exit
addi $s3, $s3, 1
j Loop Exit: …
Basic Blocks
이렇게 분기 명령어로 끝나는 명령어 시퀀스는 컴파일러에게 특히 중요한 의미가 있기 때문에 basic block이라는 별칭이 붙어 있다. basic block이란 분기 명령을 포함하지 않으며 분기 목적지나 분기 레이블도 없는 시퀀스이다. 컴파일러의 초기 단계 작업 중 하나는 프로그램을 basic block으로 나누는 일이다.
More Conditional Operations
두 변수 간의 대소 비교가 필요할 때도 있다.
- slt rd, rs, rt
- if (rs < rt) rd = 1; else rd = 0;
- slti rt, rs, constant
- if (rs < constant) rt = 1; else rt = 0;
- Use in combination with beq, bne
slt $t0, $s1, $s2 # if ($s1 < $s2)
bne $t0, $zero, L # branch to L
Branch Instruction Design
blt, bge 등 명령어는 왜 없을까?
대소 비교하는 연산(<, >=, ...)은 동등 비교하는 연산(=,!=,...) 보다 느리다.
대소 비교하는 명령어를 구현하는 클럭 속도가 느려지거나 이 명령 실행에 별도의 클럭 사이클이 더 필요하게 된다.
그러므로 빠른 명령어 2개를 사용하는 것이 더 유리하다.
Signed vs. Unsigned
- Signed comparision: slt, slti
- Unsigned comparison: sltu, sltiu
아래 두 명령어의 실행 후 $t0, $t1의 값은 얼마인가?
slt $t0, $s0, $s1 # signed comparison
sltu $t1, $s0, $s1 # unsigned comparison
$s0의 값을 부호있는 정수로 보면 -1이고 부호 없는 정수로 보면 2^32 - 1이다.
$s1의 값은 부호 유무에 상관없이 1이다.
따라서 $t0은 1이고 $t1은 0이다.