지난 글에서는 SPARC의 (근데 찾아보니 아마도 기본적인 컴퓨터 공통의) 메모리 맵을 정리하면서 BSS 섹션의 내용을 추가로 정리하였다.
bss 섹션은 정적 메모리 영역 중 하나로, 이곳에 할당한 변수는 모두 0으로 초기화되는 특징이 있었다.
이번 글에서는 다시 '스택' 영역으로 돌아와서, 새로운 스택 영역의 메모리를 할당하는 단위인, '스택 프레임' 에 대해 정리하고자 한다.
Stack Frame (스택 프레임)
스택 프레임은 나중에 정리할 '서브루틴' 과 관련되어 있다.
서브루틴(함수)는 호출될 때마다, 레지스터 공간과 함께 스택 공간을 새로 할당한다.
이때 각 함수마다 할당하는 스택 공간의 크기를 임의로 지정할 수 있는데, 이 스택 공간을 '스택 프레임' 이라고 한다.
즉, 스택 프레임은 서브루틴에 할당되는 메모리 공간이라고 볼 수 있다.
다시 메모리 맵을 보면서 정리해보자.
스택이라는 메모리 영역은 높은 주소부터 시작하여, 새로 할당될 때마다, 낮은 주소로 감소한다.
이 그림에서는 그림을 거꾸로 그려두었는데, 이렇게 보면 마치 스택이 쌓이는 모습과 같이 보인다.
저 이미지의 'Stack' 한 칸이 스택 프레임을 나타낸다.
각 스택프레임은 스택프레임의 시작주소인 'frame pointer' (%fp) 와, 끝 주소인 'stack pointer' (%sp) 로 구성되어있다.
메모리의 방향이 감소하는 방향으로 스택이 할당되기 때문에, 스택 프레임의 사이즈는 %fp - %sp 이다.
스택 프레임 할당과 반환
새로운 스택 프레임을 할당하는 것은, 전에 save 명령어와 restore 명령어를 정리하면서 공부하였다.
다시 복습하자면, save 명령어는 현재 스택에 할당된 가장 마지막 끝 주소인 stack pointer (%sp) 를 원하는 사이즈 만큼 감소시킴으로써 새로운 스택 프레임을 할당한다.
save %sp, -96, %sp
위와 같은 명령어를 실행하여 스택 프레임을 할당한다.
기존 스택 포인터를 96바이트만큼 감소시킨다는 의미로, 새로운 96바이트의 스택 프레임을 할당하겠다는 의미와 같다.
스택 프레임을 새로 할당하게 되면, 기존 %sp 의 값이 %fp 의 값으로 새로 설정되고,
%sp의 값은 스택 프레임의 사이즈만큼 감소한다.
하나의 서브루틴에서 볼 때, %fp는 스택 프레임의 시작 주소, %sp는 스택 프레임의 끝 주소와 같다.
(스택은 감소하는 방향으로 할당되므로, 시작주소 값 > 끝 주소 값 인 점은 기억하자)
만약 스택의 사용이 끝났다면, 이제 사용하지 않는 스택 프레임을 다시 반환해야한다.
이때는 restore 명령어로 반환하게 된다.
스택 프레임의 크기
스택 프레임의 크기에는 나름의 규칙이 있다.
우선 스택 프레임은 최소 64바이트의 크기를 가져야한다.
나중에 정리하겠지만, 스택 프레임에는 i-register, l-register 의 값을 백업하는 공간으로 (4byte*8) * 2 = 64 바이트의 공간이 최소로 필요하다.
그 밖에도, OS가 사용하는 공간이 필요하다.
(이미 64바이트인데 그럼 OS를 위한 공간까지 가지려면 최소 공간의 사이즈는 더 커져야 되는 거 아닌가..?)
=> 교수님 말씀으로는... 이 내용은 어셈블리 수업 범위 밖의 내용이니 지금은 그런가보다 하라고 하셨다..ㅎ
그리고 스택 프레임의 크기는 반드시 8의 배수여야 한다.
SPARC가 다루는 메모리 공간의 단위 중 제일 큰 단위가 double (8byte) 이기 때문에 그렇다.
스택 프레임의 크기는 보통 64 + 24 + 4 = 92 바이트에서 8의 배수로 맞춘 96 바이트의 공간을 가지는 것이 일반적이다.
여기에 더해 만약 함수에서 지역변수나 구조체 등을 사용한다면 필요한만큼 스택 프레임 크기를 더 늘려서 할당할 수 있다.
이때 64바이트에 더해주는 24바이트는, 서브루틴 내에서 또 다른 서브루틴을 호출 시 인자로 넘길 값을 저장하는 공간이다.
(o-register에서 o0부터 o5까지는 함수 인자 전달 용도로 쓰는 거니까 4 byte * 6 = 24byte 로 이해했다.
그런데, i-register, l-register 는 백업 용도니까 이해되는데, o-register 도 백업을 해야하나?)
=> 일단 백업 용도가 맞는 것 같은데, 역시 교수님이 하신 말씀으로는 컴파일러 수업같은 깊이 있는 내용을 다루는 수업을 들어야 이해가 가능하다고... 지금 단계에서는 그러려니 해야한다고 한다.
또 추가적으로 더해주는 4바이트는, 만약 반환하는 데이터가 단일 데이터가 아니라 여러 데이터의 묶음인 구조체라면, 해당 구조체의 포인터를 반환해야 하기 때문에, 그 구조체의 포인터 주소를 저장할 4바이트를 저장할 공간이 필요하다.
(그럼 반환 시 구조체를 안쓰면 4byte는 더 할당할 필요가 없지 않을까?)
=> 구조체를 사용하지 않아도 4byte 공간을 선언하는 것은 이미 이렇게 '정해진' 규칙이라서 따라야 한다.
만약 이에 더해 추가적인 지역변수를 사용하고자 한다면 스택 프레임을 크기를 필요한만큼 더 늘린 뒤, 8의 배수에 맞추면 된다.
만약 이 92바이트 기본적인 할당량에 4-byte 크기의 지역변수를 2개 더 사용한다고 해보자.
그러면 92 + 4 * 2 = 100 바이트의 공간이 필요하고, 이를 8의 배수에 맞춰주면 104 바이트의 스택프레임 공간을 할당하도록 하면 된다.
스택 프레임의 구조
스택 프레임은 위 그림과 같은 구조로 되어 있다.
우선 64byte 의 i-register, l-register 를 저장하는 공간은 모든 서브루틴이 필수로 가져야 하므로, 64바이트가 최소 스택프레임의 사이즈로, %sp 바로 뒤에 나온다.
다음으로 만약 서브루틴 내에서 또 다른 서브루틴을 호출하는 경우, 4byte 구조체 포인터 공간과 24byte 의 6개 매개변수를 전달할 공간을 추가로 할당해야 한다.
다른 서브루틴을 호출하는 일이 없다면, 이 28바이트의 공간은 할당하지 않아도 된다.
마지막으로 사용하고자 하는 지역변수의 크기만큼 추가로 더 할당된 공간이 %fp가 가리키는 위치까지 존재한다.
그렇다면 여러개의 지역 변수가 있을 때, 지역변수는 어떤 순서로 스택 프레임에 채워질까?
지역변수는 먼저 선언된 지역변수가 더 높은 (더 시작점에 가까운) 주소를 가지면서 스택 프레임에 채워진다.
만약 위 그림처럼 int형(4바이트)의 지역 변수를 4개 생성한다면, %fp에 가까운 쪽부터 차례대로 할당된다.
만약 스택 프레임 크기를 잡을 때, 지역 변수를 4개보다 더 많이 저장할 수 있도록 크기를 잡았다면, 남는 공간은 위 그림 처럼 비어있는 채로 남아있게 된다.
스택 프레임 할당 예시
4byte 크기의 지역변수를 5개 사용하는 스택프레임을 할당하려고 한다.
이 스택 프레임에서 함수 호출은 없다.
이 경우, 스택 프레임의 사이즈는 최소 얼마를 선언해야 되는가?
64 + (4*5) = 84
-84 & -8 = -88
따라서 88 바이트를 아래와 같은 방법으로 선언하면 된다.
save %sp, -88 , %sp
또는
save %sp, -(64 + (5*4)) & -8, %sp
또는
save %sp, -(64 + (5*4)) & 0xfffffff8, %sp
0xfffffff8 은 -8 과 같다.
직접 8의 배수를 계산하는 것이 힘들다면, 8의 배수로 맞춰 주기 위해서 -8 과 &연산을 취할 수도 있다.
왜 이렇게 연산하면 8의 배수로 맞춰지는 걸까?
8의 배수가 아닌 -20에 & -8 연산을 한 결과를 살펴보자.
이렇게 된다.
양수 계산과는 헷갈리지 말자.
이렇게 된다.
지금까지 스택프레임의 개념과 스택프레임의 구성, 그리고 스택 프레임의 최소 사이즈와 지역 변수의 할당 순서를 정리해보았다.
다음 글에서는 지금까지 정리한 내용을 토대로 실제 코드에서 어떻게 활용하는지 코드 예를 정리해보고자 한다.
'CS > 어셈블리' 카테고리의 다른 글
[SPARC] 27. 일차원 배열 (0) | 2023.11.24 |
---|---|
[SPARC] 26. Stack Frame 사용 예제 (0) | 2023.11.19 |
[SPARC] 24. SPARC 메모리 맵 정리 ( + bss 영역) (0) | 2023.11.16 |
[SPARC] 23. label로 data 영역의 메모리주소 가져오기 (set, sethi) (0) | 2023.11.09 |
[SPARC] 22. 정적 메모리와 경계정렬 (0) | 2023.10.20 |