프로그래밍 공부
작성일
2023. 4. 20. 02:05
작성자
WDmil
728x90

함수 호출 규약 이란?

함수가 호출될 때 매개변수와 반환값을 어떻게 전달하는지 정의하는 규칙이다.

함수 호출 규약은 컴퓨터 아키텍처, 운영체제, 컴파일러와 같은 요소에 따라 달라진다.

 

그러나, 대부분의 호출규약은 함수 호출 프로토콜을 다음과 같이 규정한다.

  1. 매개 변수 전달 방식
    • 매개변수는 레지스터, 스텍, 메모리 또는 조합으로 전달된다.
  2. 매개변수 전달 순서
    • 매개 변수는 왼쪽에서 오른쪽으로, 또는 오른쪽에서 왼쪽으로 전달된다.
  3. 반환값 처리
    • 반환값은 레지스터, 스텍, 메모리 또는 조합으로 처리된다.
  4. 스택 처리
    • 함수가 호출되고 반환될 때 스택 프레임이 생성되고 해제된다.

 

대표적인 함수 호출 규약으로는, C언어에서 사용되는 cdecl, stdcall, fastcall, this call, vectorcall, Nacked call이 있고,

Microsoft의 x64 calling convention이 있다.


  • cdecl
    • cdecl은 "C declaration" 의 약자로, C언어의 기본 호출 규약이다. 이 규약은 함수 호출 시 매개변수를 스택에 푸시하고, 호출한 함수가 반환한 값도 스택에 푸시한다. 이러한 방식으로 매개변수와 반환값을 전달하기에, 스택공간이 상대적으로 많이 필요하다. 매개변수가 적은 함수에서 자주 사용된다.
    • cdecl의 특징은 다음과 같다.
      1. 인자 전달 순서 : 오른쪽에서 왼쪽으로 전달된다.
      2. 인자 전달 방법 : 스택에 푸시된다.
      3. 스택 프레임 정리 : 호출한 함수가 정리한다.
      4. 가변인자 사용여부 : 사용가능.

  • cdecl 호출규약의 단점.
    1. 매개변수의 개수가 많아질수록 스택의 사용이 늘어나게 되어 호출 속도가 느려질 수 있다.
    2. 호출 스택의 제거를 호출자가 해야함으로 함수 호출 시 오버헤드가 발생할 수 있다.

즉, 기본적인 함수 호출시 실행되는 함수 호출 규약은 cdecl이 사용된다.


  •  stdcall
    • stdcall은 WinAPI와 같은 WIndows 플랫폼에서 주로 사용되는 함수 호출 규약으로, cdecl과 마찬가지로 매개변수와 반환값을 스택에 푸시하고, 호출한 함수가 스택을 제거하는 것 이 아니라, 호출한 함수가 만든 스택 프레임을 호출한 함수에서 제거하게 된다. Windows API와 같은 매개변수가 많은 함수에서 사용된다.
    • stdcall의 특징은 다음과 같다.
      1. 인자 전달 순서 : 오른쪽에서 왼쪽으로 전달된다.
      2. 인자 전달 방법 : 스택에 푸시된다.
      3. 스택 프레임 정리 : 호출한 함수가 정리한다.
      4. 가변인자 사용여부 : 사용가능.

  • stdcall 호출규약의 단점.
    1. 매개변수의 개수가 많아질수록 스택의 사용이 늘어나게 되어 호출 속도가 느려질 수 있다.
    2. 호출 스택의 제거를 호출자가 해야함으로 함수 호출 시 오버헤드가 발생할 수 있다.

호출규약의 단점은 같으나, cdecl과 stdcall의 차이점이 존재한다.

 

cdecl은, 함수 호출 시 매개변수를 스택에 저장하고, 호출이 끝난 후 스택에서 매개변수를 제거한다. 이러한 방식은 매개변수의 개수가 많아질수록, 스택의 사용이 늘어나게되어 호출속도가 느려질 수 있다.

 

