gpt4 book ai didi

assembly - Dlang - 理解汇编中的 std.cycle()

转载 作者:行者123 更新时间:2023-12-04 23:40:18 24 4
gpt4 key购买 nike

import std.range : cycle;
void foo() pure @safe {
cycle([1, 2]);
}

今天遇到一个用D语言写的程序。我试图从一个简单的函数开始理解它的汇编代码。

来自 the asm output on the D compiler explorer :

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

我试图读它几次,但不明白为什么 std.range.cycle()获取 3 个参数( RDIRSIRDX ),或者我的范围在哪里( [1, 2] )。它不是类 C 的结构吗?

还是我错过了什么?

最佳答案

看起来您正在使用 x86-64 SystemV ABI,它基于 rdi 和 rsi 进行 arg 传递,因为 Windows 64 位 ABI 使用不同的 regs。见标记 wiki 以获得 ABI 文档的链接,或查看 the current revision here .

按值传递的小对象(如结构)进入多个整数寄存器。按值返回大对象(超过 128 位)还使用指向调用者分配的空间的隐藏指针,而不是打包到 RDX:RAX 中。这就是你的函数中发生的事情。

基于 asm 和 docs ,我认为一个 Cycle 对象具有三个值:开始、结束和索引。我根本不知道D,但它是有道理的。由于它们都是 64 位的,因此它太大而无法放入 RDX:RAX,因此它由隐藏指针返回。

Cycle() 入口的 arg 传递寄存器是 :

  • RDI:指向返回值的“隐藏”指针(这是一个由三个 64 位整数组成的结构)
  • RSI:Range arg 的第一个成员(我称之为 range_start)
  • RDX:Range arg 的第二个成员(我称之为 range_end)


  • 我启用了优化以在没有太多噪音的情况下获得更具可读性的 asm,但不幸的是,看起来这个 D 编译器没有 clang 或 gcc 复杂得多。与 -O -release -inline (正如 this page 所推荐的),它仍然对堆栈进行大量存储/重新加载。
    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

    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

    或者只是完全内联对 Cycle 的调用,因为它很简单。实际上,我认为它确实内联到了 foo() 中,但是仍然发出了 cycle() 的独立定义。

    我们不知道哪两个函数 foo()调用,因为编译器资源管理器似乎正在反汇编 .o (不是链接的二进制文件)而不解析符号。所以调用偏移量是 00 00 00 00 ,链接器的占位符。但它可能正在调用内存分配函数,因为它使用 esi=2 和 edi=0 进行调用。 ( Using mov edi, 0 in optimizing release mode! Yuck!)。调用目标显示为下一条指令,因为那是 call's rel32 displacement从算起。

    希望 LDCGDC做得更好,因为它们基于现代优化后端(LLVM 和 gcc),但是您链接的编译器资源管理器站点没有安装这些编译器。如果有另一个基于 Matt Godbolt's compiler explorer code 的站点,但是对于其他 D 编译器,那会很酷。

    关于assembly - Dlang - 理解汇编中的 std.cycle(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39546575/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com