tgool
Tgool
tgool
전체 방문자
오늘
어제
  • 분류 전체보기
    • Data Science
      • AI
      • Data Mining
      • ML(Machine Learning)
    • Computer Science
      • 자료구조
      • 알고리즘
      • 시스템 프로그래밍
      • 운영체제
      • 컴퓨터 구조
      • 컴퓨터 네트워크
      • 데이터 베이스
      • 파이썬
      • 자바
      • 아두이노
    • Math
      • 통계학
      • 확률론
      • 선형대수학
      • 수리통계학
      • 회귀분석
    • TOFEL
    • Git
    • Plan
    • Book
    • Working out
      • 영양과 생활
      • 운동 정보
      • 운동 기록

인기 글

최근 글

최근 댓글

hELLO · Designed By 정상우.
tgool

Tgool

[CSAPP] 4.1 The Y86-64 Instruction Set Architecture
Computer Science/컴퓨터 구조

[CSAPP] 4.1 The Y86-64 Instruction Set Architecture

2023. 3. 1. 19:53

학습 주제

  • Processor 설계를 학습해야 하는 이유와 더불어서 살펴볼 Y86-64 ISA에 대해 살펴볼 예정이다.

정리한 내용

개요

지금까지는 machine-level program 수준으로만 computer-system을 살펴봤었다. 지금 chapter에서는 processor 하드웨어의 설계를 간략히 봐볼 것이다.

왜 하드웨어 설계를 학습해야 할까?

  1. 지적으로 흥미롭고(intellectually interesting) 중요한 것이기 때문. 동작이 어떻게 돌아가는지 배우는 것에는 본질적인 가치가 있다.
  2. 전반적인 Computer System이 어떻게 동작하는지를 이해 가능. Chapter6에서 나올 memory system을 위해, processor-memory간의 interface를 중점으로 봐도 좋음
  3. Embedded system 설계자들은 프로세서가 어떻게 동작하는지를 무조건 이해해야 하는데, 이들은 더욱이 낮은 추상화 레벨에서 설계되고 프로그래밍 되기 때문임.
  4. 현재, 혹은 목표가 프로세서 설계자라면 말할 것도 없다.

현재 Chapter에서 사용할 Architecture Y86-64의 특징

  • X86-64 Instruction set으로부터 영감을 얻은 것
  • 더 적은 data types
  • 더 적은 instructions
  • 더 적은 addressing mode (addressing mode란 주소를 지정하는 방식으로, instruction들의 인자로 오는 것들을 생각해보면 좋을 것이다.)
    • 예) D(Base, Index, Scale) → 이런 방식이 addressing mode 중 하나
    • 여기 Y86-64에선 Index와 Scale을 지원하지 않기에 addressing mode가 적다고 하는 것.
  • 단순한 byte-level encoding
    • byte 수준의 encoding → machine code가 더 커지는 단점이 잇음
    • 단, CPU의 decoding logic 설계가 훨씬 쉽다.
  • 매우 단순한 instruction set이지만, 정수 데이터에 대한 연산은 모두 가능
  • 이후에 이런 프로세서를 설계하면서 맞닥뜨리는 도전들을 봐볼 것이다.

여러가지 도전들

  • digital 하드웨어 설계의 배경지식을 제공해줄 것이다.
    • 기본 building block들
    • 어떻게 서로 연결되고 동작하는지를 이해하기 위해
  • HCL Language를 소개할 것이다.
  • logic 설계 개념을 알고 있어도 사용하는 표기를 이해하기 위해 읽어야 한다.

