본문 바로가기

무언가 만들기 위한 지식/ARM Processor

APCS(ARM Procedure Call Standard)


APCSArm Procedure Call Standard의 약자로 Arm Processor에서 함수를 호출하는 표준이다.
함수상에서 인자를 전달하거나 리턴되는 값 및 동작, 각 Register의 역할을 규칙으로 정해놓았다.
Arm compiler는 이를 준수한다. (역어셈을 해보면 APCS규칙대로로 각 Register가 쓰이는 모습을 확인할 수 있다.)

혹시 ARM Processor 에서 사용하는 37개의 Register Set, Register의 구조에 대해 잘 모른다면 필자의 예전 포스팅을 참고해 보자. 어느정도 이해하고 있어야 아래 표도 이해가 갈 것이다.

ARM Processor Register Set : http://shinlucky.tistory.com/272

아래의 표는
필자가 APCS ARM으로 적절한 표 그림을 찾는 도중(Google Image Search) 발견한
가장 깔끔하고 명확한 설명을 하고 있는 표이다. (특히 오른쪽에 Saving convention정리가 정말 마음에 든다.)
아래 표를 통해서 
APCS 상에서는 각 모드(7개의 Mode와 6개의 Stack이 존재)의 Register가 어떻게 사용되는지 알아보자.
 


※ r10의 APCS Name은 v6이 아니라 v7이다. 잘못나온 것임.

모드별로 공용하는 Register와 개별적인 Register는 후에 생각하자.
(예외처리시에는 확실히 고려해줘야 한다. -함수호출은 생각할 필요 없다.) 

위 표는 함수간에 인자와 리턴값에 대한 각 Register의 역할이다. 기본적으로 주어진 16개의 Register(r0~r15)는 APCS상에서 각각 Naming되어 진다. 그 사용되는 역할은 표상의 Use와 같다.
pc, lr, sp, fp는 지금까지 우리가 자주 본 것들이다. (어셈블러상에서 r15로도 쓸수 있고 pc로도 둘다 사용이 가능하다.)
잘 모른다면 위 링크에서 ARM Processor Register Set 및 SPARC Register 에서 언급한 내용을 참고하자.

a1~a4를 주목할 필요가 있는데, 이는 Argument의 약자이다. 즉 인자로 쓰인다는 것이다.
즉 기본적으로 함수가 인자로 값을 전달할때, Register에서는 a1~a4 4개의 Register를 통해서 전달한다.
 
함수가 호출된다는 의미는 곧 분기를 통해 다른 코드가 실행되는 의미이고, 또한 Stack의 크기가 증가하면서(쌓이면서) 새로운 Stack을 사용한다는 의미이기도 하다.
Stack이 새로 쌓이면서 메모리에 Local 변수등을 사용하는데, 그 과정에서 Register가 이용된다.
(Register는 Processor에 가장 근접한 기억장치로 모든 연산에 사용된다.)
Stack이 위로 올라가면서(낮은주소로 쌓이면서) 이전 Stack(즉 fp와 연결된 Stack, Mode 별로 다른 Stack을 말하는게 아니다.)에서 사용한 Register들을 다시 사용하게 되는데, 여기서 Caller와 Callee둘중에 어느 하나가 사용할 Register를 보존할 의무를 갖는다. (보존을 해야지 함수상 Return하면서 이전 상태를 되돌릴 수 있다. - Register의 값 복원을 말함.)

※ 각 CPU마다 Register가 사용되는 방법이 다르다. 필자도 처음에는 SPARC의 Register사용에 대해 공부했는데, ARM과는 완전 상이하다. 하지만 결국 Register를 돌려가며 사용한다는 의미에서는 모두 공통점이 있다.

a1~a4는 총 32bit int Type을 4개 전달한다는 것을 의미한다. float 8byte를 전달할 경우 a1,a2 a3,a4를 하나의 짝을 이루어 사용하게 된다.
여기서 문제가 되는 것은 만약 인자가 4개 이상일 경우 이다.
이때는 Stack을 이용하여 전달한다. 함수 호출과정에서 Stack에 인자를 전달하고, Callee에서 Stack상에서 값을 가져다가 사용하게 된다.
(Caller 는 함수를 호출한 대상, Callee는 호출된 대상이다. 많이 나오는 개념이므로 확실히 알아두자.)

※ 이때 단순히 함수 호출 개념에서는 간단하지만, Exception 발생으로 Stack간에 인자전달시에는 애매해진다. stack이 각 mode별로 다르기 때문에 주소를 넘겨주던지 Stack자체를 넘겨주던지 선택해야한다. =>이것이 SWI 구현하는데 관건이었다.

함수에는 Return값이 존재한다. APCS상에서 Return 값은 a1~a2를 통해 전달한다.
C언어 함수에서 Return은 하나밖에 전해줄 수 없다. APCS상 Procedure의 Return도 마찬가지다. 
4 byte 자료 한개 아니면 8byte(a1+a2 같이 사용) 하나만 Return이 a1~a2를 통해 가능하다.

이때 인자로 들어온 값 a1~a2가 호출된 함수에서 인자로 실행된후 복귀를 해줄때, 리턴 값을 다시 a1~a2에 써주기 때문에 기존 Register에 있던 a1~a2의 값들의 훼손된다.
그렇기 때문에 함수에 들어가는 인자가 Caller상에서 사용될 경우에는 함수 호출전 미리 Stack에 보존을 해야하고, 호출한 함수가 종료되면 다시 복구를 해주어야만 한다.

APCS상에서 Scratch Pad Register라는 용어가 있다.
이 스크래치 레지스터란 APCS Name 중에서 a1~a4, ip, lr (r0~r3, r12, r14)를 지칭한다.
이 레지스터들은 함수내에서 훼손될 가능성이 있는 레지스터들을 말한다.
그렇기 때문에 함수가 호출될 경우 호출 직전에 Stack에 따로 저장을 해주어야 한다.
a1~a4 Register가 인자로만 쓰이고 다른용도로 사용되지 않는다면, 굳이 보존할 필요는 없다.
자체 명령어를 줄이기 위해 인자는 4개이하로 사용하고, 인자는 인자용도로만 사용하는 것이 효율적인 코딩이 될것이다. 

※ 잠깐 위에서 언급했지만, 함수호출(Function Call)의 경우 Register 보존 및 인자전달은 하나의 Stack을 사용하기 때문에 다른 사항을 고려할 필요는 없지만, 예외처리(Exception)의 경우에는 서로 다른 Stack간에 이동이 발생(ARM은 6개의 개별적인 Stack이 존재한다.-Memory에 위치하는 개념상)하기 때문에 인자전달시 어떻게 전달해줄 것인가를 고려해야 하고, Register는 어떤 시점에서 저장해야할 것인가를 생각해보아야 한다.
(그리고 그에 맞게 어셈블리어를 작성해야 한다.)


위 자료는 Google Image에서 검색한 ARM11관련 자료이다.
필자가 생각하기에도 정말 깔끔하게 정리해놓았다.
(단점은 초보자가 보고 배우기에는 좀 힘들듯하다. 이미 조금 아는 분들이 정리하기에는 정말 좋다.)

※ 출처미상인데 문제가 되면 바로 삭제할 예정입니다.

'무언가 만들기 위한 지식 > ARM Processor' 카테고리의 다른 글

SWI_Exception_Implement(System Call-1)  (0) 2010.04.06
Context Switching & Saving Convention  (2) 2010.04.06
SWI(Software Interrupt)  (1) 2010.04.06
System Call Interface  (2) 2010.03.19
ARM Processor Register Set  (0) 2010.03.19