3.9) 스택 포인터 정리하기
다음은 10과 20을 출력하는 단순한 코드다.
SpArrange.c |
#include "CIL.h"
PROC(main)
PUSH(10) INVOKE(print_int)
PUSH(20) INVOKE(print_int)
ENDP |
개행 명령이 없으므로 결과가 1020으로 나온다는 것만 빼면 설명할 것이 없는 단순한 코드다. 그런데 이 코드의 흐름을 한 번 살펴보자. 먼저 main 프로시저가 호출된 직후의 상황이다.
10을 푸시 한다.
print_int 프로시저를 호출하면 이렇게 될 것이고,
print_int 프로시저가 끝나면 이런 상태일 것이다.
그런데 여기서 20을 푸시하면 이런 상태가 된다.
여기서 print_int 프로시저를 다시 호출하면 이렇게 되고,
프로시저가 끝나면 이런 상태가 된다.
결과적으로 함수 호출이 끝날 때마다 인자로 넘겼던 메모리가, 프로시저가 종료하기 전까지 계속 놀고 있는 상태가 된다. 고작 4바이트라고 생각할 수도 있으나, 만약 크기가 큰 구조체를 통째로 인자로 넘기는 경우에는 이는 분명한 문제가 된다. 따라서 이는 해결해야 하는 문제인데 어떻게 해야 할까?
답은 매우 단순한데, 넘겼던 인자의 크기만큼 스택에서 메모리를 확보했으니, 넘겼던 인자의 크기만큼을 다시 스택 메모리에서 빼내면 된다. 즉 다음과 같이 해결하면 된다.
SpSolution.c |
#include "CIL.h"
PROC(main)
PUSH(10) INVOKE(print_int) ADD(sp, 4) // 넘긴 인자의 크기만큼 스택 포인터에 더한다
PUSH(20) INVOKE(print_int) ADD(sp, 4) // 넘긴 인자의 크기만큼 스택 포인터에 더한다
ENDP |
이 해법이 옳은지를 점검해보자. main 프로시저가 호출되면 다음 상태에 있게 된다.
print_int 함수가 종료될 때까지는 진행 상황이 같다. 그림으로는 다음 상태와 같다.
여기서 ADD 명령으로 sp를 조정하면 다음과 같은 일이 일어난다.
즉 main 프로시저를 처음으로 호출한 순간과 상황이 완전히 같아졌다. 이후에 진행되는 상황은 인자로 20이 넘어갔다는 사실을 제외하면 달라지지 않는다.
이와 같이 프로시저에 넘긴 인자의 크기만큼을 프로시저 종료 시에 스택 포인터에 더하는 해법이 적절함을 알 수 있었다.