프로세서 설계의 단계

  • 처음에는 sequential한 연산을 기반으로 하는 Y86-64를 볼 것이다.
    • 하나의 instruction은 하나의 clock cycle마다 실행되는 경우를 봐볼 것.
    • 즉, cycle은 충분히 느려야 한다.
  • 이후 sequential한 설계를 기반으로 pipelined 프로세서를 만들기 위해 ‘변형’을 가해볼 것이다.
    • 각 instruction들을 다섯 단계(stage)로 나눌 것이다.
    • 한 cycle에는 하나의 stage를 실행하고, 또한 새로운 하나의 instruction이 pipeline에 들어올 것이다.
    • 즉, 5개의 instruction이 동시에 실행됨을 생각할 수 있다.
  • 이런 Y86-64 ISA의 순차적인 동작을 유지하기 위해 여러 “Hazard Condition”(각 instruction들 사이의 의존 관계 등)을 다뤄야 한다.

4.1장에서 다룰 내용

ISA를 정의하려 함 → ISA를 정의하는 것은 다음을 정의하는 것과 같다.

  • 상태의 다양한 요소들을 정의
  • instruction set과 이들의 encoding을 정의
  • convention들을 정의
  • 예외 사항을 다룸

4.1.1 Programmer-Visible State

Programmer-visible state란, 각 Instruction들은 processor state의 여러 부분들을 읽거나 수정할 수 있는데, 이렇게 읽거나 수정할 수 있는 processor state를 Programmer-visible state라고 한다.

  • 여기서 Programmer는 assembly code로 프로그램을 작성하는 프로그래머 또는 machine-level code를 생성하는 compiler 를 지칭한다.

이 Y86-64의 state

  • 15개의 program register (x86-64에서 %r15만 생략한 것)
  • → 각각 64-bit word를 저장한다.
  • three 1-bit Condition code들(ZF, SF, OF)
  • → 가장 최근의 산술,논리 연산의 영향에 대한 정보를 저장한다.
  • PC
  • → 현재 실행할 instruction의 주소를 담고 있다.
  • Memory→ program과 data를 담고 있다.
  • → 개념적으론 큰 byte 배열이다.
  • Stat(status code)→ normal operation인지 exception이 발생했는지를 나타낸다.
  • → 프로그램의 실행 상태를 나타낸다.

4.1.2 Y86-64 Instructions

위 그림을 보면 여러 instruction들이 있는 것을 볼 수 있다. 포함하는 요소들을 나열해보자면 다음과 같다.

  • 오직 8-byte integer operation
    • 오직 8-byte 정수형 데이터만을 다루기 때문에, 아무런 모호성 없이 이를 words 라고 지칭할 수 있다.
  • 더 적은 addressing mode
    • memory의 addressing mode로는 오로지 Displacement밖에 없다(index register 또는 scale factor는 y86-64에서는 지원하지 않는다).
  • 더 적은 operation

Y86-64 Instruction의 세부 사항

  • Move instruction : irmovq, rrmovq, mrmovq, rmmovq
    • 8-byte 데이터만 다루므로 뒤 접미사로는 q밖에 오지 않는다.
    • source : immediate, register, memory
    • destination : register, memory
    • 이들의 조합으로 더 많은 instruction이 있을 것 같지만, 4개밖에 없다. 그 이유는 다음과 같다.
      • Y86-64 instruction은 immediate value(상수)를 memory로 옮기는 것은 허용하지 않고 있다.
      • memory에서 memory로 직접 옮기는 것은 하나의 instruction으로 처리할 수 없다.(x86-64도 마찬가지임)
    • memory는 addressing mode로 Displacement만 지원한다.
  • Integer operation instruction : addq, subq, andq, xorq
    • 오직 register data에만 동작을 한다 (x86-64는 memory data에도 동작을 했었다).
    • 이 명령어들은 전부 3개의 condition code들(ZF, SF, OF)을 설정한다.
    • ZF : zero code
    • SF : sign code
    • OF : overflow code
  • Jump instruction : jmp, jle, jl, je, jne, jge, jg
    • 여기 책에서는 Jmp를 Branch라고 한다.
    • 각 Branch들은 설정되어 있는 Condition code와 각 branch type에 따라 선택된다.
    • 각 branch들의 조건은 x86-64와 동일하다.
  • Conditional move instruction : cmovle, cmovl, cmove, cmovne, cmovge, cmovg
    • 이들 전부 rrmovq instruction과 동일한 format을 가진다.
    • 단, destination register는 조건이 만족할 때만 update된다는 차이가 있다.
  • Call and ret instruction : call, ret
    • call : stack에 return address를 push하고 destination 주소로 jump한다.
    • ret : 해당 call로부터 반환시킨다.
  • push and pop instruction : pushq, popq
    • x86-64와 동일
  • Halt instruction : halt
    • x86-64에선 hlt 라고 하는 동일한 기능을 하는 instruction이 있다. → 단, x86-64에선 사용을 허용하지 않는데, 이는 전체 시스템에 대한 동작을 중지시키기 때문이다.
    • Y86-64에선 processor를 중지시키기 위해 사용한다.
    • 이 때 HLT 라는 status code를 설정한다.