stdcall은, 함수 호출 시 매개변수를 스택에 저장하고, 호출이 끝난 후 스택에서 매개변수를 제거한다. 그럼으로, stdcall호출규약은 스텍의 제거를 호출 대상이 해야함으로 호출자는 호출대상에서 반환된 값을 쉽게 얻을 수 있다. 그러나, 호출 대상이 해야하기 때문에 호출대상에 대한 오버헤드가 발생할 수 있다.


  • fastcall
    • fastcall은 매개변수를 레지스터에 저장하고 많은 경우에 함수 호출시 매우 빠른 실행속도를 보장한다. 매개변수의 개수가 많을 경우에는 스택을 이용하여 전달하게 된다.
    • fastcall의 특징은 다음과 같다.
      1. 인자 전달 순서 : 오른쪽에서 왼쪽으로 전달된다.
      2. 인자 전달 방법 : 레지스터에 저장된다. 레지스터가 모자라면 스택에 저장된다.
      3. 스택 프레임 정리 : 호출한 함수가 정리한다.
      4. 가변인자 사용여부 : 사용가능.

  • fastcall 호출규약의 단점
    1. 매개변수가 많으면 레지스터가 부적해져 스택을 이용하여 매개변수를 전달해야 함으로 호출속도가 떨어질 수 있다.
    2. 컴파일러에 따라 지원되지 않을 수 있다.
    3. 레지스터의 사용이 많아져서 함수의 크기가 증가할수 있음으로 코드의 크기가 커져, 캐시효과가 떨어지기 때문에 성능이 감소할 수 도 있다.

  • thiscall
    • thiscall 호출규약은 맴버 함수에서 사용되는 호출규약이다. 이 호출규약은 함수의 첫 번쨰 매개변수로 this 포인터를 전달하는 것이 특징이다. 이를 통해 맴버함수가 속한 객체의 주소를 전달하고 맴버 변수에 접근할 수 있다.
    • thiscall의 특징은 다음과 같다.
      1. 인자 전달 순서 : 오른쪽에서 왼쪽으로 전달된다.
      2. 인자 전달 방법 : 첫번째 매개변수로 'this'포인터를 전달하고, 나머지 매개변수는 호출 스택에 쌓는다.
      3. 스택 프레임 정리 : 호출한 함수가 정리한다.
      4. 가변인자 사용여부 : 사용불가능.

자기자신을 재참조하는 역활을 함으로 구조체 함수 선언시 자동으로 사용된다.

  • thiscall 호출규약의 단점
    1. 인라인 함수에서의 오버헤드가 발생할 수 있다. 멤버함수가 인라인 함수로 작성될 경우 'this'포인터 전달 및 호출 스택 관련 작업 때문에 오버헤드가 발생한다. [인라인 함수는 코드가 함수 호출문에 삽입되는 방식을 이야기 한다]
    2. 함수포인터 사용에 제한이 있다. 일반적인 함수 포인터와 호환되지 않아, 호출대상 함수가 __thiscall 호출규약을 사용하는 경우 해당 함수를 호출하는 함수포인터 도 __thiscall 호출 규약을 사용해야 한다.
    3. 표준이 아닌 호출규약 이기 때문에, C++표준이 아님으로 특정 컴파일러에서만 지원된다(Microsoft Visual C++ 컴파일러 에서만 지원) 그럼으로 이식성이 떨어질 수 있다.

  • vectorcall
    • vectorcall 호출규약은 Microsoft Visual C++ 2013부터 도입된 비표준(non-standard)호출규약 중 하나이다. 이 호출규약은 SSE(SIMD) 명령어를 이용한 고성능 연산을 위해 만들어 졌으며, 벡터 자료형의 인자 전달 및 반환을 위한 호출규약 이다.
    • vectorcall 의 특징은 다음과 같다.
      1. 인자 전달 순서 : 왼쪽에서 오른쪽으로 전달된다.
      2. 인자 전달 방법 : 레지스터에 저장된다. 첫번째부터 세번째까지는 XMM레지스터에, 네번쨰부터 여덟번째 까지는 일반 레지스터에 저장된다.
      3. 스택 프레임 정리 : 호출한 함수가 정리한다.
      4. 가변인자 사용여부 : 사용불가능.

