x64 소프트웨어 규칙

이 섹션에서는 x86 아키텍처에 대한 64비트 확장인 x64의 C++ 호출 규칙 방법론에 대해 설명합니다.

x64 호출 규칙 개요

x86과 x64 간의 중요한 두 가지 차이점은 64비트 주소 지정 기능과 일반 사용을 위한 16개 64비트 레지스터의 플랫 집합입니다. 확장된 레지스터 집합이 지정된 경우 x64는 __fastcall 호출 규칙 및 RISC 기반 예외 처리 모델을 사용합니다. __fastcall 규칙은 처음 4개 인수에 레지스터를 사용하고 스택 프레임을 사용하여 추가 인수를 전달합니다. 레지스터 사용, 스택 매개 변수, 반환 값, 스택 해제 등 x64 호출 규칙에 대한 자세한 내용은 x64 호출 규칙을 참조하세요.

x64에 최적화 사용

다음 컴파일러 옵션은 x64용 애플리케이션을 최적화하는 데 도움이 됩니다.

형식 및 스토리지

이 섹션에서는 x64 아키텍처용 데이터 형식의 열거형 및 스토리지에 대해 설명합니다.

스칼라 형식

모든 맞춤을 사용하여 데이터에 액세스할 수 있지만, 성능 손실을 방지하기 위해 해당 자연 경계 또는 일부 배수에서 데이터를 맞추는 것이 좋습니다. 열거형은 상수 정수이며 32비트 정수로 처리됩니다. 다음 표에서는 다음 맞춤 값을 사용하여 맞춤과 관련된 데이터의 형식 정의 및 권장 스토리지에 대해 설명합니다.

  • 바이트 - 8비트

  • 단어 -16비트

  • 2배 단어 - 32비트

  • 4배 단어 - 64비트

  • 8배 단어 - 128비트

스칼라 형식 C 데이터 형식 스토리지 크기(바이트) 권장 맞춤
INT8 char 1 Byte
UINT8 unsigned char 1 Byte
INT16 short 2 단어
UINT16 unsigned short 2 단어
INT32 int, long 4 2배 단어
UINT32 unsigned int, unsigned long 4 2배 단어
INT64 __int64 8 4배 단어
UINT64 unsigned __int64 8 4배 단어
FP32 (단정밀도) float 4 2배 단어
FP64 (배정밀도) double 8 4배 단어
POINTER * 8 4배 단어
__m64 struct __m64 8 4배 단어
__m128 struct __m128 16 8배 단어

집계 및 공용 구조체

배열, 구조체, 공용 구조체와 같은 다른 형식에는 일관성 있는 집계 및 공용 구조체 스토리지와 데이터 검색을 보장하는 보다 엄격한 맞춤 요구 사항이 있습니다. 배열, 구조체 및 공용 구조체에 대한 정의는 다음과 같습니다.

  • 배열

    인접한 데이터 개체의 정렬된 그룹을 포함합니다. 각 개체를 요소라고 합니다. 배열 내의 모든 요소는 크기 및 데이터 형식이 동일합니다.

  • 구조체

    데이터 개체의 정렬된 그룹을 포함합니다. 배열의 요소와 달리 구조체 내의 데이터 개체는 서로 다른 데이터 형식 및 크기를 가질 수 있습니다. 구조체의 각 데이터 개체를 멤버라고 합니다.

  • Union

    명명된 멤버 집합 중 하나를 보유하는 개체입니다. 명명된 집합의 멤버는 임의의 형식일 수 있습니다. 공용 구조체에 할당된 스토리지는 해당 공용 구조체의 가장 큰 멤버에 필요한 스토리지와 맞춤에 필요한 패딩을 합한 것과 같습니다.

다음 표에서는 공용 구조체 및 구조체의 스칼라 멤버에 권장되는 맞춤을 보여 줍니다.

스칼라 형식 C 데이터 형식 필요한 맞춤
INT8 char Byte
UINT8 unsigned char Byte
INT16 short Word
UINT16 unsigned short Word
INT32 int, long 2배 단어
UINT32 unsigned int, unsigned long 2배 단어
INT64 __int64 4배 단어
UINT64 unsigned __int64 4배 단어
FP32 (단정밀도) float 2배 단어
FP64 (배정밀도) double 4배 단어
POINTER * 4배 단어
__m64 struct __m64 4배 단어
__m128 struct __m128 8배 단어

