x64位软件调用约定II

x64 calling convention [节选]

This section describes the standard processes and conventions that one function (the caller) uses to make calls into another function (the callee) in x64 code.

本节描述x64位代码中一个函数(调用者)调用另一个函数的标准流程和约定

Calling convention defaults

The x64 Application Binary Interface (ABI) uses a four-register fast-call calling convention by default. Space is allocated on the call stack as a shadow store for callees to save those registers. There's a strict one-to-one correspondence between the arguments to a function call and the registers used for those arguments. Any argument that doesn’t fit in 8 bytes, or isn't 1, 2, 4, or 8 bytes, must be passed by reference. A single argument is never spread across multiple registers. The x87 register stack is unused, and may be used by the callee, but must be considered volatile across function calls. All floating point operations are done using the 16 XMM registers. Integer arguments are passed in registers RCX, RDX, R8, and R9. Floating point arguments are passed in XMM0L, XMM1L, XMM2L, and XMM3L. 16-byte arguments are passed by reference. Parameter passing is described in detail in Parameter Passing. In addition to these registers, RAX, R10, R11, XMM4, and XMM5 are considered volatile. All other registers are non-volatile. Register usage is documented in detail in Register Usage and Caller/Callee Saved Registers.

x64应用程序二进制接口(ABI ABI的具体定义参考程序员的自我修养)默认使用4个寄存器的快速调用约定。(编译器)为被调用者在堆栈上分配空间用以存储这些寄存器(快速调用的寄存器)。这些函数参数和用于传参的寄存器之间有着严格的一一对应的关系。任何参数的大小如果不满足8字节,或者不是1,2,4,8字节,必须以引用方式传递。(这里分2种情况讨论:1.参数大小在8B以下的类型,多数属于基本类型,而基本类型无外乎1B、2B、4B、8B;如果是8B以下的结构体,又会被编译器对齐到8B;2.而参数大小在8B以上的类型,即使在编码阶段是按值传递,但实际上编译器仍然会隐式的生成一个临时变量,临时变量以引用传递的方式传给被调用者)单个参数不会跨越多个寄存器。x87寄存器栈不再使用(可能是指fld/fsd这类指令),但有可能由被调用函数使用,所以要考虑交叉调用的情况。所有浮点数操作都使用16个XMM寄存器。整型参数使用RCX,RDX,R8,R9寄存器传递。浮点数参数通过XMM0L,XMM1L,XMM2L,XMM3L寄存器传递。16字节的参数以引用方式传递。参数传递细节参考Parameter Passing. 除了这些寄存器,RAX,R10,R11,XMM4,XMM5是易失型寄存器。其他所有寄存器是非易失型寄存器。寄存器使用方法已归档于Register Usage and Caller/Callee Saved Registers.

Alignment

对齐

Most structures are aligned to their natural alignment. The primary exceptions are the stack pointer and mallocor alloca memory, which are aligned to 16 bytes in order to aid performance. Alignment above 16 bytes must be done manually, but since 16 bytes is a common alignment size for XMM operations, this value should work for most code. For more information about structure layout and alignment, see Types and Storage. For information about the stack layout, see x64 stack usage.

绝大多数结构体是自然对齐(8B对齐)的。主要的例外是栈指针和malloc或者alloca分配的内存,为了提高效率,他们被对齐到16B边界。大于16B的对齐边界必须手动设置,但是,由于16B对齐对于XMM操作而言很常见,因此这个(对齐)值对绝大多数代码而言是奏效的。更多关于结构体布局和对齐相关的信息,查看Types and Storage。堆栈布局信息,查看x64 stack usage.

Parameter passing

传参

The first four integer arguments are passed in registers. Integer values are passed in left-to-right order in RCX, RDX, R8, and R9, respectively. Arguments five and higher are passed on the stack. All arguments are right-justified in registers, so the callee can ignore the upper bits of the register and access only the portion of the register necessary.

前四个整型参数通过寄存器传递。整型值从左往右以此通过RCX,RDX,R8,R9传递。更多的参数通过栈传递。所有参数在七寸器中是向右对齐的(高位向左扩展?),所以被调用方可以忽略寄存器的高位,而只访问必要的部分。

Any floating-point and double-precision arguments in the first four parameters are passed in XMM0 - XMM3, depending on position. The integer registers RCX, RDX, R8, and R9 that would normally be used for those positions are ignored, except in the case of varargs arguments. For details, see Varargs. Similarly, the XMM0 - XMM3 registers are ignored when the corresponding argument is an integer or pointer type.

前4个参数中的任何浮点数和双精度浮点数的通过XMM0-XMM3传递。通常用于这些位置的整型寄存器RCX,RDX,R8,R9会被忽略,除非用于可变参数。参见Varargs。类似的当参数类型是整型或者指针型时,这些位置上的XMM0-XMM3寄存器会被忽略。

__m128 types, arrays, and strings are never passed by immediate value. Instead, a pointer is passed to memory allocated by the caller. Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed as if they were integers of the same size. Structs or unions of other sizes are passed as a pointer to memory allocated by the caller. For these aggregate types passed as a pointer, including __m128, the caller-allocated temporary memory must be 16-byte aligned.