4.1.3 Instruction Encoding

이제 4.1.3장에선 byte-level encoding에 대해 살펴볼 것이다.

Y86-64 Instruction의 Byte-level Encoding

  • 각 instruction들은 어떤 field를 가지느냐에 따라 1 ~ 10byte의 크기를 가진다.

Field

  1. Instruction Type
    • 모든 instruction들이 이 정보를 가지고 있음
    • 1-byte 크기를 가진다(2개의 4bit 정보)
    • 상위 4-bit : code 라고 한다.
      • 0 ~ 0xB의 범위안의 값을 가진다.
    • 하위 4-bit : function value 라고 한다.
      • instruction group은 같은 code 값을 가지는데, 이 때 이 function value로 instruction을 구분한다.
      • Operation들은 6이라는 code값을 공통으로 가지고 있고, Branch와 Move instruction들도 이와 같은 모습을 하고 있다. 따라서 각각 function value로 각각의 instruction들을 구분한다.Figure 4.3 Function codes for Y86-64 instruction set.

  1.  
    •  
      • 특이한 모습으로는, rrmovq 나 jmp instruction같은 경우, conditional이 아닌데도 같은 code 값을 갖는다.
      • 이는 instruction의 byte format이 동일해서 code 값을 공통으로 하는 것이다.
      • 즉, 다시 말하자면 code 값으로 byte format을 구분할 수 있다는 것.
  2. register identifier(ID) - rA, rB

  1.  
    • Instruction에서 4-bit의 크기를 차지한다.
    • Y86-64 ISA에선 15개의 register를 갖는데, 이 때 각 register는 0 ~ 0xE 범위의 번호를 주소로 갖는다.
    • 이 주소가 바로 register identifier 이다.
    • CPU의 register file에 저장된다.
    • 0xF의 값을 갖는 instruction들은 이를 register를 필요로 하지 않는 다는 의미로 사용한다.
    • 몇몇 instruction들은 하나 또는 두개의 register를 operand로 갖는다. → rA와 rB로 부른다.
      • 이들은 source 또는 destination으로 사용된다.
      • 또한 base register(in an address computation)로도 사용된다.
    • register를 인자로 갖지 않는 instruction들은 이러한 register specifier 공간이 없다(call 또는 ret instruction이 그 예시이다).
    • register를 하나만 인자로 갖는 instruction들은 다른 하나를 0xF로 지정한다(popq 또는 irmovq 와 pushq 가 그 예시이다).
  2. immediate value
    • 사용처
      • immediate data (예: irmovq 의 operand)
      • displacement for address specifier (예: rmmovq 와 mrmovq 의 displacement)
      • jmp 또는 call의 목적지 주소
        • 이 때의 목적지 주소는 절대 주소로 주어져야 한다.
        • 이전에는 PC-relative addressing을 x86-64에서 사용하고 있음을 봤었다. 이는 더 compact한 encoding을 위해, 그리고 다른 memory part로 이동될 때(linker에 의해 주소를 할당받을 때 등) branch의 target address를 수정할 필요 없게끔 하기 위해서이다.
        • 여기선, 더욱이 간단함을 더 중요시하고 있기 때문에 absolute addressing 방식을 사용한다.
        • 또한 x86-64와 마찬가지로 모든 정수는 little endian encoding을 사용한다. → 즉, imm 값은 모두 8byte이고, 전부 little endian으로 표기해야 한다.

