Next: , Previous: Caller Saves, Up: Stack and Calling


17.10.11 函数入口和出口

这一章描述了输出函数入口(prologue)和出口(epilogue)代码的宏。

— Target Hook: void TARGET_ASM_FUNCTION_PROLOGUE (FILE *file, HOST_WIDE_INT size)

如果被定义,则为一个函数,其为函数的入口输出汇编代码。序言负责设置栈帧, 初始化帧指针寄存器,保存必须被保存的机器,并分配保存局部变量所需要的额外字节数 sizesize为一个整数。 file为汇编代码应该被输出到的一个stdio流。

函数起始处的标号不需要被该宏输出。其已经在该宏运行时被输出了。

要确定哪些寄存器要保存,该宏可以引用数组regs_ever_live: 元素r为非零,如果硬件寄存器r在函数某处被使用。 这意味着倘若其不是调用使用的(call-used)寄存器, 则函数序言应该保存寄存器r。 (同样TARGET_ASM_FUNCTION_EPILOGUE也必须使用regs_ever_live。)

在具有“寄存器窗口”的机器上,函数入口代码不在栈中保存位于窗口中的寄存器, 即使它们认为被函数调用保留;替代的,如果在函数中使用了任何非调用使用的寄存器, 其使用适当的步骤来“压入”寄存器栈中。

在一些机器上,函数可以有帧指针,也可以没有,则函数入口代码必须相应的有所不同; 如果需要则其必须建立帧指针,否则不建立。要确定是否想要帧指针, 宏可以引用变量frame_pointer_needed。在运行时,如果函数需要帧指针, 则变量的值将被设为1。参见Elimination

函数入口代码负责分配函数需要的任何栈空间。该栈空间包括下面列出的域。 大多数情况下,这些域按照列出的顺序被分配,最后列出的域最靠近栈顶 (如果STACK_GROWS_DOWNWARD被定义,则为最低地址,如果没有定义, 则为最高地址)。你可以为一个机器使用不同的顺序, 如果这样做更加方便或者出于兼容的原因。除了由于标准或者调试器的要求之外, 没有理由GCC使用的栈布局需要适合机器上的其它编译器所使用的。

— Target Hook: void TARGET_ASM_FUNCTION_END_PROLOGUE (FILE *file)

如果被定义,则为一个函数,在序言的结尾处输出汇编代码。 这应该被用于当函数序言作为RTL输出时,并且你需要输出一些额外的汇编语言。 参见“序言指令模式”

— Target Hook: void TARGET_ASM_FUNCTION_BEGIN_EPILOGUE (FILE *file)

如果被定义,则为一个函数,在尾声的起始处输出汇编代码。 这应该被用于当函数尾声作为RTL输出时,并且你需要输出一些额外的汇编语言。 参见“尾声指令模式”

— Target Hook: void TARGET_ASM_FUNCTION_EPILOGUE (FILE *file, HOST_WIDE_INT size)

如果被定义,则为一个函数,其为函数的退出输出汇编代码。 尾声负责恢复保存的寄存器和栈指针为函数被调用时的值,并将控制返回给调用者。 该宏接受跟TARGET_ASM_FUNCTION_PROLOGUE相同的参数, 并且要恢复的寄存器按照相同的方式通过regs_ever_liveCALL_USED_REGISTERS来确定。

在一些机器上,有一个单独的指令,可以做从函数中返回的所有工作。 在这些机器上,给出那个名为‘return’的指令, 并且不要定义宏TARGET_ASM_FUNCTION_EPILOGUE

如果你想使用TARGET_ASM_FUNCTION_EPILOGUE, 则不要定义名为‘return’的指令模式。 如果你想target切换使用return指令或者尾声,则定义一个‘return’指令模式, 带有一个有效性条件用来测试target的适当的切换。 如果‘return’指令模式的有效性条件为假,则使用尾声。

在一些机器上,函数可以有帧指针,也可以没有,则函数的退出代码必须相应有所不同。 有时这两种情况的代码会完全不同。要确定是否需要帧指针, 该宏可以引用变量frame_pointer_needed。当编译一个需要帧指针的函数时, 变量的值将为1。

通常,TARGET_ASM_FUNCTION_PROLOGUETARGET_ASM_FUNCTION_EPILOGUE 必须单独处理叶子函数。对于这样的函数,C变量current_function_is_leaf为 非零。参见Leaf Functions

在一些机器上,一些函数在退出时弹出它们的参数,而其它的则将它们留给调用者来完成。 例如,68020当给定-mrtd时会弹出具有固定参数个数的函数的参数。

