内联函数如何编译为汇编?

时间:2019-02-19 18:40:17

标签: c++ assembly visual-c++ inline

我有一些C ++代码可以循环以更新值,出于好奇,我想看看组成主体循环的程序集。这使我对编译后的内联外观进行了一些实验(编译器是带有O2的MSVC )。

但是,当我将指令集与实际内联时的外观进行比较时,我对发现的结果感到有些困惑。这里是一些上下文:

template<typename T>
struct ClassWithInline
{
    Values *v;

    ClassWithInline(Values *v) : v{ v } {}
    T inlineMe(T * const c) const
    {
        // some function of *c, using v->some_constants
    }
};

Values对象只是包含常量的东西。 ClassWithInline是另一个对象Owner的成员,并且所有者具有函数callTheInline

struct Owner
{

    ClassWithInline<double> a;
    Values *v;

    Owner(Values *v) : a{ ClassWithInline<double>(v) }, v{ v } {}
    void callTheInline()
    {
        double *ptr = new double[100];
        double *dptr = new double[100];

        size_t the_end = std::floor(1000 + log(100000));

        for (size_t n = 0; n < the_end; ++n)
        {
            dptr[n] = a.inlineMe(ptr + n);
        }

        ClassWithInline<double> b(v);
        for (size_t n = 0; n < the_end; ++n)
        {
            dptr[n] = b.inlineMe(ptr + n);
        }
    }
};

(最终的迭代次数太少了,因此编译器在编译时不知道循环的大小,因此引入了其他一些优化方法。)

现在,当我查看为for循环生成的程序集时,它们有很大的不同。实际上,从inlineMe调用a的那个具有两倍的汇编指令。我如何弥合这种差距?

a.inlineMe(ptr + n);

000000013F642094  mov         rbp,rbx  
000000013F642097  mov         qword ptr [rsp+20h],r15  
000000013F64209C  sub         rbp,rsi  
000000013F64209F  lea         r15,[r9-3]  
000000013F6420A3  mov         r14,rsi  
000000013F6420A6  lea         r10,[rbx+8]  
000000013F6420AA  sub         r14,rbx  
000000013F6420AD  nop         dword ptr [rax]  
000000013F6420B0  mov         rcx,qword ptr [rdi]  
000000013F6420B3  lea         rdx,[r14+r10]  
000000013F6420B7  movsd       xmm0,mmword ptr [r10-8]  
000000013F6420BD  movsd       xmm1,mmword ptr [rdx+rbp-10h]  
000000013F6420C3  addsd       xmm1,mmword ptr [r10]  
000000013F6420C8  movsd       xmm2,mmword ptr [rdi+8]  
000000013F6420CD  lea         rax,[rcx+r8]  
000000013F6420D1  mulsd       xmm0,xmm3  
000000013F6420D5  mulsd       xmm2,xmm2  
000000013F6420D9  addsd       xmm1,mmword ptr [rbx+rax*8]  
000000013F6420DE  mov         rax,r8  
000000013F6420E1  sub         rax,rcx  
000000013F6420E4  addsd       xmm1,mmword ptr [rbx+rax*8]  
000000013F6420E9  subsd       xmm1,xmm0  
000000013F6420ED  divsd       xmm1,xmm2  
000000013F6420F1  movsd       mmword ptr [r14+r10-8],xmm1  
000000013F6420F8  movsd       xmm1,mmword ptr [r10+8]  
000000013F6420FE  addsd       xmm1,mmword ptr [r10-8]  
000000013F642104  mov         rcx,qword ptr [rdi]  
000000013F642107  movsd       xmm0,mmword ptr [r10]  
000000013F64210C  movsd       xmm2,mmword ptr [rdi+8]  
000000013F642111  mulsd       xmm0,xmm3  
000000013F642115  lea         rax,[rcx+r8]  
000000013F642119  mulsd       xmm2,xmm2  
000000013F64211D  addsd       xmm1,mmword ptr [rbx+rax*8+8]  
000000013F642123  mov         rax,r8  
000000013F642126  sub         rax,rcx  
000000013F642129  addsd       xmm1,mmword ptr [rbx+rax*8+8]  
000000013F64212F  subsd       xmm1,xmm0  
000000013F642133  divsd       xmm1,xmm2  
000000013F642137  movsd       mmword ptr [rdx],xmm1  
000000013F64213B  movsd       xmm1,mmword ptr [r10+10h]  
000000013F642141  addsd       xmm1,mmword ptr [r10]  
000000013F642146  mov         rcx,qword ptr [rdi]  
000000013F642149  movsd       xmm0,mmword ptr [r10+8]  
000000013F64214F  movsd       xmm2,mmword ptr [rdi+8]  
000000013F642154  mulsd       xmm0,xmm3  
000000013F642158  lea         rax,[rcx+r8]  
000000013F64215C  mulsd       xmm2,xmm2  
000000013F642160  addsd       xmm1,mmword ptr [rbx+rax*8+10h]  
000000013F642166  mov         rax,r8  
000000013F642169  sub         rax,rcx  
000000013F64216C  addsd       xmm1,mmword ptr [rbx+rax*8+10h]  
000000013F642172  subsd       xmm1,xmm0  
000000013F642176  divsd       xmm1,xmm2  
000000013F64217A  movsd       mmword ptr [r14+r10+8],xmm1  
000000013F642181  movsd       xmm1,mmword ptr [r10+18h]  
000000013F642187  addsd       xmm1,mmword ptr [r10+8]  
000000013F64218D  mov         rcx,qword ptr [rdi]  
000000013F642190  movsd       xmm0,mmword ptr [r10+10h]  
000000013F642196  movsd       xmm2,mmword ptr [rdi+8]  
000000013F64219B  mulsd       xmm0,xmm3  
000000013F64219F  lea         rax,[rcx+r8]  
000000013F6421A3  mulsd       xmm2,xmm2  
000000013F6421A7  addsd       xmm1,mmword ptr [rbx+rax*8+18h]  
000000013F6421AD  mov         rax,r8  
000000013F6421B0  add         r8,4  
000000013F6421B4  sub         rax,rcx  
000000013F6421B7  addsd       xmm1,mmword ptr [rbx+rax*8+18h]  
000000013F6421BD  subsd       xmm1,xmm0  
000000013F6421C1  divsd       xmm1,xmm2  
000000013F6421C5  movsd       mmword ptr [r14+r10+10h],xmm1  
000000013F6421CC  add         r10,20h  
000000013F6421D0  cmp         r8,r15  
000000013F6421D3  jb          Owner::callTheInline+0B0h (013F6420B0h) 

