在汇编代码中查找不必要的缓冲区副本

Looking for unnecessary buffer copies in assembly code

本文关键字:缓冲区 副本 不必要 查找 汇编 代码      更新时间:2023-10-16

我正在使用Visual Studio 2008 c++ for Windows Mobile 6 ARMV4I,我正在学习阅读由VS生成的ARM汇编代码,以尽量减少应用程序中不必要的缓冲区副本。因此,我创建了一个测试应用程序,如下所示:

#include <vector>
typedef std::vector< BYTE > Buf;
class Foo
{
public:
    Foo( Buf b ) { b_.swap( b ); };
private:
    Buf b_;
};
Buf Create()
{
    Buf b( 1024 );
    b[ 0 ] = 0x0001;
    return b;
}
int _tmain( int argc, _TCHAR* argv[] )
{
    Foo f( Create() );
    return 0;
}

我想了解Create返回的缓冲区是否在给定给Foo构造函数时被复制,或者编译器是否能够优化该复制。在启用了优化的发布版本中,这会生成如下的程序集:

class Foo
{
public:
    Foo( Buf b ) { b_.swap( b ); };
0001112C  stmdb       sp!, {r4 - r7, lr} 
00011130  mov         r7, r0 
00011134  mov         r3, #0 
00011138  str         r3, this 
0001113C  str         r3, [r7, #4] 
00011140  str         r3, [r7, #8] 
00011144  ldr         r3, this 
00011148  ldr         r2, this 
0001114C  mov         r5, r7 
00011150  mov         r4, r1 
00011154  str         r3, this, #4 
00011158  str         r2, this, #4 
0001115C  mov         r6, r1 
00011160  ldr         r2, this 
00011164  ldr         r3, this 
00011168  mov         lr, r7 
0001116C  str         r3, this 
00011170  str         r2, this 
00011174  ldr         r2, [lr, #8]! 
00011178  ldr         r3, [r6, #8]! 
0001117C  str         r3, this 
00011180  str         r2, this 
00011184  ldr         r3, this 
00011188  movs        r0, r3 
0001118C  beq         |Foo::Foo + 0x84 ( 111b0h )| 
00011190  ldr         r3, [r1, #8] 
00011194  sub         r1, r3, r0 
00011198  cmp         r1, #0x80 
0001119C  bls         |Foo::Foo + 0x80 ( 111ach )| 
000111A0  bl          000112D4 
000111A4  mov         r0, r7 
000111A8  ldmia       sp!, {r4 - r7, pc} 
000111AC  bl          |stlp_std::__node_alloc::_M_deallocate ( 11d2ch )| 
000111B0  mov         r0, r7 
000111B4  ldmia       sp!, {r4 - r7, pc} 
--- ...stlportstl_vector.h -----------------------------
// snip!
--- ...asm_test.cpp
    private:
        Buf b_;
    };
Buf Create()
{
00011240  stmdb       sp!, {r4, lr} 
00011244  mov         r4, r0 
    Buf b( 1024 );
00011248  mov         r1, #1, 22 
0001124C  bl          |    
    b[ 0 ] = 0x0001;
00011250  ldr         r3, [r4] 
00011254  mov         r2, #1 
    return b;
}
int _tmain( int argc, _TCHAR* argv[] )
{
00011264  str         lr, [sp, #-4]! 
00011268  sub         sp, sp, #0x18 
    Foo f( Create() );
0001126C  add         r0, sp, #0xC 
00011270  bl          |Create ( 11240h )| 
00011274  mov         r1, r0 
00011278  add         r0, sp, #0 
0001127C  bl          |Foo::Foo ( 1112ch )| 
    return 0;
00011280  ldr         r0, argc 
00011284  cmp         r0, #0 
00011288  beq         |wmain + 0x44 ( 112a8h )| 
0001128C  ldr         r3, [sp, #8] 
00011290  sub         r1, r3, r0 
00011294  cmp         r1, #0x80 
00011298  bls         |wmain + 0x40 ( 112a4h )| 
0001129C  bl          000112D4 
000112A0  b           |wmain + 0x44 ( 112a8h )| 
000112A4  bl          |stlp_std::__node_alloc::_M_deallocate ( 11d2ch )| 
000112A8  mov         r0, #0 
}

我可以在汇编代码中寻找什么模式,以了解Buf结构被复制的位置?

分析Create相当简单,因为代码很短。这里显然应用了NRVO,因为返回语句没有生成指令,返回值是在r0中就地构造的。

Foo::Foo的按值传递参数进行的复制稍微难以分析,但是在调用CreateFoo::Foo之间很少有代码需要进行复制,并且没有任何代码会对std::vector进行深度复制。所以看起来这个拷贝也被消除了。另一种可能性是Foo::Foo的自定义调用约定,其中参数实际上是通过引用传递的,并在函数内部复制。你需要一个能够更深入地分析ARM汇编的人,我要排除这种可能性。

缓冲区将被复制;你正在使用c++的按值传递语义;没有编译器会为你优化。如何复制取决于std::vector的复制构造函数。