fastcall调用约定是否真的比其他调用约定更快,例如cdecl?是否有任何基准测试表明调用约定会影响性能?
我在理解调用者和被调用者保存的寄存器之间的区别以及何时使用什么方面遇到了一些麻烦.
我使用的是MSP430:
程序:
mov.w #0,R7
mov.w #0,R6
add.w R6,R7
inc.w R6
cmp.w R12,R6
jl l$loop
mov.w R7,R12
ret
Run Code Online (Sandbox Code Playgroud)
上面的代码是被调用者,并且在教科书示例中使用,因此它遵循惯例.R6和R7被呼叫者保存,R12被呼叫者保存.我的理解是被调用者保存的regs不是"全局的",因为在过程中改变它的值不会影响它在程序之外的值.这就是您必须在开头将新值保存到被调用者注册表中的原因.
R12,保存的来电者是"全球性的",因为缺乏更好的词汇.该程序在通话后对R12产生持久影响.
我的理解是否正确?我错过了其他的东西吗?
编译以下代码片段时(clang x86-64 -O3
)
std::array<int, 5> test()
{
std::array<int, 5> values {{0, 1, 2, 3, 4}};
return values;
}
Run Code Online (Sandbox Code Playgroud)
它产生了我期望的典型装配
test(): # @test()
mov rax, rdi
mov ecx, dword ptr [rip + .L__const.test().values+16]
mov dword ptr [rdi + 16], ecx
movups xmm0, xmmword ptr [rip + .L__const.test().values]
movups xmmword ptr [rdi], xmm0
ret
.L__const.test().values:
.long 0 # 0x0
.long 1 # 0x1
.long 2 # 0x2
.long 3 # 0x3
.long 4 # 0x4
Run Code Online (Sandbox Code Playgroud)
但是对于小型阵列,似乎已经找到了窍门?
std::array<int, 3> test()
{ …
Run Code Online (Sandbox Code Playgroud) 我编写了函数'A',它将调用许多其他函数之一.为了保存重写函数'A',我想传递函数作为函数'A'的参数调用.例如:
function A{
Param($functionToCall)
Write-Host "I'm calling : $functionToCall"
}
function B{
Write-Host "Function B"
}
Function C{
write-host "Function C"
}
A -functionToCall C
Run Code Online (Sandbox Code Playgroud)
返回:我正在打电话:C
我期待它回归:我在呼唤:功能C.
我尝试了各种各样的东西,比如:
Param([scriptblock]$functionToCall)
Run Code Online (Sandbox Code Playgroud)
无法将System.String转换为ScriptBlock
A -functionToCall $function:C
Run Code Online (Sandbox Code Playgroud)
返回"写主机"功能C"
A - functionToCall (&C)
Run Code Online (Sandbox Code Playgroud)
这在其余部分之前进行评估:
Function C
I'm Calling :
Run Code Online (Sandbox Code Playgroud)
我确定这是编程101,但我无法弄清楚正确的语法或它是什么我做错了.我们将非常感激地提供任何帮助.非常感谢.
我可以理解旧PPC RISC系统的这个要求,甚至是x86-64,但是对于旧的,经过验证的x86?在这种情况下,堆栈只需要在4字节边界上对齐.是的,某些MMX/SSE指令需要16字节对齐,但如果这是被调用者的要求,则应确保对齐正确.为什么要为每个来电者增加这项额外要求?这实际上可能会导致性能下降,因为每个呼叫站点都必须管理此要求.我错过了什么吗?
更新:在对此进行一些调查并与一些内部同事进行一些咨询后,我对此有一些理论:
我对最后一项的问题是,对于依赖于被调用者清理堆栈的调用约定,上述要求实际上 "uglify"了codegen.例如,某些编译器决定为自己的内部使用实现更快的基于寄存器的调用样式(即任何不打算从其他语言或源调用的代码)?这种堆栈对齐可能会通过在寄存器中传递一些参数来抵消一些性能提升.
更新:到目前为止,唯一真正的答案是一致性,但对我来说,答案有点太容易了.我有超过20年的x86架构经验,如果一致性,而不是性能,或其他具体的东西,那么我真的是因为我恭敬地建议开发人员要求它有点天真.他们忽略了近三十年的工具和支持.特别是如果他们期望工具供应商能够快速轻松地为他们的平台调整他们的工具(可能不是......这是 Apple ......),而不必跳过几个看似不必要的箍.
我会在另一天左右给出这个话题,然后关闭它......
我试过这个
typedef void (* __stdcall MessageHandler)(const Task*);
Run Code Online (Sandbox Code Playgroud)
这编译但给了我这个警告(VS2003):
警告C4229:使用的时间错误:忽略数据上的修饰符
我想用stdcall调用约定声明一个指向函数的指针?我究竟做错了什么?
C++ 具有小型结构调用约定优化,其中编译器在函数参数中传递小型结构与传递原始类型(例如,通过寄存器)一样有效。例如:
class MyInt { int n; public: MyInt(int x) : n(x){} };
void foo(int);
void foo(MyInt);
void bar1() { foo(1); }
void bar2() { foo(MyInt(1)); }
Run Code Online (Sandbox Code Playgroud)
bar1()
并bar2()
生成几乎相同的汇编代码,除了分别调用foo(int)
和foo(MyInt)
。特别是在 x86_64 上,它看起来像:
mov edi, 1
jmp foo(MyInt) ;tail-call optimization jmp instead of call ret
Run Code Online (Sandbox Code Playgroud)
但是如果我们测试std::tuple<int>
,它会有所不同:
void foo(std::tuple<int>);
void bar3() { foo(std::tuple<int>(1)); }
struct MyIntTuple : std::tuple<int> { using std::tuple<int>::tuple; };
void foo(MyIntTuple);
void bar4() { foo(MyIntTuple(1)); }
Run Code Online (Sandbox Code Playgroud)
生成的汇编代码看起来完全不同,小尺寸的struct( std::tuple<int>
)是通过指针传递的:
sub rsp, 24 …
Run Code Online (Sandbox Code Playgroud) 函数的返回值通常存储在堆栈或寄存器中.但对于大型结构,它必须在堆栈上.在这个代码的真实编译器中必须进行多少复制?还是优化了?
例如:
struct Data {
unsigned values[256];
};
Data createData()
{
Data data;
// initialize data values...
return data;
}
Run Code Online (Sandbox Code Playgroud)
(假设函数无法内联..)
c compiler-theory abi calling-convention compiler-optimization
在以下 C 代码中:
#include <stdio.h>
int main(void){getchar();}
Run Code Online (Sandbox Code Playgroud)
它产生以下汇编:
main:
push rbp
mov rbp, rsp
# no extra instruction here when header is included
call getchar
mov eax, 0
pop rbp
ret
Run Code Online (Sandbox Code Playgroud)
但是,如果我不包含stdio.h
在文件中,那么它仍然可以编译,但会添加看起来像随机mov eax, 0
指令的内容:
这是编译器资源管理器:https : //godbolt.org/z/3fTcss。这只是“未定义行为”的一部分,还是有特殊原因将之前的指令call getchar
添加到那里?
int max(int n, ...)
Run Code Online (Sandbox Code Playgroud)
我正在使用cdecl
调用约定,其中调用者在被调用者返回后清理变量.
我想知道怎么做宏va_end
,va_start
和va_arg
工作?
调用者是否将参数数组的地址作为max的第二个参数传递?