b.inlineMe(ptr + n);

000000013F6422A4  movsd       xmm1,mmword ptr [rcx+r10*8-10h]  
000000013F6422AB  addsd       xmm1,mmword ptr [rdx+rcx]  
000000013F6422B0  movsd       xmm0,mmword ptr [rdx+rcx-8]  
000000013F6422B6  mulsd       xmm0,xmm3  
000000013F6422BA  addsd       xmm1,mmword ptr [rcx+r8*8-8]  
000000013F6422C1  addsd       xmm1,mmword ptr [rcx-8]  
000000013F6422C6  subsd       xmm1,xmm0  
000000013F6422CA  divsd       xmm1,xmm5  
000000013F6422CE  movsd       mmword ptr [rdi+rcx-8],xmm1  
000000013F6422D4  movsd       xmm2,mmword ptr [rdx+rcx-8]  
000000013F6422DA  addsd       xmm2,mmword ptr [rdx+rcx+8]  
000000013F6422E0  movsd       xmm0,mmword ptr [rdx+rcx]  
000000013F6422E5  mulsd       xmm0,xmm3  
000000013F6422E9  addsd       xmm2,mmword ptr [rcx+r8*8]  
000000013F6422EF  addsd       xmm2,mmword ptr [rcx]  
000000013F6422F3  subsd       xmm2,xmm0  
000000013F6422F7  divsd       xmm2,xmm5  
000000013F6422FB  movsd       mmword ptr [rdi+rcx],xmm2  
000000013F642300  movsd       xmm0,mmword ptr [rdx+rcx+8]  
000000013F642306  movsd       xmm1,mmword ptr [rdx+rcx]  
000000013F64230B  addsd       xmm1,mmword ptr [rcx+rbp]  
000000013F642310  mulsd       xmm0,xmm3  
000000013F642314  addsd       xmm1,mmword ptr [rcx+r8*8+8]  
000000013F64231B  addsd       xmm1,mmword ptr [rcx+8]  
000000013F642320  subsd       xmm1,xmm0  
000000013F642324  divsd       xmm1,xmm5  
000000013F642328  movsd       mmword ptr [rdi+rcx+8],xmm1  
000000013F64232E  movsd       xmm2,mmword ptr [rcx+r10*8+18h]  
000000013F642335  addsd       xmm2,mmword ptr [rdx+rcx+8]  
000000013F64233B  movsd       xmm0,mmword ptr [rcx+rbp]  
000000013F642340  mulsd       xmm0,xmm3  
000000013F642344  addsd       xmm2,mmword ptr [rcx+r8*8+10h]  
000000013F64234B  addsd       xmm2,mmword ptr [rcx+10h]  
000000013F642350  subsd       xmm2,xmm0  
000000013F642354  divsd       xmm2,xmm5  
000000013F642358  movsd       mmword ptr [r14+rcx],xmm2  
000000013F64235E  add         rcx,20h  
000000013F642362  sub         rax,1  
000000013F642366  jne         Owner::callTheInline+2A4h (013F6422A4h)  

