일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 오퍼레이팅시스템
- 컴퓨터공학과
- bfs
- 컴공
- 컴공과
- 너비우선탐색
- c++
- 스택
- 구현
- 브루트포스
- coding
- 정석학술정보관
- 그래프
- Operating System
- 백준
- DP
- Computer science
- 문제풀이
- 오에스
- 북리뷰
- 자료구조
- 알고리즘
- 코테
- Stack
- 코딩
- cs
- OS
- vector
- 정석
- 개발
- Today
- Total
Little Jay
[CS] Instructions II(Assembly Code) - Computer System 3rd Edition by Bryant 본문
[CS] Instructions II(Assembly Code) - Computer System 3rd Edition by Bryant
Jay, Lee 2022. 3. 18. 17:10Data Size
먼저 앞서서 간단하게 살펴본 Data Size를 짚고 넘어가자.
b: 1Byte - Byte
w: 2Byte - Word
l: 4Byte - Double word
q: 8Byte - Quad word
assembly 코드를 읽을 때 이 suffix를 잘 파악해야 몇 바이트 단위로 끊어서 해석해야하는지 알 수 있기 때문에 이를 잘 숙지해야한다.
Mov Instruction
이전 포스팅에서 간단하게 mov instruction에 대해서 언급했었는데 이를 조금 더 자세히 살펴보고자 한다.
mov가 그러하듯 다른 instruction에서 suffix로 Data Size를 붙이게 되면 그 만큼의 Data를 저장할 수 있다.
하지만 또 때에 따라서는 이 Data Size를 생략하고 쓸 수 있기 때문에 이를 유념하자.
일반적인 Data Size의 suffix들이 붙은 instruction들을 살펴보았지만 맨 아래의 movabsq 라는 instruction이 무엇일까?
movabsq 명령어는 임의의 64비트값을 소스 오퍼랜드로 지정할 수 있으며, 레지스터를 receiver로만 사용할 수 있다.
movabsq $0x0011223344556677, %rax //%rax = 0011223344556677
이렇게 64비트의 값을 한번에 rax 레지스터에 저장하는 기능을 한다.
src의 값이 dst의 값보다 작을때는 movz, movs instruction들을 사용한다.
먼저 movz class의 instruction들은 dst의 remaining bytes를 0으로 채운다.
unsigned movement가 발생한다고 하면 이해하기 쉽다.
반면에 movs class의 instruction들은 dst의 remaining bytes를 MSB로 채우게 된다.
즉, sign integer처럼 작동한다고 이해하면 쉽다.
이 instructions들은 특히 suffix를 잘 봐야하는데, src와 dst가 suffix에 명시되어야 하기 때문에 이를 잘 파악해야한다.
흔히 movz instruction을 ZeroExtends, movs instruction을 SignExtends라고 부른다.
아래의 예시를 보면 조금 더 직관적으로 이해를 할 수 있을 것이다.
Arithmatic Instructions
위의 표는 Arithmatic Instruction들을 정리해놓은 테이블이다.
이 명령어들 또한 suffix로 크기를 지정해주어서 연산하는 데이터의 크기를 지정해줄 수 있다.
테이블을 자세히 보면 4개의 그룹으로 명령어들이 나누어져 있는 것을 볼 수 있는데, 아래와 같이 나눌 수 있다.
- Load Effective Address
- Unary Operation
- Binary Operation
- Shift Operation
Load Effective Address
lea operation이라고 한다. 이 명령어는 'effective address'를 복사해서 저장을 한다
그런데 앞서서 mov instruction도 저장을 하는 명령어라고 언급했다.
결과적으로 말하자면 lea instruction은 src에 있는 주소를 dst에 저장을 하는 것이고,
mov instruction은 src에 있는 데이터를 dst에 저장을 하는 것이다.
이해가 잘 안될 수 있지만, 아래의 예시를 보자.
8(%rax), %rdx
4(%rax, %rax, 4), %rdx
첫 번째 명령어를 mov의 관점에서 해석해보자면 rax에 있는 주소값 + 8 후에 그 메모리에 있는 데이터를 rdx레지스터에 저장을 하는 것이고, lea의 관점에서 해석해보자면 rax + 8의 주소값을 rdx에 저장을 하는 것이다.
마찬가지로 두 번째 명령어도 mov의 관점에서 본다면 5*rax + 4의 주소에 있는 데이터를 rdx 레지스터에, lea의 관점에서 본다면 5*rax + 4의 주소값을 rdx 레지스터에 저장한다.
mov 명령어를 물론 인간의 관점에서 보았을때는 이해가 잘 되지만, 컴퓨터의 입장에서는 register를 optimize하는 것이 훨씬 중요하기 때문에 lea 명령어를 사용한다. 아래의 예시 코드를 보자
long mul20(long x) {
return x * 20;
}
간단한 코드이지만 lea instruction을 사용한다면 assembly 코드는 아래와 같이 간단해진다.
lea (%rdi, %rdi, 4), %rax
shl $02, %rax
retq
그러나 최적화가 되지 않은 채 mov instruction들만 사용한다고 하면 아래와 같이 매우 길어질 것이다.
mov %rdi, -0x8(%rbp)
mov -0x8(%rbp), %rdx
mov %rdx, %rax
mov %rdx, %rdi
shl $0x2, %rdx
add %rdi, %rdx
add %rdx, %rax
shl $0x2, %rax
이렇듯 추가적인 instruction과 register의 사용을 수반하기 때문에,
lea operation의 존재는 optimization 관점에서 필수적이다.
Unary Operation
위에 표를 보면 알겠지만 이는 Single Operand만 요구된다.
이때 Operand는 레지스터나 메모리 둘 다 올 수 있다. (dst의 관점)
INC는 ++해주는 것과 같고, DEC는 --, NEG는 negate operation, NOT은 Complement Operation을 수행한다.
책의 3.8에 있는 예제를 보면 주어진 조건은 아래와 같고, 문제는 주석처럼 해결할 수 있다.
decq %rcx // rcx에는 0x0이 결과적으로 있을 것이다
incq 16(%rax) // 0x110에는 0x14가 결과적으로 저장될것
Binary Operation
대부분의 Arithmetic Operation은 Binary Operation에서 작성한다.
위의 테이블에 나와있는 연산은 그 약자대로 그대로 해석을 하면 된다.
항상 주의할 점은 연산한 결과값이 그대로 Dst 쪽에 저장이 된다는 것이다.
해석할 때 항상 이 점에 유의해야한다.
교재 3.8번의 문제를 이어서 조금 더 풀어보자면, (표는 위에 나와있다)
addq %rcx, (%rax) // 0x100에는 0x100이 들어갈 것이다
subq %rdx, 8(%rax) // 0x108번지에는 0xA8이 계산되어 들어갈 것이다
imulq $16, (%rax, %rdx, 8) //0x118번지에는 0x110이 저장될 것이다
Shift Operation
말그대로 시프트를 한다. 왼쪽으로 밀면 *2, 오른쪽으로 밀면 /2를 수행하는 것과 같다.
사실 Left Shift는 SAL을 하든, SHL을 사용해도 상관이 없다. 이는 Shift Left를 할 때 LSB를 어차피 0으로 채우기 때문에 둘 중 어떤 것을 사용해도 상관이 없다. 문제는 Right Shift이다.
Data Type이 Unsigned면 전혀 상관이 없지만, Sign이면 Right Shift에서 MSB를 무조건 0으로 채우게 된다면 data가 잘못 들어갈 수 있다. 따라서 이를 구분을 해줘야 하는데,
Arithmetic Shift가 Sign Extension으로 MSB의 값으로 채워지게 되고 (SAR)
Logical Shift는 Zero Extend를 하게 된다 (SHR)
Wrapping Up
교재에 나와있는 c코드를 assembly로 disassemble하는 것으로 본 포스팅을 마치겠다
long arith(long x, long y, long z) { //x를 %rdi, y를 %rsi, z를 %rdx라고 가정
long t1 = x ^ y;
long t2 = z * 48;
long t3 = t1 & 0x0F0F0F0F;
long t4 = t2 - t3;
return t4;
}
arith:
xorq %rsi, %rdi
leaq (%rdx, %rdx, 2), %rax
salq $4, %rax
andl $252645135, %edi
subq %rdi, %rax
ret
Reference
Randal E. Bryant, & David R. O’Hallaron. (2016). Computer Systems A Programmer’s Perspective Third edition. Carnegie Mellon University: Pearson.