본문 바로가기

무언가 만들기 위한 지식/SPARC Assembler

Leaf subroutine


Leaf Subroutine 이란 함수이지만 다른 함수를 호출하지 않는 함수를 말한다.

그 특징으로 보면

1. Stack Frame 할당이 없다.
2. 현재의 CRW를 유지한다.
(즉 현재 Register Set 을 그대로 이용한다.)
3. save와 restore가 불필요하다.
(Stack에서 Pop자체를 하지 않는다.)
4. 레지스터 사용의 제한이 있다. 
   (%o0~%o5, %g1, %g2 - save가 실행되지 않으므로 레지스터간 맵핑도 되지 않는다. 즉 매개변수로 %o0~%o5를 그대로 사용하면 된다. - %i Register로 받을 필요가 없다.)
5. 반환주소를 %o7+8이다. 이는 특수하게 retl 이란 명령어를 사용한다.

사용되는 예를 한번 보도록 하자.


[작성환경 : solaris sparc machine  작성툴 : Vi editor   컴파일러 : gcc   테스팅환경 : gdb]


위의 코드는 main과 Leaf Subroutine인 foo함수로 이루어져 있다.
소스의 목적은 1부터 8까지를 foo함수에 인자를 전달해주고 그 모든 수를 더한후 결과값을 %o0에 Return해주는 것이다.

일반 SubRoutine과의 큰 차이점이란,
인자를 전해 받을때 %i0 Register를 사용하지 않고 그냥 %o0를 통해 받는다는 것이다.
보시다시피 stack 및 Reguster Set을 위한 save나 restore는 사용되지 않았다.

그리고 14번 줄을 보면 ret대신에 retl을 사용한 모습을 볼 수 있다.
매개변수가 5개를 초과하는 경우는 sp주소값을 이용하여 접근하였다.

Leaf Subroutine이기 때문에 %fp가 아닌 %sp를 이용하여 주소에 접근하여 가져온 모습을 11, 13줄에서 확인할 수 있다.

이 소스에서 가장 의문이 가는점은 사실 18, 30번째의 줄이다.
이 방법은 Leaf subroutine 뿐만아니라 Subroutine에서도 사용되는 기법이다.
함수를 호출하기 전에 매개변수로 넘겨줄 메모리 공간을 잠깐 늘렸다가, 리턴되었을 때 다시 총 메모리 공간을 원래대로 할당하는 것이다.

의미적으로는 간단히 이해가 가지만 약간 의심해 볼만한 것들이 있다.

첫째, %sp를 이용한 add연산 만으로 메모리 공간이 확보되는 것인가?, 그냥 주소값만 바뀌는 것이 아니었나?
      (add의 특성인가, %sp 레지스터가 증가하면 자동으로 공간이 확보되는 것인가, save로 공간이 할당 되는 것이 아닌가?)

둘째, Stack영역에 공간이 할당된다고 할때 Stack높이를 따졌을 때 상위 부분에 공간이 할당되는 것인가, 하위 부분이 할당되는 것인가? 중간부분이 할당되는 것인가? (save시는 상위부분할당이 맞지만, Stack의 상위 부분에서 92byte 아래 부분이 증가하는 가능성도 있다. 아니면 상위가 복사된후 92byte가 다시 위로 복사될 수도 있다.)

셋째, 만약 영역이 증가되고 92byte가 상위 부분으로 이동한다면 어느시점에서 복사되어 이동되는가? add명령어 바로 실행 다음인가? 아니면 몇개의 명령어를 거친 후인가?


필자는 어떻게 보면 쓸모없는 위의 궁금증을 풀기 위해 어제밤과 오늘 아침 계속 테스팅을 해보았다.
짜증나서 피똥사는줄 알았음(예측한 결과값이 안나와서리 ㅠ.ㅜ;)
그래서 몇가지의 결론을 추론해 내었으나, 아직 확인할 수는 없다.
교수님께 확인받고 월요일 쯤 위에 관한 포스팅을 자세히 할 예정이다.


<Gdb를 이용한 레지스터 값의 확인>

위 Gdb상에서는 Break Point를 2개 찍어놓았다. 하나는 foo함수의 반환직전, 또하나는 main의 반환직전이다.
그리하여 foo함수내에서는 인자가 sp를 이용하여 잘 전달이 되었는지 확인해 보았고, main에서는 1부터 8까지의 합이 잘 계산되어 리턴되었는지 확인하였다.
확인 결과 foo함수 내부의 %o1은 8로 값이 잘 전달되었고
%o0로 리턴된 main에서의 값은 36으로 옳은 값을 전달하였음을 알 수 있다.