__m128类型,数组已经字符串永远不会以立即数的方式传递。取而代之,会传递指向这片内存区域的指针。大小为8,16,32,或者64位以及__m64位结构体/联合体,被当做整型值传递。而其他大小的(由于结构体对齐,其他大小是指>64bit)结构体或者联合体被调用者用指针传递。对于这些以指针传递聚合体,包含__m128,调用者临时分配的内存必须16字节对齐(这段其实在上面的对齐中有提及)

Intrinsic functions that don't allocate stack space, and don't call other functions, sometimes use other volatile registers to pass additional register arguments. This optimization is made possible by the tight binding between the compiler and the intrinsic function implementation.

The callee is responsible for dumping the register parameters into their shadow space if needed.

如果有必要,被调用函数负责转储(保存)寄存器参数到他们的临时空间中

The following table summarizes how parameters are passed:

下表总结了参数如何被传递:

Parameter type How passed
Floating point First 4 parameters - XMM0 through XMM3. Others passed on stack.
Integer First 4 parameters - RCX, RDX, R8, R9. Others passed on stack.
Aggregates (8, 16, 32, or 64 bits) and __m64 First 4 parameters - RCX, RDX, R8, R9. Others passed on stack.
Aggregates (other) By pointer. First 4 parameters passed as pointers in RCX, RDX, R8, and R9
__m128 By pointer. First 4 parameters passed as pointers in RCX, RDX, R8, and R9

Example of argument passing 1 - all integers

func1(int a, int b, int c, int d, int e);
// a in RCX, b in RDX, c in R8, d in R9, e pushed on stack

Example of argument passing 2 - all floats

func2(float a, double b, float c, double d, float e);
// a in XMM0, b in XMM1, c in XMM2, d in XMM3, e pushed on stack

Example of argument passing 3 - mixed ints and floats

func3(int a, double b, int c, float d);
// a in RCX, b in XMM1, c in R8, d in XMM3

Example of argument passing 4 -__m64, __m128, and aggregates

func4(__m64 a, _m128 b, struct c, float d);
// a in RCX, ptr to b in RDX, ptr to c in R8, d in XMM3

Return values

返回值

A scalar return value that can fit into 64 bits is returned through RAX; this includes __m64 types. Non-scalar types including floats, doubles, and vector types such as __m128__m128i__m128d are returned in XMM0. The state of unused bits in the value returned in RAX or XMM0 is undefined.

小于64bit的标量值可以通过RAX返回;非标量值包括float,double,和向量等通过XMM0返回。通过RAX或XMM0返回的未使用的位(高位)的值没有明确定义。

User-defined types can be returned by value from global functions and static member functions. To return a user-defined type by value in RAX, it must have a length of 1, 2, 4, 8, 16, 32, or 64 bits. It must also have no user-defined constructor, destructor, or copy assignment operator; no private or protected non-static data members; no non-static data members of reference type; no base classes; no virtual functions; and no data members that do not also meet these requirements. (This is essentially the definition of a C++03 POD type. Because the definition has changed in the C++11 standard, we don't recommend using std::is_pod for this test.) Otherwise, the caller assumes the responsibility of allocating memory and passing a pointer for the return value as the first argument. Subsequent arguments are then shifted one argument to the right. The same pointer must be returned by the callee in RAX.

自定义类型可以通过全局函数或静态成员函数返回值。通过RAX返回自定义类型,其长度必须为1,2,4,8,16,32,64位。同时,该自定义类型中不存在自定义构造/析构/拷贝构造函数,不存在非静态私有/保护成员变量,不存在非静态引用类型;不存在基类,虚函数。否则,调用者将负责分配内存并将返回值的指针作为第一个参数传递。随后的参数会向右移动一个参数。RAX中的被调用者必须返回相同的指针。

These examples show how parameters and return values are passed for functions with the specified declarations:

Example of return value 1 - 64-bit result

__int64 func1(int a, float b, int c, int d, int e);
// Caller passes a in RCX, b in XMM1, c in R8, d in R9, e pushed on stack,
// callee returns __int64 result in RAX.

Example of return value 2 - 128-bit result

__m128 func2(float a, double b, int c, __m64 d);
// Caller passes a in XMM0, b in XMM1, c in R8, d in R9,
// callee returns __m128 result in XMM0.

Example of return value 3 - user type result by pointer

struct Struct1 {
   int j, k, l;    // Struct1 exceeds 64 bits.
};
Struct1 func3(int a, double b, int c, float d);
// Caller allocates memory for Struct1 returned and passes pointer in RCX,
// a in RDX, b in XMM2, c in R9, d pushed on the stack;
// callee returns pointer to Struct1 result in RAX.

Example of return value 4 - user type result by value

struct Struct2 {
   int j, k;    // Struct2 fits in 64 bits, and meets requirements for return by value.
};
Struct2 func4(int a, double b, int c, float d);
// Caller passes a in RCX, b in XMM1, c in R8, and d in XMM3;
// callee returns Struct2 result by value in RAX.

猜你喜欢

转载自blog.csdn.net/lixiangminghate/article/details/87297468
今日推荐