예시

 

rmmovq     %rsp, 0x123456789abcd(%rdx)
  • rmmovq 는 40이라는 instruction type을 가지고 있음
  • source register로 %rsp 를 갖는다. → rA field로 들어가고 이는 4의 값을 가진다.
  • base register로 %rdx 를 갖는다. → rB field에 들어가고 이는 2의 값을 가진다.
  • Displacement로 사용되는 Imm value인 0x123456789abcd 값은 8-byte little endian에 따라 instruction에 들어가게 된다.
  • 따라서 Instruction의 구성은 다음과 같게 된다.
4042cdab896745230100 -> [40][42][cdab896745230100]

💡 Comparing x86-64 to Y86-64 instruction encodings x86-64 : compact하고, register-field는 다양한 위치에 들어갈 수 있고, 또한 1, 2, 4, 8byte 등의 상수 값을 처리할 수 있다. Y86-64 : 더 간단하지만 덜 compact하다. 또한 고정된 위치에서만 register field가 등장하고, 오직 8-byte상수만 처리 가능하다.

 

연습문제 4.1

다음 assembly code를 보고 instruction의 byte-encoding을 결정해라

  • 정답
  • 0x100 : 30f30f00000000000000
  • 0x10a : 2031
  • 0x10c : 4013fdffffffffffffff
  • 0x116 : 6031
  • 0x118 : 700c0100000000000000

연습문제 4.2

아래의 byte sequence를 보고 Y86-64 instruction으로 encoding해라

(위의 문제를 풀어보면, 이들을 encoding 하는 것은 어렵지 않다)

  • 정답
  • 책 p.517에 나옴

4.1.4 Y86-64 Exceptions

Programmable-visible state는 도한 status code, Stat을 포함한다고 했다. 이제 이 Stat에 대해 다뤄보자.

  • Code value 1 : AOK
    • 정상적인 실행을 하고 있을 때 설정됨
  • Code value 2 : HLT
    • halt instruction을 맞닥뜨렸을 때 설정됨
  • Code value 3 : ADR
    • invalid memory address에 읽기 또는 쓰기를 시도 시 설정됨
      1. instruction을 memory에서 CPU로 fetch할 때
      2. data에 read 또는 write 할 때
    • 주소의 maximum을 정해두는데, 이를 넘어갈 때 ADR이 설정된다.
  • Code value 4 : INS
    • invalid instruction을 맞닥뜨렸을 때

Y86-64의 Exception에 대해

Y86-64는 exception들 중 하나를 만나면 프로세서의 실행을 멈춰버린다. x86-64같은 복잡한 프로세서의 경우 exception에 대한 복잡한 처리를 수행한다.

  • exception handler라는 것이 있음
  • 각 exception당 수행할 것들이 미리 설계되어 있음
  • 이 수행에 대해 설정도 따로 할 수 있음 → signal handler를 정의함으로(Chapter 9에서 자세히 볼 예정)

4.1.5 Y86-64 Programs

long sum(long *start, long count)
{
		long sum = 0;
		while (count) {
				sum += *start;
				start+=;
				count--;
		}
		return sum;
}

여기에서는 위의 코드를 gcc로부터 생성된 x86-64 code와 Y86-64의 code를 비교하며 살펴볼 예정이다.

x86-64 V.S. Y86-64

x86-64 code.

Y86-64 code.

Complete assembly code written in Y86-64

Figure 4.7. Simple program writeen in Y86-64 assembly code.

코드를 보기 전 알아야 할 구성요소들

이 프로그램은 data와 instruction 모두를 포함하고 있다. 여러가지 지시자들도 있는데, 이를 살펴보자.