대부분의 인자값을 레지스터로 사용함으로 빠르다.

  • vectorcall 호출규약의 단점
    1. 비표준 호출규약이기 때문에 이식성이 떨어질 수 있다.
    2. 스택 프레임을 정리하는 부담이 호출자에게 들어가기 때문에, 함수 내에서 동적 메모리 할당을 수행하는경우 할당한 메모리를 호출자(예를들어 int main()같은 ) 가 스택 프레임을 정리하기 때문에 호출자가 동적 메모리를 반환하기전 에는 해당 메모리를 해제할 수 없다. 그럼으로 메모리누수가 발생할 가능성이 있으며, 이때문에 프로그래머는 메모리 해제 작업을 함수 외부에서 수행해야만 한다.

  • Nacked Call
    • Nacked Call 호출규약은 함수 진입점에서 컴파일러가 자동으로 함수 프롤로그와 에필로그 코드를 생성하지 않는 호출규약이다. 이 규약을 사용하면 호출된 함수가 진입점에서 스택 프레임을 생성하거나 삭제하지 않으며, 이에대한 방법은 전부 개발자가 작업해야 한다.
    • Nacked Call 의 특징은 다음과 같다. 
      1. 인자 전달 순서 : 개발자가 직접 구현.
      2. 인자 전달 방법 : 개발자가 직접 구현.
      3. 스택 프레임 정리 : 개발자가 직접 구현.
      4. 가변인자 사용여부 : 사용가능하나, 해당 함수에서 스택 프레임을 제거하지 않도록 만들었다면 호출자가 스택 프레임을 제거해야 한다.

모든걸 프로그래머가 직접 지정해서 설정해주어야 한다.

  • Nacked Call 호출규약의 단점
    1. 컴파일러가 인자전달순서, 방법, 정리, 가변인자 사용여부 등을 생성하지 않기 떄문에 개발자가 스택 프레임과 관련된 모든 작업을 직접 구현해야 한다. 이에따라 코드가 복잡해질 수 있다.
    2. 스택 프레임과 관련된 작업이 제대로 구현되지 않으면 큰 문제가 발생할 수 있다.
    3. 이 호출규약은 Microsoft Visual C++ 에서만 지원됨으로 이식성이 떨어질 수 있다.

  • Microsoft x64 calling convention
    • Microsoft x64 calling convention은 64비트 WIndows에서 사용되는 함수 호출 규약으로, cdecl과 유사하게 매개변수와 반환된 값을 스택에 저장한다. 그러나, 레지스터를 더 많이 사용하여 매개 변수 전달 및 반환 값을 최적화 한다.
      1. 인자 전달 순서 : 정수형의 경우 RCX, RDX, R8, R9 레지스터에 저장됨, 실수의 경우 XMM0, XMM1, XMM2, XMM3 레지스터를 사용하게 된다.
      2. 인자 전달 방법 : 레지스터에 저장된다. 레지스터가 모자라면 스택에 저장된다.
      3. 스택 프레임 정리 : 호출한 함수가 정리한다.
      4. 가변인자 사용여부 : 사용가능.

Microsoft x64 calling convention 은 cdecl 과 마찬가지로 따로 지정하지 않아도, 64비트 환경에서는 컴파일러가 플랫폼과 아키텍처에 따라 선택하게 됨으로. 호출규약을 지정해주지 않아도 64비트 환경에서는 사용된다.

 

  • Microsoft x64 calling convention 호출규약의 단점
    1. 매개변수 전달방식에 따른 제약이 존재한다. 처음 4개의 정수형 매개변수 까지는 레지스터에 저장하여 전달하고 5번째 매개변수 부터는 스택을 사용하여 전달하게 된다. 이때문에 매개변수의 개수가 많을 경우 스택을 사용하기 때문에 느려질 수 있다.
    2. 반환값의 제약이 존재한다. 해당 호출규약은 반환값을 레지스터에 저장하는 방식을 사용하는데, 그 반환값이 레지스터에 저장할 수 있는 크기보다 큰 경우 스택을 이용하게 된다. 이 경우 속도의 저하가 발생할 수 있다.
    3. 함수 호출 시 레지스터 사용이 많아지게 되면, 다른 함수 호출 시 레지섵의 값을 저장하고 복원하는 작업이 필요해짐으로 호출 속도가 느려질 수 있다.
    4. Microsoft x64 calling convention은, 64비트 시스템에서만 사용이 가능하다! 이러한 제한 때문에 32비트에서는 다른 호출규약을 사용해야 한다.

 

728x90