결정과 반복
기본적으로 CPU는 명령을 위에서 아래로 나타나는 순서대로 실행합니다. 그 순서만으로는 선택을 하거나 작업을 반복할 수 없습니다. 분기는 프로그램이 그 직선 경로를 벗어나는 방법입니다. 조건을 테스트하고, 조건이 성립하면 CPU가 다음 줄이 아닌 다른 위치에서 계속하도록 지시합니다. 모든 결정과 모든 루프는 이 하나의 메커니즘에서 나옵니다.
if나 for는 없습니다. 레이블(이름이 붙은 위치)과 분기(조건이 성립할 때 위치로 점프)로 그것들을 구성합니다. 레이블은 이름 뒤에 :가 오는 것입니다.
분기가 어떻게 실행되는지 이해하려면 프로그램 카운터(PC)에 대해 알아야 합니다. PC는 코드에서 숨겨진 특수 레지스터로, 실행 중인 명령의 메모리 주소를 보관합니다. 분기 명령은 PC에 새 값을 쓰므로, CPU는 다음 명령을 코드의 다음 줄이 아니라 대상 레이블에서 가져옵니다.
분기 계열
모든 분기는 두 레지스터를 비교하고 테스트가 성립할 때만 레이블로 점프합니다. 테스트가 실패하면 실행은 다음 줄로 계속됩니다. 여섯 가지 테스트가 핵심을 이룹니다:
beq a, b, label # a == b이면 분기bne a, b, label # a != b이면 분기blt a, b, label # a < b이면 분기 (부호 있음)bge a, b, label # a >= b이면 분기 (부호 있음)bltu a, b, label # a < b이면 분기 (부호 없음)bgeu a, b, label # a >= b이면 분기 (부호 없음)
와 는 동등성을 테스트하고 부호를 무시합니다. 와 는 순서 쌍, 즉 보다 작음과 보다 크거나 같음을 이루며, 레지스터를 부호 있는 수로 비교합니다. 와 는 같은 비교를 수행하지만 값을 부호 없는 것으로 취급하며, 이는 결코 음수가 아닌 주소나 카운트에 적합합니다.
부호 있는과 부호 없는은 무슨 뜻일까요? 레지스터는 32비트를 보관하며, 같은 비트를 두 가지 방식으로 읽을 수 있습니다. 부호 없는 것으로 읽으면 0부터 약 40억까지의 단순한 정수를 이룹니다. 부호 있는 것으로 읽으면 최상위 비트가 마이너스 부호로 계산되어, 비트 패턴의 절반이 음수를 나타냅니다. 비트 자체는 변하지 않습니다. 변하는 것은 읽는 방식뿐입니다. 그래서 두 세트의 비교가 있습니다. 음수가 될 수 있는 일반적인 수에는 부호 있는 것(, )을 사용하고, 메모리 주소처럼 결코 음수가 아닌 값에는 부호 없는 것(, )을 사용하세요.
직접적인 "더 크면 분기"나 "작거나 같으면 분기"는 없습니다. 그것들은 두 레지스터를 교환하여 얻으며, 어셈블러는 교환을 대신 수행하는 의사 분기로 와 를 제공하므로 는 가 됩니다. 두 가지 더 많은 지름길: 와 는 하나의 레지스터를 0과 비교하고, 은 조건 없이 점프합니다.
li t0, 0 # 합계 = 0li t1, 1 # i = 1li t2, 6 # 한계 = 6 (i < 6인 동안 반복)loop:bge t1, t2, done # i >= 6이면 루프 종료add t0, t0, t1 # 합계 += iaddi t1, t1, 1 # i++j loop # 다시 확인하러 돌아간다done:mv a0, t0 # 합계를 출력 (15가 되어야 함)li a7, 1ecallli a7, 10ecall
추적해 보세요. t0는 실행 중인 합계를 보관하고(0에서 시작), t1은 카운터 i를 보관하며(1에서 시작), t2는 한계 6을 보관합니다. loop를 통과할 때마다: 은 i가 6에 도달하면 루프를 빠져나갑니다. 그렇지 않으면 이 i를 합계에 더하고, 이 i를 1 증가시키며, 가 다시 테스트하기 위해 점프합니다. 루프는 1, 2, 3, 4, 5를 더하고, done은 총합 15를 출력합니다.
bge는 t1과 t2를 비교하여 루프를 계속할지 결정합니다.
t1이 6이 되면, 은 본문을 다시 실행하게 하는 대신 제어를 done으로 옮깁니다. 그 테스트가 반복하는 패스와 멈추는 패스를 구분합니다.t1이 증가하고 프로그램 카운터(pc)가 loop로 점프해 돌아옵니다. pc가 loop로 돌아올 때마다 분기 조건은 거짓이었으므로 루프는 다시 실행됩니다.