. 으로 시작하는 단어 : assembler directives라고 한다. 이는 assembler에게 word data들 또는 생성된 code들이 어디 주소에 생성되어야 할지를 설명해준다.

  • .pos
    • (line 2) : 생성된 code는 주소값 0에서부터 시작해야 함을 지시하고 있다. (Y86-64 프로그램은 항상 0에서부터 시작한다고 한다)
    • (line 39) : line 40에 stack이라는 label을 볼 수 있는데, 즉 이 stack은 주소 0x200부터 아래로 자라난다고 정의를 한 것이다. 이것으로 line 3에서 stack pointer %rsp를 초기화 하는 모습을 볼 수 있다.
  • .align
    • (line 8 ~ 13) : 4개의 words를 가지는 array를 선언한 모습을 볼 수 있다. line 9의 array label은 array의 시작을 명시하고 있다. 또한 .align 지시자를 통해 8-byte boundary로 align되도록 하고 있다. main에서 이 four-words array를 sum의 인자로 넘기는 모습도 확인할 수 있다.

YAS, assembler for creating Y86-64 code

위의 예시를 봤듯이, Y86-64 code를 만드는 도구는 assembler가 유일하다. 즉, 프로그래머가 compiler, linker, runtime-system가 해야 할 일을 도맡아서 해야한다는 것이다. 다행히도, 이 Y86-64 code에 대해선 작은 프로그램들만 볼 것이라고 한다.

위의 code를 YAS에 의해 assembling한 결과

  • YAS의 결과
    • ASCII 형식으로 되어 있어, 우리가 읽기 쉽다.
    • 하나의 line에 instruction 또는 data를 포함하고, 또한 address와 그 뒤로는 1 ~ 10byte의 값들(instruction들)이 나온다.

    • 위쪽 주소를 보면 0x000부터 해서 0x090까지 instruction들에 대한 byte sequence가 적혀있다.추가적으로 볼 것
    • 위의 0x000부터 0x090까지는 아래의 text영역의 범위라고 할 수 있다.
    • 또한 stack의 영역은 0x200부터 시작이니 옆의 그림에서의 stack의 시작 주소(맨 위 주소)는 0x200라는 것을 알 수 있다.
  • 즉, 흔히 볼 수 있는 process의 virtual address space의 그림을 보며 이해를 해보자면 다음과 같다.

YIS???

  • instruction set simulator라고 하는데, 이의 목적은 Y86-64 machine-code 프로그램의 실행을 프로세서의 세부적인 행동을 신경쓰지 않고 모델링하기 위해서이다.
  • 실제로 이 code를 실행하기 전에 program이 동작을 잘 하는지를 살펴보기 위한 debugging 용도로 있다고 한다.

  • register와 memory 값들 중 왼쪽 값은 초기 값을 나타낸다. (전부 0임)
  • 오른쪽 값은 프로그램이 끝날 때의 값을 보여주고 있다.
    • %rax register에 담긴 값은 four words array를 sum한 결과값이 담겨 있는 것이다.
    • stack에 대한 register인 %rsp는 0x200의 값을 가지고 있는데, 이는 start address라고 위에 언급했었다.
    • 또한 memory를 보면 0x1f0 ~ 0x1f8까지 변경된 것을 알 수 있는데, 이는 메모리 stack이 0x200에서 아래로 자라나기 때문에 0x200보다 작은 범위에서 사용이 되고 있음을 보여주고 있다.
    • 그리고 실행 코드의 주소의 상한이 0x090인 것을 미루어 보아, 메모리 영역이 실행 코드를 침범하지 않았다는 것을 볼 수 있다.

4.1.6 Some Y86-64 Instruction Details

간략하게 눈여겨 볼만한, 또는 주의해야할 점들을 살펴볼 예정이다.

대부분의 Y86-64 instruction 들은 program state를 확실하게 변경한다. 하지만 그렇지 않은 것들이 있는데, 이를 좀 살펴보자.

