· 목차
안녕하세요 이번에는 프로시저(Procedure)에 대해서 총 정리하도록 하겠습니다.
이전 포스팅에서 간략하게 살펴보고 총 정리본이 필요하다고 생각했기 때문에 이렇게 총 정리하게 되었습니다.
함수, 서브루틴, 루틴, 메서드, 프로시저는 소프트웨어에서 특정 동작을 수애하는 일정 코드 부분입니다.
이전 포스티에서 이야기 했던 것 처럼 '특정한 작업을 위해 재활용할 수 있도록 구현한 코드 블록'을 의미합니다.
일부는 함수와 프로시저를 반환값(return value)의 유무를 가지고 차이를 구별하기도 하지만,
이는 반은 맞고 반은 틀린 말이 됩니다. 왜냐하면 언어와 그 정의에 따라 다르기 때문입니다.
일부 언어에서는 프로시저도 반환 값을 가질 수 있으며, 함수와 프로시저의 구분이 없는 언어도 많기 때문입니다.
저희의 주제는 컴퓨터 구조에서 특히 LEGv8에서 프로시저를 설명하기 위한 것이기 때문에
더 궁금하신 분은 아래의 자료를 참고해주세요.
Procedure calling
Procedure calling은 다음과 같은 6가지 단계를 거치며 procedure을 수행하게 됩니다.
1. place parameters in registers X0 ~ X7
2. Transfer control to procedure
3. Acqurie storage for procedure
4. Perform procedure's operations
5. Place result in register for caller
6. Return to place of call
Procedure calling을 하게 되면, X10 ~ X7에 해당하는 register에 인자를 할당합니다.
여기서 만약에 X8 이상의 주소에 인자를 할당하게 되면 어떻게 될까요?
해당 값들은 stack register에 값을 할당하게 됩니다. 해당 register은 기존에 배웠던 X28의 Stack Register입니다.
여기서 특이사항이 하나 더 있는데요, 마이크로프로세서를 배워보신 분들은 Stack과 관련하여
PUSH와 POP명령어가 존재했던 것을 알고 계실 것 입니다. 또한 마이크로프로세서에서는 LIFO(Last In First Out)이
있다는 것을 알고 계실 것이라고 생각합니다.
하지만 저희가 배우는 Arm architecture(LEGv8)에는 'LIFO', 'PUSH', 'POP'이 따로 존재하지 않습니다.
그렇기 때문에 PUSH와 POP과 관련된 기능을 기존에 배웠던 BR명령어를 통해서 할당해주고, 지워주는 방식으로
수행하며, LIFO의 개념이 없기 때문에 저희는 직접 관리해주어야 합니다.
그렇다면 다시 실행 순서로 돌아가서 인자를 할당하고 난 이후에는 2번을 실행합니다.
BL의 명령어를 이용하여 return address로 PC+4의 값을 Link Register에 할당하며 지정된 주소로 분기합니다.
이후에 프로시저를 위한 저장 공간 확보를 하며 Stack Frame을 생성하고,
필요에 따라 프레임 포인터(X29)를 설정하여 현재 스택 프레임을 참조합니다.
또한 함수 내에서 변경될 수 있는 Callee-saved Register(X19 ~ X28, X29)를
Stack에 저장합니다.
그 다음에는 프로시저의 작업들을 수행하고 난 이후에 반환값을 X0에 저장합니다.
마지막으로 호출 위치로 복귀합니다.
첫 번째로 스택 프레임을 정리합니다. 함수 시작 시 저장한 레지스터를 Stack에서 복원하며,
스택 포인터를 조정하여 스택 프레임을 해제합니다.
마지막으로 RET 명령어를 이용하여 Link Register(X30)에 저장된 복귀 주소로 분기합니다.
위와 같은 과정이 Procedure call을 했을 때 수행되는 과정입니다.
또한 Caller-saved Register와 Callee-saved Register라고 Caller와 Callee가 서로 다른 레지스터를 관리하며
Caller-saved Register는 X9 ~ X17이고, Callee-saved Register는 X19 ~ X28 입니다.
Caller-saved Register는 호출자가 필요에 따라 값을 저장하고 복원합니다.
또한 Callee의 입장에서 Caller가 무엇을 필요로 하는지 모르기 때문에 Caller-saved Register를 무조건 백업합니다.
마지막으로 Leaf Procedure과 non-Leaf Procedure에 대해서 정리하고 마치도록 하겠습니다.
Leaf Procedure과 non-Leaf Procedure
처음에 들었던 의문은 모든 프로시저는 내부에 프로시저를 가지고 유무와 상관없이 Caller-Callee규약을 따라야 하는 것인지 궁금했습니다. 하지만 해당 프로시저가 내부에 또 다른 프로시저를 가지고 있지 않다면 Caller-Callee규약을 따를
필요가 없다는 것 입니다. 여기서 Leaf Procedure과 non-Leaf Procedure에 대해서 알 수 있습니다.
Leaf Procedure은 내부에서 다른 프로시저를 호출하지 않는 것이고, non-Leaf Procedure은 내부에서 프로시저를 호출한다는 것 입니다. 그렇다면 위에서 말한 것에 근거하여 Leaf Procedure은 규약을 따를 필요가 없다는 것을 알 것 입니다.
Leaf Procedure은 호출 규약의 일부를 생략하여 최적화할 수 있습니다. 하지만 코드의 일관성과 유지 보수성을 위해
가능하다면 모든 프로시저가 호출 규약을 따르는 것이 권장되기도 합니다.
그렇다면 Leaf Procedure은 어떻게 최적화할 수 있을까요?
Leaf Procedure은 다른 프로시저를 호출하지 않으므로, Callee-saved Register를 저장하거나 복원할 필요가 없습니다.
그렇기 때문에 스택 프레임을 생성하고 해제하는 오버헤드를 줄일 수 있습니다.
또한 지역 변수를 많이 사용하지 않는 경우, 스택 프레임 자체를 생략할 수 있기 때문에
실행 시간을 단축시키고 메모리 사용을 줄일 수 있습니다.
지금까지 프로시저(Procedure)에 대해서 정리하는 시간을 가져봤습니다.
글 읽어주셔서 감사합니다.
· 참고
https://ko.wikipedia.org/wiki/%ED%95%A8%EC%88%98_(%EC%BB%B4%ED%93%A8%ED%84%B0_%EA%B3%BC%ED%95%99)
'전자전기공학 > 컴퓨터구조' 카테고리의 다른 글
[컴퓨터구조][CPU의 기본역할 및 성능] (0) | 2024.09.27 |
---|---|
[컴퓨터구조][Arithmetic/Floating point] (10) | 2024.09.25 |
[컴퓨터구조][Branch, Conditional Operation/ Procedure] (0) | 2024.09.20 |
[컴퓨터구조][R, D, I - format Instruction and bitwise] (2) | 2024.09.19 |
[컴퓨터구조][기본배경 및 Operand] (4) | 2024.09.17 |