Next: Trampolines, Previous: Stack and Calling, Up: Target Macros
GCC自带了<varargs.h>
和<stdarg.h>
的实现,
可直接用于在栈上传递参数的机器上。其它机器需要它们自己的varargs实现,
并且两个机器独立的头文件必须条件包含它。
ISO <stdarg.h>
与传统<varargs.h>
的区别主要在va_start
的调用
约定上。传统的实现只接受一个参数,其为存储参数指针的变量。
ISO的实现接受额外的第二个参数。用户应该将函数的最后一个命名参数写在这里。
然而,va_start
不应该使用这个参数。
发现命名参数结尾的方法为使用下面描述的内建函数。
使用该内建函数来将参数寄存器保存在内存中,使得varargs机制可以访问它们。
va_start
的ISO版本和传统版本都必须使用__builtin_saveregs
, 除非你使用TARGET_SETUP_INCOMING_VARARGS
来替代(参见下面)。在一些机器上,
__builtin_saveregs
为开放编码的, 在target钩子TARGET_EXPAND_BUILTIN_SAVEREGS
的控制下。 在其它机器上,其调用了汇编语言写的例程,可以在libgcc2.c中找到。不管怎样,调用
__builtin_saveregs
的生成代码都出现在函数的起始处。 这是因为寄存器必须在函数开始使用它们前被保存。
这与
__builtin_args_info
等价,用于栈参数。其返回第一个匿名栈参数的地址, 类型为void *
。如果ARGS_GROW_DOWNWARD
, 其返回第一个匿名栈参数的上面的位置地址。在va_start
中使用它来初始化指针, 来从栈中获得参数。同样,在va_start
中使用它来验证第二个参数lastarg 为当前函数的最后一个命名参数。
由于每个机器具有它自己的约定,对于哪些数据类型在哪种寄存器中传递, 因此你的
va_arg
实现必须包含这些约定。 将指定数据类型归类的最简单方法是使用__builtin_classify_type
, 加上sizeof
和__alignof__
。
__builtin_classify_type
忽略object的值,只考虑它的数据类型。 其返回一个整数来描述什么类型为,整型,浮点,指针,结构体等。文件typeclass.h定义了一个枚举, 你可以用来解析
__builtin_classify_type
的值。
这些机器描述宏用来帮助实现varargs:
如果定义,该钩子产生机器特定代码,用于调用
__builtin_saveregs
。 该代码将被移动到函数的最开始处,在访问任何参数之前。 该函数的返回值应该为一个RTX,其包含了__builtin_saveregs
的返回值。
该target钩子提供了使用
__builtin_saveregs
和定义TARGET_EXPAND_BUILTIN_SAVEREGS
钩子的替代。 使用它来将匿名寄存器参数存储到栈中,使得所有参数都像是通过栈连续的传递。 当这样做时,你可以使用varargs的标准实现,其用于将所有参数在栈上传递的机器上。参数args_so_far指向
CUMULATIVE_ARGS
数据结构体, 包含了处理完命名参数之后所获得的值。 参数mode和type描述了最后一个命名参数的机器模式和作为树结点的数据类型。该target钩子应该做两件事:第一,将所有不用于命名参数的参数寄存器压入栈中, 第二,存储数据的大小,把pretend_args_size指向的
int
值得变量压入。 这里你存储的值将作为额外的偏移量,用来建立栈帧。因为你必须生成代码来将匿名参数在编译时压入,而不需要知道它们的数据类型, 所以
TARGET_SETUP_INCOMING_VARARGS
只在只有一种参数寄存器类别并用于所有 数据类型的机器上有用。如果参数second_time非0,其以为着函数的参数被第二次分析。 这发生于内联函数,其直到源文件结尾才被实际编译。对于这种情况, 钩子
TARGET_SETUP_INCOMING_VARARGS
不应该产生任何指令。
定义该钩子来返回
true
,如果函数参数传递的位置依赖于其是否为一个命名参数。该钩子控制对于varargs和stdarg函数,如何设置FUNCTION_ARG的named参数。 如果该钩子返回
true
,则named参数总是为命名参数,未命名参数总是未假。 如果返回false
,但是TARGET_PRETEND_OUTGOING_VARARGS_NAMED
返回true
,则所有参数都被作为命名的对待。否则所有命名参数,除了最后一个, 被作为命名的对待。如果其总是返回0,则不需要定义该钩子。