2 个答案:

答案 0 :(得分:2)

函数内联具有三个主要作用:

  • 它消除了函数调用的开销。
  • 它允许编译器跨函数边界进行优化。
  • 它允许编译器对传递给函数的硬编码参数进行艰难的假设。这包括指向成员函数的this指针。

内联总是在C ++代码转换为汇编之前发生。编译器实质上将内联函数视为被调用函数的源代码已插入到调用位置。几乎。 (实际上,编译器通常还 将内联函数编译为普通的普通函数并为其分配弱链接,但随后在进一步的内联过程中将不再使用它。在此不感兴趣。)

在您的示例中,aOwner的成员,而b是堆栈上的局部变量。 ab都保持状态v

要寻址a,编译器需要通过Owner的this指针对其进行寻址。为了解决b,编译器不需要使用Owner的this指针,它只是在堆栈上。仅此一项就已经在指令数量上产生了很大的不同。实际上,这还取决于是否允许编译器内联callTheInline()以及编译器对Owner实例存储的了解。

a.v的值在函数callTheInline()的末尾持续存在,而b在此函数的末尾不存在。这有可能使编译器省略某些计算。但是b.v不会在函数的末尾持续存在,后者允许编译器省略计算inlineMe()

答案 1 :(得分:1)

不是。(尤其是当它们只是模板时。)

在变成asm之前先对其进行内联(通常是指编译器内部对数据流的表示形式,通常是某种SSA)。在之后进行了进一步的优化,因此实际的asm取决于内联代码在其内联的点,当然还有args和返回值的作用。

例如,在一个调用站点中未使用输出arg的函数可以优化计算该函数的函数部分。或者,如果其中一个args是一个编译时常量,则可以极大地简化生成的asm。 (例如,if(x<8)可以在内联并不断传播后变成if(false)if(true)。)


在您的情况下,循环之一使用类成员对象,其指针可能指向任何地方。您根本没有使用使用 ClassWithInline::v来显示函数,因此很奇怪,它根本就是一个非静态成员函数,而不仅仅是模板化的自由函数。

但是如果加入ClassWithInline::v,则a.inlineMe(ptr + n);将涉及this.vthis.a.v ,这可能会或可能不会导致内存重叠。编译器不知道,因此必须做出保守的假设,或者发出两个版本的循环并在运行快速或安全版本之前检查是否重叠。这将使自动向量化失败,并且需要更多的存储/重载才能使asm正确,即使在出现别名的情况下。

(这是struct,而不是class,因此这些成员是公开的,此函数的调用者可能在致电我们之前已修改了这些成员。)

但是b.inlineMe(ptr + n)对两个指针都使用this.v,在内联后,编译器可以看到。

涉及的其他内存来自new,已知该内存不会与其他内存重叠。也就是说,任何先前存在的指针都不能指向new[]返回的缓冲区。我认为MSVC可以进行足够的别名分析来解决此问题。但是,鉴于缺乏自动矢量化功能,也许没有。


顺便说一句,同时调用两个指针v会使思考/讨论变得非常混乱。