[시스템 프로그래밍] 어셈블리어 - 조건문
어셈블리어에서 조건문을 이해하기 전 알아야 하는 지식이 있다.
%rsp 레지스터에는 현재 스택의 위치를 저장하고, %rip 레지스터에는 다음 명령어를 가져올 주소를 저장하고 있으며, %rax 레지스터는 연산 시 임시로 값을 저장할 때 사용된다.
CF / SF / ZF / OF 레지스터들은 각각 1비트 레지스터로, 산술 연산의 결과에 따라 값에 설정된다. (lea 명령어는 제외)
이렇듯, CPU는 내부의 다양한 레지스터들을 사용해 상태를 저장한다.
위의 1비트 레지스터들은 Condition Code로 불리고, 특정 조건을 만족시키면 1로 세팅된다.
CF : Carry Flag. MSB에서 Carry가 발생하면 1로 설정된다.
ZF : Zero Flag. 연산의 결과가 0이면 1로 설정된다.
SF : Signe Flag. 연산 결과가 0보다 작으면 1로 설정된다.
OF : Overflow Flag. 연산 결과에서 오버플로우가 발생했으면 1로 설정된다.
연산이 진행 될 때 마다 연산의 결과를 바탕으로 Condition Code들이 자동으로 설정되지만, 비교명령어를 사용해 직접 설정해 줄 수도 있다.
cmp
cmpq src2, src1
src1 - src2를 계산하고, 계산 결과에 따라 Condition Code를 설정한다.
연산의 결과가 따로 저장되지는 않는다. (조건 코드만 설정한다)
test
testq src2, src1
src2 & src1을 계산하고 계산 결과에 따라 Condition Code를 설정한다. (비트연산)
역시 연산의 결과가 저장되지는 않는다. (조건 코드를 설정하기 위한 명령어이다)
조건 코드는 C언어에서 if else / switch 등 제어 문법을 어셈블리어로 표현할 때 사용된다.
지금까지는 조건 코드를 설정하는 방법에 대해 살펴봤다. 이제 조건 코드를 사용해보자.
SetX
조건 코드의 값을 불러올 때 사용한다.
setX ByteRegister
X는 ByteRegister를 어떻게 설정할 지 정하는 역할을 한다. (조건 코드의 조합)
조건코드의 조합에 따라 ByteRegister를 0또는 1로 설정한다.
ByteRegister의 나머지 7바이트는 변경되지 않고, 보통 cmp명령어를 실행한 후 조건 코드의 값을 불러올 때 사용한다.
이 때 X는 다른 명령어들처럼 피연산자의 크기를 나타내는 대신 조건 코드의 어떤 조합을 사용할 것인지를 나타낸다.
(setl : set less / setb : set below / setg : set greater ...)
cmp 명령어 이후에 setX 명령어를 사용해 C 코드를 어셈블리어로 변환했다.
즉, cmp명령어로 조건 코드를 설정한 후 setX 명령어로 조건 코드의 값에 따라 레지스터의 값을 지정해준다.
setX 명령어 표에서 각각의 조건 코드가 왜 저렇게 조합되는지는.. 한 번 읽어보고 생각해보자.
jX
jX Label
조건 코드 X가 참인 경우 Label로 이동하라는 의미이다.
SetX와 사용법이 비슷하다.
cmp, test 등으로 조건 코드를 설정한 후 jmp 명령어를 사용하는 경우가 많다.
jmp는 예외적인 경우로, 조건식에 상관없이 무조건 Label로 이동함을 의미한다.
C코드를 어셈블리 코드로 번역한 예시 중 하나이다.
cmp 이후 jle 명령어를 사용해 조건에 따라 다른 결과값을 반환하도록 했다.
jX 명령어 뒤에는 라벨이 따라오고, 정해진 라벨으로 점프한다.
여기서 라벨은 어셈블러와 추후 다루게 될 링커가 적절히 인코딩한다.
실제로 컴파일러가 C코드를 어셈블리어로 번역 할 때는 성능 최적화를 위해 C의 goto문법을 사용한다.
(C - C최적화 - 어셈블리어)
goto문법을 사용해서 작성한 C코드는 어셈블리어로 바로 변환할 수 있다.
여기서 한 단계 더 나아가보자.
cmovX
조건부 이동명령어로, X가 참인 경우 mov를 수행하는 명령어이다.
최근의 CPU는 네 가지 명령어를 한 번에 처리하기 위해 각 단계별로 명령어를 하나씩 넣어서 처리하는데, 여기서 분기문을 사용하게 되면 파이프라인의 인스트럭션 흐름을 방해하게 된다.
좀 더 자세히 알아보자.
프로세서들은 파이프라인을 통해 성능을 향상시킨다.
파이프라인을 실행될 명령어들로 미리 채워 놔야 하고, 채우기 위해서는 실행될 명령어들의 순서를 알고 있어야 한다.
그런데 여기서 조건부 분기를 만난다면? 분기 조건에 대한 계산이 끝나기 전까지는 어디로 분기될지 알 수 없고, 점프 하나를 잘못 예측하면 이미 채워진 결과들을 버려야 하고 다시 채워야 하기에 큰 손실을 가져온다.
바로 이 부분 때문에 조건부 이동 명령어가 사용된다.
조건부 이동명령어를 사용하면 제어의 이동을 사용하지 않게 돼 이 문제를 해결할 수 있고, 이 명령어를 사용하기 위해 C코드에서 if-else 문법을 삼항연산자로 변환한 후 어셈블리어로 변환하는 작업을 거쳐 최적화한다.
(물론, if-else 문법이 간단하게 작성된 경우만 가능하다..)
'Computer Science > System Programming' 카테고리의 다른 글
[시스템 프로그래밍] 프로시저 (0) | 2022.10.28 |
---|---|
[시스템 프로그래밍] 어셈블리어 - 반복문 (0) | 2022.10.24 |
[시스템 프로그래밍] 어셈블리어 (0) | 2022.10.13 |
[시스템 프로그래밍] 정수의 산술연산과 실수의 표현 (0) | 2022.09.29 |
[시스템 프로그래밍] 컴퓨터 시스템과 정보의 표현 (0) | 2022.09.15 |
댓글
이 글 공유하기
다른 글
-
[시스템 프로그래밍] 프로시저
[시스템 프로그래밍] 프로시저
2022.10.28 -
[시스템 프로그래밍] 어셈블리어 - 반복문
[시스템 프로그래밍] 어셈블리어 - 반복문
2022.10.24 -
[시스템 프로그래밍] 어셈블리어
[시스템 프로그래밍] 어셈블리어
2022.10.13 -
[시스템 프로그래밍] 정수의 산술연산과 실수의 표현
[시스템 프로그래밍] 정수의 산술연산과 실수의 표현
2022.09.29