Khá*_*ang 4 assembly d x86-64 std abi
import std.range : cycle;
void foo() pure @safe {
cycle([1, 2]);
}
Run Code Online (Sandbox Code Playgroud)
今天我遇到了用D语言编写的程序.我试图了解它的汇编代码,从一个简单的函数开始.
pure nothrow @nogc @safe std.range.Cycle!(int[]).Cycle std.range.cycle!(int[]).cycle(int[]):
push rbp
mov rbp,rsp
sub rsp,0x40
mov QWORD PTR [rbp-0x20],rdi
mov QWORD PTR [rbp-0x10],rsi
mov QWORD PTR [rbp-0x8],rdx
... rest of the function
Run Code Online (Sandbox Code Playgroud)
我已经尝试了几次读它,但无法理解为什么
std.range.cycle()得到3个参数(RDI,RSI和RDX),或者我的范围是([1, 2]).它不是一个类似C的结构?
或者我错过了什么?
看起来你正在使用x86-64 SystemV ABI,基于rdi和rsi进行arg传递,因为Windows 64位ABI使用不同的regs.有关ABI文档的链接,请参阅x86标记wiki,或在此处查看当前版本.
通过值传递的小对象(如结构)进入多个整数寄存器.按值返回大对象(超过128位)也会使用隐藏指针来指向调用者分配的空间,而不是打包到RDX:RAX中.这是你的功能中发生的事情.
基于asm和docs,我认为Cycle对象有三个值:start,end和index.我根本不知道D,但这是有道理的.因为它们都是64位,所以它太大而不适合RDX:RAX,所以它是由隐藏指针返回的.
进入Cycle()的arg传递寄存器是:
我启用了优化以获得更具可读性的asm而没有太多噪音,但遗憾的是,看起来这个D编译器比clang或gcc要复杂得多.使用-O -release -inline(按照本页的建议),它仍然会对堆栈进行大量存储/重新加载.
pure nothrow @nogc @safe std.range.Cycle!(int[]).Cycle std.range.cycle!(int[]).cycle(int[]):
sub rsp,0x28
mov QWORD PTR [rsp+0x20],rdi # hidden first arg (return-value pointer).
mov QWORD PTR [rsp+0x8],0x0 # totally useless: overwritten without read
mov QWORD PTR [rsp+0x10],0x0 # totally useless: same.
mov QWORD PTR [rsp+0x8],rsi # first "real" arg
mov QWORD PTR [rsp+0x10],rdx # second "real" arg
xor eax,eax
xor edx,edx # zero rax:rdx. Perhaps from the index=0 default when you only use one arg?
div QWORD PTR [rsp+0x8] # divide 0 by first arg of the range.
mov QWORD PTR [rsp+0x18],rdx # remainder of (index / range_start), I guess.
lea rsi,[rsp+0x8] # RSI=pointer to where range_start, range_end, and index/range_start were stored on the stack.
movs QWORD PTR es:[rdi],QWORD PTR ds:[rsi] # copy to the dst buffer. A smart compiler would have stored there in the first place, instead of to local scratch and then copying.
movs QWORD PTR es:[rdi],QWORD PTR ds:[rsi] # movs is not very efficient, this is horrible code.
movs QWORD PTR es:[rdi],QWORD PTR ds:[rsi]
mov rax,QWORD PTR [rsp+0x20] # mov rax, rdi before those MOVS instructions would have been much more efficient.
add rsp,0x28
ret
Run Code Online (Sandbox Code Playgroud)
ABI需要返回大对象的函数来返回RAX中的隐藏指针,因此调用者不必单独保留指向返回缓冲区的指针的副本.这就是函数设置RAX的原因.
一个好的编译器会这样做:
std.range.Cycle...:
mov [rdi], rsi # cycle_start
mov [rdi+0x8], rdx # cycle_end
mov [rdi+0x10], 0 # index
mov rax, rdi
ret
Run Code Online (Sandbox Code Playgroud)
或者只是完全调用了对Cycle的调用,因为它是微不足道的.其实,我觉得它没有内嵌到FOO(),但对于周期独立的定义()仍然发出.
我们无法分辨哪两个函数foo()调用,因为编译器资源管理器似乎在不解析符号的情况下反汇编.o(不是链接的二进制文件).因此,调用偏移量是00 00 00 00链接器的占位符.但它可能正在调用内存分配函数,因为它使用esi = 2和edi = 0进行调用.(用mov edi, 0在优化释放模式!呸!).调用目标显示为下一条指令,因为这是调用的rel32位移计数的地方.
希望LDC或GDC做得更好,因为它们基于现代优化后端(LLVM和gcc),但您链接的编译器 - 资源管理器站点没有安装这些编译器.如果有另一个基于Matt Godbolt的编译器资源管理器代码的站点,但与其他D编译器一样,那将是很酷的.
| 归档时间: |
|
| 查看次数: |
134 次 |
| 最近记录: |