Computer Science/컴파일러개론(Compiler)

[컴개/CI] IR Translation: Storage Management

gxxgsta 2023. 12. 14. 16:27
반응형
SMALL

2 Classes of Storage in Process

우리가 사용할 수 있는 저장소는 두 가지가 있다.

 

Registers

빠른 접근
일반 프로그래머에게는 보이지 않음

간접 접근이 불가능하다. 즉, 주소를 얻을 수 없다.

 

Memory

상대적으로 접근이 느리지만, 간접접근 즉 주소를 얻을 수 있다.

 

위와 같은 특성으로 인해 변수 저장을 register를 이용할 것인지, memory를 이용할 것인지 결정하는데, 이는 HIR을 LIR로 변환하는 단계에서 주로 결정된다.

 

Storage Class Selection

저장소에 저장하는 방법은 두 가지 방법이 존재한다.

 

All memory approach

이 방법은 일단 모든 변수를 memory에 저장한 후

해당 변수 중 가능한 것을 register에 저장하도록 조정 방법이다.

 

Standard approach

이 방법은 All memory approach에 원칙을 추가한 방법이다.

 

global 변수나 static 변수는 메모리에 두는 것을 원칙으로 한다.

 

이후 지역 변수에 대해 addr v, 또는 3-addr code 등으로 인해 메모리 주소가 필요한 structs 또는 arrays는 메모리에 저장을 한다. 이후 남은 연산들에 대해 int x와 같은 스칼라 변수는 레지스터에 저장을 하지만, &x과 같이 주소값에 접근하는 변수는 메모리에 저장을 해야 한다.

 

이때 변수들을 레지스터에 넣는다고 언급하였는데, 이 레지스터는 버추얼 레지스터라고도 부른다. 레지스터의 숫자를 세지 않으며 저장하므로, 즉 레지스터의 개수가 무한하다고 가정하고 진행하기 때문이다. 버추얼 레지스터 단계에서 작업하는 것이 중간언어 IR이다.

 

Memory의 4대 영역

메모리의 영역은 4가지로 나눌 수 있다.

 

Code space

명령어를 저장하는 공간으로 read-only로 만드는 경우가 많은데, 이는 read-only로 만들면 성능이 좋기 때문이다.

 

Static (or Global)

프로그램과 life time을 함께하는 변수들의 집합으로 global 또는 static 변수들을 이야기한다.

 

Stack

함수 호출에 따라 생성됐다가 사라지는 스택 변수가 있다.

이들을 보통 local 변수라고 하고, 블럭의 life time을 함께한다.

 

Heap

malloc, new 등과 같은 System call에 의해 동적으로 할당되는 공간이다.

 

Memory Organization

 

위 사진은 메모리의 구성이다.

 

stack과 heap은 런타임에 크기가 변경되고, stack은 아래쪽, heap는 위쪽으로 증가하며 스택과 힙의 위치가 바뀔 수도 있다.

 

이때, 스택과 힙이 마주보고 있는 이유는 고정된 크기를 할당하는 것이 아니라, 스택보다 힙이 더 많이 사용하는 경우 또는 힙보다 스택이 더 많이 사용하는 경우와 같이 한쪽이 더 많이 사용하는 경우에 효율적으로 메모리 공간을 사용할 수 있기 때문이다.

 

code와 static data는 컴파일러에 의해 크기가 결정된다.

컴파일 이후 코드는 어셈블리 코드에서 바이너리 코드로 변환되어 디스크에 .exe 파일로 저장한다. 이때, .exe 파일을 열면 code와 static data가 존재한다.

 

static data + code는 프로그램의 사이즈이고, 여기에 stack과 heap을 더하면 프로세스의 사이즈가 된다.

 

예1) Windows PE (Portable Excutable)

 

위 사진은 Window의 PE 모양으로 프로그램이 디스크 있는 모양이다.

헤더의 개수가 많으며 가장 아래의 Mappable Section만 진짜 데이터와 코드가 존재한다. 다른 부분은 Mappable Section의 위치를 다양한 표준으로 제공하는 역할을 한다.

 

예2) ELF (Executable and Linkable Format)

 

위 구조는 리눅스의 구조로 program header table로 접근이 가능하다.

이때, .text는 코드이고, .rodata, .data는 static data를 의미한다.

 

Memory Organization (Data)

코드를 제외한 데이터 영역을 살펴보자.

변수는 어딘가에 저장되는데, 그 저장된 곳과 변수의 이름과의 연결고리를 자료구조로 가지고 있어야 한다.

 

용어를 살펴보자.

 

environment

변수가 어떤 위치에 저장되어 있는가를 나타내는데,

storage location, 즉 메모리 주소를 나타낸다.

이 과정이 없었으면 우린 변수 대신 주소값을 직접 접근하여 값을 변경해야 했다.

<변수, storage location>

 

state

변수의 이름, 어떤 값과 묶여있는 지를 나타내며, environment보다 조금 더 상위 개념이다.

<변수, 값>

 

바인딩 (binding)

바인딩은 변수와 속성(저장위치, 값)을 엮어내는 것으로

지금은 범위를 좁혀서 어떤 environment 에서 변수 이름 N이 storage location S 에 지정되면 N이 S 에 바인딩 된다고 한다.

 

N이 composite type이면, 즉 배열 타입이면 연속적인 storage location에 바인딩 된다.

 

Memory Organization (Global Data)

 

Static allocation

프로그램 전체를 수행하는 동안 변하지 않는 location으로 바인딩 하는 것이다.

즉 전역 변수나 constants, C에서의 static 변수를 의미하고, 이들은 코드와 붙어 있어 디스크에서 공간이 확보되어 있는 경우가 많다.

 

Heap allocation

변수가 malloc, new 등으로 선언할 때 저장되는 곳으로, 연속적인 global 영역의 일부를 OS로부터 받은 것으로, 프로그램 수행 중 요청과 반환(deallocate, free)을 지원하는데, 반환 순서는 무순이다. (stack에서는 역순으로 반환한다.)

 

반환을 안 하려면 언어에서 garbage collection을 지원해야 한다. garbage collection는 언어 밑에서 free section, in-use section 등으로 구분하여 유지를 해야 한다.

 

 

반응형
LIST