在下面的C++源代码的汇编中.为什么RAX被推入堆栈?
正如我从ABI理解的那样,RAX可以包含来自调用函数的任何内容.但是我们将它保存在这里,然后将堆栈移回8个字节.所以堆栈上的RAX,我认为只与std::__throw_bad_function_call()
操作相关......?
代码:-
#include <functional>
void f(std::function<void()> a)
{
a();
}
Run Code Online (Sandbox Code Playgroud)
gcc.godbolt.org
使用Clang 3.7.1 -O3 输出:
f(std::function<void ()>): # @f(std::function<void ()>)
push rax
cmp qword ptr [rdi + 16], 0
je .LBB0_1
add rsp, 8
jmp qword ptr [rdi + 24] # TAILCALL
.LBB0_1:
call std::__throw_bad_function_call()
Run Code Online (Sandbox Code Playgroud)
我确定原因很明显,但我很难弄清楚.
这是一个没有std::function<void()>
包装器的尾部调用,用于比较:
void g(void(*a)())
{
a();
}
Run Code Online (Sandbox Code Playgroud)
琐碎的:
g(void (*)()): # @g(void (*)())
jmp rdi # TAILCALL
Run Code Online (Sandbox Code Playgroud) 尽管存在__restrict关键字,但将成员变量移动到局部变量会减少此循环中的写入次数.这是使用GCC -O3.Clang和MSVC在两种情况下都优化写入. [注意,由于这个问题已经发布,我们观察到将__restrict添加到调用函数会导致GCC也将商店移出循环.请参阅下面的godbolt链接和评论]
class X
{
public:
void process(float * __restrict d, int size)
{
for (int i = 0; i < size; ++i)
{
d[i] = v * c + d[i];
v = d[i];
}
}
void processFaster(float * __restrict d, int size)
{
float lv = v;
for (int i = 0; i < size; ++i)
{
d[i] = lv * c + d[i];
lv = d[i];
}
v = lv;
}
float c{0.0f};
float v{0.0f};
};
Run Code Online (Sandbox Code Playgroud)
使用gcc …
GCC汇编了这个:
#include <atomic>
std::atomic<int> a;
int b(0);
void func()
{
b = 2;
a = 1;
}
Run Code Online (Sandbox Code Playgroud)
对此:
func():
mov DWORD PTR b[rip], 2
mov DWORD PTR a[rip], 1
mfence
ret
Run Code Online (Sandbox Code Playgroud)
所以,为我澄清一些事情:
另外,clang(v3.5.1 -O3)这样做:
mov dword ptr [rip + b], 2
mov eax, 1
xchg dword ptr [rip + a], eax
ret
Run Code Online (Sandbox Code Playgroud)
这似乎对我的小脑子更直接,但为什么不同的方法,每个方法的优势是什么?
第一个版本通过将值从内存移动到局部变量来进行优化.第二个版本没有.
我原以为编译器可能会选择在这里进行localValue优化,而不是在循环的每次迭代中从内存中读取和写入值.为什么不呢?
class Example
{
public:
void processSamples(float * x, int num)
{
float localValue = v1;
for (int i = 0; i < num; ++i)
{
x[i] = x[i] + localValue;
localValue = 0.5 * x[i];
}
v1 = localValue;
}
void processSamples2(float * x, int num)
{
for (int i = 0; i < num; ++i)
{
x[i] = x[i] + v1;
v1 = 0.5 * x[i];
}
}
float v1;
};
Run Code Online (Sandbox Code Playgroud)
processSamples组装成代码如下:
.L4:
addss xmm0, DWORD PTR …
Run Code Online (Sandbox Code Playgroud) 我正在尝试调试堆损坏问题。我看过一些帖子提到 gflags 和应用程序验证器。这两个工具之间有什么区别,或者应用程序验证器是围绕相同技术的漂亮 UI?
举个愚蠢的例子:
class Base
{
public:
virtual void ant() { i++; };
virtual void dec() { i--; };
int i;
};
void function(Base * base)
{
base->ant();
base->dec();
}
Run Code Online (Sandbox Code Playgroud)
我想象这将由编译器实现的方式是两个虚函数调用。Clang就是这样做的(对dec()的调用使用尾部调用):
function(Base*): # @function(Base*)
push rbx
mov rbx, rdi
mov rax, qword ptr [rbx]
call qword ptr [rax]
mov rax, qword ptr [rbx]
mov rdi, rbx
pop rbx
jmp qword ptr [rax + 8] # TAILCALL
Run Code Online (Sandbox Code Playgroud)
另一方面,GCC在部分内联函数调用方面还有很长的路要走:
Base::ant():
add DWORD PTR [rdi+8], 1 # this_2(D)->i,
ret
Base::dec():
sub DWORD PTR [rdi+8], 1 …
Run Code Online (Sandbox Code Playgroud)