你对宏的定义决定了哪些函数弹出它们的自己的参数。 TARGET_ASM_FUNCTION_EPILOGUE需要知道这些。 称作current_function_pops_args的变量为函数应该弹出的参数的字节个数。 参见Scalar Return

— Macro: EXIT_IGNORE_STACK

定义该宏为一个C表达式,其为非0,如果返回指令或者函数尾声忽略栈指针的值; 换句话说,如果在从函数中返回前,删除调整栈指针的指令是安全的。缺省为0。

注意该宏的值只于维护帧指针的函数相关。 在没有帧指针的函数中删除最后的栈调整是绝对不安全的,并且编译器知道这种情况, 而不管EXIT_IGNORE_STACK定义如何。

— Macro: EPILOGUE_USES (regno)

定义该宏为一个C表达式,其为非0,对于用于尾声或者‘return’指令模式的寄存器。 栈和帧指针寄存器已经被假设需要使用。

— Macro: EH_USES (regno)

定义该宏为一个C表达式,其为非0,对于用于异常处理机制的寄存器, 所以其应该被考虑为在一个异常边的入口上是活跃的。

— Macro: DELAY_SLOTS_FOR_EPILOGUE

定义该宏,如果函数尾声包含延迟槽,并且函数其余的指令可以被移动过去。 该定义应该为一个C表达式,其值为一个整数表示有多少个延迟槽。

— Macro: ELIGIBLE_FOR_EPILOGUE_DELAY (insn, n)

一个C表达式,返回1,如果insn可以放在尾声中的延迟槽编号n中。

参数n为一个整数,其标识了目前被考虑的延迟槽(由于不同的延迟槽可以具有不同 的适任规则)。其从不为负,并且总是小于尾声延迟槽的总数 (DELAY_SLOTS_FOR_EPILOGUE的返回值)。 如果你为给定的延迟槽拒绝了一个特定的insn,原则上,其可以被后续的延迟槽重新考虑。 而且,其它insn还可以(至少原则上)被目前为止还没有被填充的延迟槽考虑。

被接受填充尾声延迟槽的insn被放在一个RTL链表中,使用insn_list对象, 并存储在变量current_function_epilogue_delay_list中。 第一个延迟槽的insn位于链表中的第一个。你对宏TARGET_ASM_FUNCTION_EPILOGUE 的定义应该通过输出该链表的insn来填充延迟槽,通常是调用final_scan_insn

你不需要定义该宏,如果你没有定义DELAY_SLOTS_FOR_EPILOGUE

— Target Hook: void TARGET_ASM_OUTPUT_MI_THUNK (FILE *file, tree thunk_fndecl, HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, tree function)

一个函数,输出一个thunk函数的汇编代码,用于实现具有多继承的C++虚函数调用。 thunk作为一个虚函数的封装,用来调整隐式对象参数,在将控制移交给实函数之前。

首选,输出代码来增加整数delta为包含传递进来的第一个参数的为。 假设该参数包含一个指针,并用于在C++中传递this指针。 这是在函数序言之前的参数,例如在sparc上为‘%o0’。

然后,如果vcall_offset非0,则在增加delta之后应该进行额外的调整。 特别是,如果p为被调整的指针,则应该进行如下的调整:

          p += (*((ptrdiff_t **)p))[vcall_offset/sizeof(ptrdiff_t)]

加法之后,输出代码跳转到function,其为FUNCTION_DECL。 这是一个直接跳转,而不是调用,并且不触及返回地址。因此从FUNCTION中返回时, 将返回到调用当前‘thunk’的地方。

其效果就好像是函数被直接调用,并使用调整后的第一个参数。 该宏负责输出thunk函数的所有代码;TARGET_ASM_FUNCTION_PROLOGUETARGET_ASM_FUNCTION_EPILOGUE不被调用。

thunk_fndecl是冗余的。(deltafunction已经从中被抽取出来。) 其可能在一些target上有用,也很可能没用。

如果你没有定义该宏,则C++前端的target无关代码将会生成一个不太有效的重量级的 thunk,其调用function而不是直接跳转过去。普通的方法不支持varargs。

— Target Hook: bool TARGET_ASM_CAN_OUTPUT_MI_THUNK (tree thunk_fndecl, HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, tree function)

一个函数,返回真,如果TARGET_ASM_OUTPUT_MI_THUNK应该能够为其传递的参数所指定的 thunk函数输出汇编代码,否则为假。在后一种情况下,C++前端将会使用普通的方式, 并具有之前提到的限制。