행복한 연어의 이야기

(윈도우 시스템) 10. 컴퓨터 구조 세번째 본문

IT/윈도우 시스템 프로그래밍

(윈도우 시스템) 10. 컴퓨터 구조 세번째

해피살몬 2021. 8. 9. 21:26

1. 절차적 함수호출(Procedure Call) 지원 CPU 모델

스택 프레임 구조

함수내에 선언된 변수가 스택에 할당되는 것은 이미 알고 있다.

함수 호출 과정에서 할당되는 메모리 블록을 스택 프레임이라고 한다.

함수가 반환되면 그 함수의 스택 프레임을 날린다.

 

스택 포인터(Stack Pointer) 레지스터

스택은 LIFO 구조를 가지고 있다.

스택을 쌓거나 반환하기 위해서는 TOP 의 위치를 기억해야하는데

CPU 레지스터에 그 역할을 하는 sp(Stack Pointer) 라는 이름의 레지스터가 존재한다.

우리는 이미 4. 컴퓨터 구조 두번째에서

r5 레지스터를 sp(스택 포인터) 레지스터로 디자인 해 두었다.

함수 호출시 선언된 변수의 크기만큼 sp 는 올라가게 되는데

호출된 함수가 종료 될 경우 sp 는 그만큼 내려가야 한다.

하지만 그만큼은 어느 정도 인가?

이를 해결하기 위해 프레임 포인터 레지스터를 사용해보자

 

프레임 포인터(Frame Pointer) 레지스터

Base Pointer 라고도 한다.

위 글에서 그만큼 을 모르기 때문에 fp 레지스터를 활용한다고 하였다.

하지만 그만큼을 계산하기위해 변수의 크기만큼 더해주고 빼주는 연산을 하는 것은

CPU 에게 오히려 추가 연산을 시켜 비용을 늘리는 결과를 초래한다.

그래서 우리는 sp 포인터가 가르키고 있던 주소값을 기억하는 형태로 구현하도록 한다.

 

그렇다고 새로운 함수가 호출될때마다 fp 레지스터 값을 백업해 놓지 않고 

sp 레지스터의 값을 fp 레지스터로 바로바로 덮어 씌운다면 문제가 발생한다.

한번의 함수 반환 뒤에는 이미 덮어 씌워졌기 때문에 그 이전의 함수로 갈수 없기 때문이다.

이를 해결하기 위해서 sp 레지스터의 값을 fp 레지스터로 덮어씌우기전에 

fp 레지스터의 값을 스택에 저장(백업)한다.

그래서 함수 반환시 fp 레지스터는 스택에 있는 값을 불러와 이전 함수위치를 알수 있게 되는 것이다.

정리하자면

 

함수 호출시

1) fp 의 주소값을 스택에 저장

2) fp 에 sp 값을 덮어씌운다.

3) 할당된 메모리 만큼(지역변수, 매개변수) sp 값 증가

함수 종료시

4) sp 에 fp 값을 덮어씌운다. (3번에서 할당한 스택프레임을 날리는 효과)

5) 스택에 저장된 이전 fp 값을 fp 에 덮어씌운다.

 

2. 함수 호출 인자의 전달과 PUSH & POP 명령어 디자인

함수 호출 인자의 전달 방식

함수 호출시 전달되는 인자를 어디에다 저장하는지는 CPU 마다 다르다.

우리는 레지스터 말고 스택에 쌓는 것으로 구현해보자

sp 위치에 매개변수 a 를 저장하고 그만큼 크기를 증가시키는 명령어를 만들어보자

(지역변수도 스택에 저장한다는 것은 동일하나 지역변수는 함수를 호출할때 한번에 메모리를 잡는다는 차이가 있다.)

 

PUSH & POP 명령어 디자인

PUSH : sp 가 가르키는 현재 위치에 값을 저장하고 sp 를 증가시켜 다음 메모리 주소를 가르킨다.

POP : 맨 위에 있는 값을 빼서 레지스터나 메모리에 저장하고 그 만큼 sp를 감소시킨다.

두 명령어를 기존에 있던 명령어들로 조합해서 완성해보자

 

매개변수가 7 이라고 가정 PUSH 7 명령어는 다음과 같이 작성할 수 있다.(대괄호 표시는 Indirect)

어셈블리 코드

ADD r1 7 0

STORE sp 0x40

STORE r1 [0x40]

ADD sp sp 4

해석

r1 에 매개변수를 저장 (ADD r1 7 0)

sp는 다음 메모리 주소값을 가르키고 있고

그 값을 임의의 값 0x40 에 넣습니다. (STORE sp 0x40)

 Indirect 모드로 0x40 이 가르키고 있는 값(sp 가 가르키고 있는 값)에 

r1의 값을 넣습니다. (STORE r1 [0x40])

sp 의 값을 증가 시킵니다. (ADD sp sp 4)

 

POP r1 명령어는 다음과 같이 작성 할수 있다. (맨위 데이터는 4바이트라 가정)

어셈블리 코드

SUB sp sp 4

ADD r1 sp 0

해석

sp 는 다음 메모리 주소(비어있는 값)을 가르키고 있기 때문에 데이터 크기만큼 빼줘야 합니다. (SUB sp sp 4)

sp 메모리에 있는 값을 r1 데이터에 넣어줍니다. (ADD r1 sp 0)

 

3. 함수 호출의 의한 실행의 이동

메모리 구조와 프로그램 카운터

우리는 메모리 구조가

코드, 데이터, 힙, 스택 영역으로 나뉜다는 것을 알고 있다.

1. 컴퓨터 구조 첫번째에서 CPU가 Fetch, Decode, Execution 순서로 일을 한다고 했는데

Fetch를 할때 가져오는 위치가 코드영역이다.

그리고 코드 영역에서 어디까지 코드를 불러왔고 다음은 어느 코드를 부를지 기록하는 레지스터가 하나 있는데

그 레지스터가 바로 pc(Program Counter) 레지스터 이다.

기본적으로 pc 레지스터의 크기는 CPU 가 알아서 컨트롤 한다.

 

함수 호출과 함수 종료

함수 호출과 함수 종료시 이동은 위에서 말한 pc 가 담당한다.

다만 fp 와 마찬가지로 함수 종료시 pc 가 돌아갈 값을 백업해두지 않는다면

함수 종료시 곤란할 것이다.

그렇기에 pc 도 돌아갈 값을 따로 저장하는데 그 위치가 fp 와 마찬가지로 스택이다.

 

4. 함수 호출규약

함수 인자를 넘기는 방식, 스택 프레임을 반환하는 방식을 약속해 놓은 것을 말한다.

32비트, 64비트 에 따라 다르며

64비트는 운영체제에 따라 또 달라진다.

 

알고 넘어가야할 것

1. 스택이 관리되는 방법

2. 함수 호출규약

3. 전달인자와 레지스터(32비트와 64비트의 레지스터 활용 차이)

 

'윤성우 저자'님의 '뇌를 자극하는 윈도우즈 시스템 프로그래밍' 책을 보고 정리한 내용입니다.

 

Comments