다음 집계 맞춤 규칙이 적용됩니다.

  • 배열의 맞춤은 배열 요소 중 하나의 맞춤과 동일합니다.

  • 구조체 또는 공용 구조체의 시작 부분의 맞춤은 개별 멤버의 최대 맞춤입니다. 구조체 또는 공용 구조체 내의 각 멤버는 이전 표에 정의된 적절한 맞춤에 배치해야 합니다. 이전 멤버에 따라 암시적 내부 패딩이 필요할 수 있습니다.

  • 구조체 크기는 해당 맞춤의 정수배여야 합니다. 마지막 멤버 뒤에 패딩이 필요할 수 있습니다. 구조체와 공용 구조체를 배열로 그룹화할 수 있으므로 구조체 또는 공용 구조체의 각 배열 요소는 이전에 결정된 적절한 맞춤에서 시작하고 끝나야 합니다.

  • 이전 규칙이 유지되는 한 맞춤 요구 사항보다 더 크게 데이터를 맞추는 것이 가능합니다.

  • 개별 컴파일러는 크기 때문에 구조체의 압축을 조정할 수 있습니다. 예를 들어 /Zp(구조체 멤버 맞춤)를 사용하여 구조체의 압축을 조정할 수 있습니다.

구조체 맞춤 예제

다음 네 가지 예제는 각각 구조체 또는 공용 구조체 맞춤을 선언하고 해당 그림은 메모리에서 해당 구조체 또는 공용 구조체의 레이아웃을 보여 줍니다. 그림의 각 열은 메모리 바이트를 나타내며 열의 숫자는 해당 바이트의 변위를 나타냅니다. 각 그림의 두 번째 행에 있는 이름은 선언의 변수 이름에 해당합니다. 음영 처리된 열은 지정된 맞춤을 구현하는 데 필요한 패딩을 표시합니다.

예제 1

// Total size = 2 bytes, alignment = 2 bytes (word).

_declspec(align(2)) struct {
    short a;      // +0; size = 2 bytes
}

AMD 변환 예제 1 구조체 레이아웃

예제 2

// Total size = 24 bytes, alignment = 8 bytes (quadword).

_declspec(align(8)) struct {
    int a;       // +0; size = 4 bytes
    double b;    // +8; size = 8 bytes
    short c;     // +16; size = 2 bytes
}

AMD 변환 예제 2 구조체 레이아웃

예제 3

// Total size = 12 bytes, alignment = 4 bytes (doubleword).

_declspec(align(4)) struct {
    char a;       // +0; size = 1 byte
    short b;      // +2; size = 2 bytes
    char c;       // +4; size = 1 byte
    int d;        // +8; size = 4 bytes
}

AMD 변환 예제 3 구조체 레이아웃

예제 4

// Total size = 8 bytes, alignment = 8 bytes (quadword).

_declspec(align(8)) union {
    char *p;      // +0; size = 8 bytes
    short s;      // +0; size = 2 bytes
    long l;       // +0; size = 4 bytes
}

AMD 변환 예제 4 공용 구조체 레이아웃

비트 필드

구조체 비트 필드는 64비트로 제한되며, signed int, unsigned int, int64 또는 unsigned int64 형식일 수 있습니다. 형식 경계를 넘는 비트 필드는 비트 필드를 다음 형식 맞춤에 맞추는 비트를 건너뜁니다. 예를 들어 정수 비트 필드는 32비트 경계를 넘지 않을 수 있습니다.

x86 컴파일러와 충돌

x86 컴파일러를 사용하여 애플리케이션을 컴파일할 때 4바이트보다 큰 데이터 형식은 스택에서 자동으로 맞추지 않습니다. x86 컴파일러의 아키텍처는 4바이트 맞춤 스택이기 때문에 4바이트보다 큰 값(예: 64비트 정수)을 자동으로 8바이트 주소에 맞출 수 없습니다.

맞추지 않은 데이터로 작업하는 경우 두 가지 의미가 내포됩니다.

  • 맞춘 위치에 액세스하는 것보다 맞추지 않은 위치에 액세스하는 데 시간이 더 오래 걸릴 수 있습니다.

  • 맞추지 않은 위치는 연동 작업에서 사용할 수 없습니다.

보다 엄격한 맞춤이 필요한 경우 변수 선언에 __declspec(align(N))을 사용합니다. 이렇게 하면 컴파일러가 스택을 동적으로 맞춰 사양을 충족합니다. 그러나 런타임에 스택을 동적으로 조정하면 애플리케이션 실행 속도가 느려질 수 있습니다.

레지스터 사용

