Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/image/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 将右值传递给非ref参数,为什么可以';编译器没有删除副本吗?_C++_Clang_X86 64_Compiler Optimization_Abi - Fatal编程技术网

C++ 将右值传递给非ref参数,为什么可以';编译器没有删除副本吗?

C++ 将右值传递给非ref参数,为什么可以';编译器没有删除副本吗?,c++,clang,x86-64,compiler-optimization,abi,C++,Clang,X86 64,Compiler Optimization,Abi,编译(在Linux so System V ABI上使用针对x86_64的clang 6.0.0,标志:-O3-march=broadwell)到 如果我读对了,这就是正在发生的事情: getStuff被传递一个指向foo堆栈(rsp+40)的指针,用于其返回值,因此getStuff之后返回rsp+40到rsp+71包含getStuff的结果 然后,该结果立即复制到较低的堆栈地址rsp到rsp+31 然后调用foo,它将从rsp读取其参数 为什么下面的代码不是完全等效的(为什么编译器不生成它)

编译(在Linux so System V ABI上使用针对x86_64的clang 6.0.0,标志:
-O3-march=broadwell
)到

如果我读对了,这就是正在发生的事情:

  • getStuff
    被传递一个指向
    foo
    堆栈(
    rsp+40
    )的指针,用于其返回值,因此
    getStuff
    之后返回
    rsp+40
    rsp+71
    包含
    getStuff
    的结果
  • 然后,该结果立即复制到较低的堆栈地址
    rsp
    rsp+31
  • 然后调用
    foo
    ,它将从
    rsp
    读取其参数
  • 为什么下面的代码不是完全等效的(为什么编译器不生成它)

    其思想是:让
    getStuff
    直接写入堆栈中
    foo
    将从中读取的位置

    此外: 下面是vc++在windows for x64上编译的相同代码(12个整数而不是8个整数)的结果,这似乎更糟糕,因为windows x64 ABI通过引用传递和返回,因此副本完全未使用

    test1():                              # @test1()
            sub     rsp, 32
            mov     rdi, rsp
            call    getStuff()
            call    foo(Big)
            add     rsp, 32
            ret
    

    你是对的编译器似乎错过了优化。如果还没有重复的错误,您可以报告此错误()

    与流行的观点相反,编译器通常不会生成最佳代码。它通常已经足够好了,而且现代CPU在不太长依赖链的情况下非常擅长处理多余的指令,特别是关键路径依赖链(如果有)

    如果大型结构不适合打包到两个64位整数寄存器中,则在堆栈上按值传递大型结构,并通过隐藏指针返回。编译器可以也应该(但不应该)提前计划并将返回值临时重用为调用
    foo(Big)
    的堆栈参数


    gcc7.3、ICC18和MSVC CL19也未进行此优化。:/我把你的密码放上去了。gcc使用4x
    push-qword[rsp+24]
    进行复制,而ICC使用额外的指令将堆栈对齐32

    使用1x 32字节加载/存储而不是2x 16字节可能无法证明MSVC/ICC/clang的
    vzeroupper
    对于如此小的函数的成本是合理的
    vzeroupper
    在主流Intel CPU(只有4个UOP)上很便宜,我确实使用了
    -march=haswell
    来调整它,而不是AMD或KNL,因为后者更贵


    相关:x86-64 Windows通过隐藏指针传递大型结构,并以这种方式返回它们。被调用方拥有指向的内存。()

    在第一次调用
    getStuff()
    之前,只需为临时+阴影空间保留空间,并允许被调用方销毁临时空间,因为我们以后不需要它,这种优化仍然可用

    不幸的是,MSVC在这里或相关案例中并不是这么做的

    另请参见@BeeOnRope的答案和我的评论。如果您试图设计通过传递隐藏常量引用来避免复制的调用约定(调用者拥有内存,被调用者可以在需要时进行复制),那么确保复制构造函数始终在非平凡可复制对象的合理位置运行是有问题的

    但这是一个非常量引用(被调用方拥有内存)最好的例子,因为调用方希望将对象交给被调用方

    不过,有一个潜在的问题:如果有指向此对象的指针,让被调用方直接使用它可能会导致错误。考虑一些其他函数:<代码> GULALL指针[A](4)=0;<代码>。如果被调用方调用该函数,它将意外地修改被调用方的by值arg


    因此,只有在转义分析可以证明没有其他东西有指向该对象的指针时,才允许被调用方在Windows x64调用约定中销毁该对象的副本。

    您可以提供您编译的调用约定吗?System V amd64已将该信息添加到问题中。不确定这是否有帮助,但是标准规定函数参数不能是NRVO的。因此,至少有一种省略是不可能发生的(当然,除了在“似乎”规则下)。根据系统V,ABI
    struct Big
    并不是真正的大。它实际上是
    INTEGER
    类的边界大小。你能试试更大的尺寸吗?我已经纠正了,显然
    int
    成员的不对中导致结构被归类为
    MEMORY
    ,谢谢你的回答。我仍然怀疑,我觉得这是一件非常简单的事情,编译器在这里会非常好。我会报告一个错误,我们会看到。。。R.e.关于windows的观点,这也许可以解释为什么我最终会与windows人员讨论函数调用边界处的副本。@John_C:这是对人类来说很简单但对编译器来说却不那么简单的事情之一,特别是在为实际目标体系结构发出代码之前在通用内部表示中进行优化的便携式编译器。在我提交的几个遗漏的优化bug报告中,gcc开发人员说很难修复,因为编译的后期需要知道一些早期阶段没有传递的东西,反之亦然。(例如,RTL优化器不知道值是否有符号:,并跟踪bug 85038。出于兴趣,我在windows上尝试了这个方法,它也复制了一个!有关详细信息,请参阅原始帖子。)details@John_C:是的,我一点也不惊讶。它基本上仍然是相同的优化,尽管在Windows上只需传递指针就更容易了。我不是s在Windows上确定是否允许被调用方删除数据的指向副本,即是否“拥有”其指针指向的参数,如stack by args(当然也可以是register args),或者是否为by const reference。const ref将允许
    test1():                              # @test1()
            sub     rsp, 72
            lea     rdi, [rsp + 40]
            call    getStuff()
            vmovups ymm0, ymmword ptr [rsp + 40]
            vmovups ymmword ptr [rsp], ymm0
            vzeroupper
            call    foo(Big)
            add     rsp, 72
            ret
    
    test1():                              # @test1()
            sub     rsp, 32
            mov     rdi, rsp
            call    getStuff()
            call    foo(Big)
            add     rsp, 32
            ret
    
    _TEXT   SEGMENT
    $T3 = 32
    $T1 = 32
    ?bar@@YAHXZ PROC                    ; bar, COMDAT
    
    $LN4:
        sub rsp, 88                 ; 00000058H
    
        lea rcx, QWORD PTR $T1[rsp]
        call    ?getStuff@@YA?AUBig@@XZ         ; getStuff
        lea rcx, QWORD PTR $T3[rsp]
        movups  xmm0, XMMWORD PTR [rax]
        movaps  XMMWORD PTR $T3[rsp], xmm0
        movups  xmm1, XMMWORD PTR [rax+16]
        movaps  XMMWORD PTR $T3[rsp+16], xmm1
        movups  xmm0, XMMWORD PTR [rax+32]
        movaps  XMMWORD PTR $T3[rsp+32], xmm0
        call    ?foo@@YAHUBig@@@Z           ; foo
    
        add rsp, 88                 ; 00000058H
        ret 0