pushq instruction : stack pointer를 8만큼 감소시키고 또한 memory에 register 값을 쓰는 두 가지의 동작을 수행한다.

  • 이는 무엇을 먼저 수행하는지 명확하지 않을 수가 있다.
    1. %rsp의 원래의 값을 push하는 걸 수도 있고,
    2. 감소된 %rsp의 값을 push하는 걸 수도 있다.

이런 두가지의 해석의 존재는 모호성을 유발하므로 이를 x86-64에서 동작하는 바와 같이 동작한다고 생각할 것이다. 이때, x86-64의 동작을 확인해볼 필요성을 느낄 것이다.

x86-64의 push 동작(Practice Problem 4.7)

  1. 기존 stack pointer의 주소를 %rax 에 옮겨놓았다.
  2. 이후 stack pointer 주소를 stack에 push한다. ← 이 동작을 보고 위의 두가지 해석 중 어떤 방식으로 동작하는지를 알 수가 있다.
    • 만약 %rsp를 push한 결과,
      • push된 값이 %rsp가 감소되기 전의 값이라면 1번과 같이 동작한 것이다.
      • 만약 %rsp가 감소된 값이 들어갔다면 2번과 같이 동작한 것이다.
      → 이는 미리 옮겨 놨던 %rax 레지스터 안에 들어있는 값과 pop된 값을 비교함으로써 알 수 있다.
  3. 따라서 push된 %rsp의 값을 확인하기 위해 %rdx에 push된 값을 꺼내서 옮겨 넣은 것이다.
  4. 즉, push된 %rsp의 값과 미리 저장했던 원래의 %rsp값 끼리 subq 연산을 통해,
    1. 0이 나온다면 1번과 같은 동작으로
    2. 0이 나오지 않는다면 2번과 같은 동작으로 동작한 것이다.

→ 프로그램의 결과로 항상 0이 나온다고 하므로, x86-64의 pushq 연산의 동작은 원래의 주소를 push하고 stack pointer를 감소시키는 것이다.

💡 하지만 x86 processor에서도 push 연산은 일치하지 않는다? (Intel documentation의 PUSH instruction부분에 따르면) IA-32 processor의 경우, 감소되기 전의 값을 push한다. 하지만 8086 processor의 경우, 감소된 새 stack pointer 값을 push한다고 한다.

즉, x86 processor가 어떠한 모드 아래에서 동작하는지에 따라 동작이 일치하지 않을 수가 있다는 것이다.

이렇게 일치하지 않게 되면 무엇이 안좋은가?

  1. code의 portability(이식성)이 떨어지게 된다.
  2. 문서를 더 복잡하게 한다.

따라서 이러한 이유로 위의 4.1.6에서와 같이 세부적인 사항들까지 살펴봤던 것이다.

'Computer Science > 컴퓨터 구조' 카테고리의 다른 글

[CSAPP] 4.3 Sequential Y86-64 Implementations  (0) 2023.03.01
[CSAPP] 4.2 Logic Design and the Hardware Control Language HCL (논리 설계와 하드웨어 제어 언어 HCL)  (0) 2023.03.01
[CSAPP] 9.7 Case Study: The Intel Core i7 / Linux Memory System  (0) 2023.03.01
[CSAPP] 9.8 Memory Mapping (메모리 맵핑)  (0) 2023.03.01
[CSAPP] 8.3 System Call Error Handling (시스템 콜 에러 처리)  (0) 2023.03.01
    'Computer Science/컴퓨터 구조' 카테고리의 다른 글
    • [CSAPP] 4.3 Sequential Y86-64 Implementations
    • [CSAPP] 4.2 Logic Design and the Hardware Control Language HCL (논리 설계와 하드웨어 제어 언어 HCL)
    • [CSAPP] 9.7 Case Study: The Intel Core i7 / Linux Memory System
    • [CSAPP] 9.8 Memory Mapping (메모리 맵핑)
    tgool
    tgool

    티스토리툴바