x64 아키텍처는 범용 레지스터 16개(이하 '정수 레지스터')와 부동 소수점용으로 사용 가능한 XMM/YMM 레지스터 16개를 제공합니다. 휘발성 레지스터는 호출자가 호출 중에 제거되는 것으로 가정하는 스크래치 레지스터입니다. 함수 호출 중에 값을 유지하려면 비휘발성 레지스터가 필요합니다. 호출 수신자는 비휘발성 레지스터(사용하는 경우)를 저장해야 합니다.

레지스터 변동성 및 보존

다음 테이블에서는 함수 호출에서 각 레지스터가 사용되는 방법을 설명합니다.

레지스터 Status 기능
RAX 휘발성 반환 값 레지스터
RCX 휘발성 첫 번째 정수 인수
RDX 휘발성 두 번째 정수 인수
R8 휘발성 세 번째 정수 인수
R9 휘발성 네 번째 정수 인수
R10:R11 휘발성 호출자가 필요에 따라 보존해야 하며 syscall/sysret 명령에 사용됨
R12:R15 비휘발성 호출 수신자가 보존해야 함
RDI 비휘발성 호출 수신자가 보존해야 함
RSI 비휘발성 호출 수신자가 보존해야 함
RBX 비휘발성 호출 수신자가 보존해야 함
RBP 비휘발성 프레임 포인터로 사용 가능(호출 수신자가 보존해야 함)
RSP 비휘발성 스택 포인터
XMM0, YMM0 휘발성 첫 번째 FP 인수( __vectorcall 사용 시에는 첫 번째 벡터 형식 인수)
XMM1, YMM1 휘발성 두 번째 FP 인수( __vectorcall 사용 시에는 두 번째 벡터 형식 인수)
XMM2, YMM2 휘발성 세 번째 FP 인수( __vectorcall 사용 시에는 세 번째 벡터 형식 인수)
XMM3, YMM3 휘발성 네 번째 FP 인수( __vectorcall 사용 시에는 네 번째 벡터 형식 인수)
XMM4, YMM4 휘발성 호출자가 필요에 따라 보존해야 함( __vectorcall 사용 시에는 다섯 번째 벡터 형식 인수)
XMM5, YMM5 휘발성 호출자가 필요에 따라 보존해야 함( __vectorcall 사용 시에는 여섯 번째 벡터 형식 인수)
XMM6:XMM15, YMM6:YMM15 비휘발성(XMM), 휘발성(YMM의 위쪽 절반) 호출 수신자가 보존해야 함. YMM 레지스터는 호출자가 필요에 따라 보존해야 함

함수 종료 시 및 C 런타임 라이브러리 호출 및 Windows 시스템 호출에 대한 함수 진입에서 CPU 플래그 레지스터의 방향 플래그는 삭제되는 것으로 예상됩니다.

스택 사용

x64의 스택 할당, 맞춤, 함수 형식 및 스택 프레임에 대한 자세한 내용은 x64 스택 사용을 참조하세요.

프롤로그 및 에필로그

스택 공간을 할당하거나, 다른 함수를 호출하거나, 비휘발성 레지스터를 저장하거나, 예외 처리를 사용하는 모든 함수는 해당 함수 테이블 항목에 연결된 해제 데이터에 주소 제한이 설명된 프롤로그 그리고 각 함수가 종료할 때마다 에필로그가 있어야 합니다. x64에서 필요한 프롤로그 및 에필로그 코드에 대한 자세한 내용은 x64 프롤로그 및 에필로그를 참조하세요.

x64 예외 처리

x64에서 구조적 예외 처리 및 C++ 예외 처리 동작을 구현하는 데 사용되는 규칙 및 데이터 구조에 대한 자세한 내용은 x64 예외 처리를 참조하세요.

내장 함수 및 인라인 어셈블리

x64 컴파일러에 대한 제약 조건 중 하나는 인라인 어셈블러를 지원하지 않는 것입니다. 즉, C 또는 C++로 작성할 수 없는 함수를 컴파일러에서 지원하는 서브루틴 또는 내장 함수로 작성해야 합니다. 일부 함수는 성능이 중요하지만 다른 함수는 그렇지 않습니다. 성능에 민감한 함수는 내장 함수로 구현해야 합니다.

컴파일러에서 지원하는 내장 함수는 컴파일러 내장 함수에 설명되어 있습니다.

이미지 형식

x64 실행 가능 이미지 형식은 PE32+입니다. 실행 가능 이미지(DLL 및 EXE)는 최대 2기가바이트로 제한되므로 32비트 치환을 사용한 상대 주소 지정을 사용하여 정적 이미지 데이터를 처리할 수 있습니다. 이 데이터에는 가져오기 주소 테이블, 문자열 상수, 정적 전역 데이터 등이 포함됩니다.

참조

호출 규칙