www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - spurious gc allocation

reply Ellery Newcomer <ellery-newcomer utulsa.edu> writes:
hello all.

I have a class member function that essentially looks like this:

ThisNode* _InsertAllBut(int value) {
     ThisNode* node = MallocAllocator.allocate!(ThisNode)(1);
     node.value = value;
     node_count++;
     return node;
}


I compile it on x86_64 and the compiler inserts a gc allocation.

I decompiled it, and it looks like it does this:

   ThisNode* _InsertAllBut(int value) {
       struct Thing {
           typeof(this) thing1;
           ThisNode* thing2;
           int thing3;
       }
       Thing* rbp28 = _d_allocmemory(0x14);
       rbp28.thing1 = this;
       rbp28.thing3 = value;
       if (this == 0) {
           // not wasting my time figuring out _d_assert_msg's calling 
conventions
           r8d = 0x137c;
           rcx = something pointing to "src/multi_index.d";
           rdi = {length associated with rsi};
           rsi = something pointing to "null this";
           rdx = {length associated with rcx};
           _d_assert_msg();
       }
       invariant._d_invariant(this);
       rbp28.thing2 = MallocAllocator.allocate(1);
       rbp28.thing2.value = rbp28.thing3;
       this.nodecount ++;
       return rbp28.thing2;
   }


So. Why the heck is it using heap space for stack space? How the heck am 
I supposed to call this from within a destructor?
Nov 07 2013
parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 08.11.2013 06:19, schrieb Ellery Newcomer:
 hello all.

 I have a class member function that essentially looks like this:

 ThisNode* _InsertAllBut(int value) {
      ThisNode* node = MallocAllocator.allocate!(ThisNode)(1);
      node.value = value;
      node_count++;
      return node;
 }


 I compile it on x86_64 and the compiler inserts a gc allocation.

 I decompiled it, and it looks like it does this:

    ThisNode* _InsertAllBut(int value) {
        struct Thing {
            typeof(this) thing1;
            ThisNode* thing2;
            int thing3;
        }
        Thing* rbp28 = _d_allocmemory(0x14);
        rbp28.thing1 = this;
        rbp28.thing3 = value;
        if (this == 0) {
            // not wasting my time figuring out _d_assert_msg's calling
 conventions
            r8d = 0x137c;
            rcx = something pointing to "src/multi_index.d";
            rdi = {length associated with rsi};
            rsi = something pointing to "null this";
            rdx = {length associated with rcx};
            _d_assert_msg();
        }
        invariant._d_invariant(this);
        rbp28.thing2 = MallocAllocator.allocate(1);
        rbp28.thing2.value = rbp28.thing3;
        this.nodecount ++;
        return rbp28.thing2;
    }


 So. Why the heck is it using heap space for stack space? How the heck am
 I supposed to call this from within a destructor?
The problem is that you define the struct Thing as a inner struct. Inner structs have references to the outer class instance they have been created from. If you want a plain old struct you have to write static struct Thing { ... } Kind Regards Benjamin Thaut
Nov 07 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 11/08/2013 07:12 AM, Benjamin Thaut wrote:
 The problem is that you define the struct Thing as a inner struct.
struct Thing only exists in the decompiled version, not in the original source. So far it looks like a bug to me.
Nov 08 2013
parent reply Ellery Newcomer <ellery-newcomer utulsa.edu> writes:
On 11/08/2013 06:19 AM, Timon Gehr wrote:
 On 11/08/2013 07:12 AM, Benjamin Thaut wrote:
 The problem is that you define the struct Thing as a inner struct.
struct Thing only exists in the decompiled version, not in the original source. So far it looks like a bug to me.
I've reduced it to the following: a.d: class C { void _InsertAllBut(int v) { int* node = null; enum mutable = __traits(compiles, {node.value ;}); } } test.d: import a; void main () { C c = new C(); c._InsertAllBut(1); } compile: dmd test.d a.d order doesn't seem to matter, works with -m32 and -m64, apparently I am running dmd v2.063-devel-e23c785 objdump -d --disassembler-options=intel test | ddemangle shows me ... 0000000000417888 <void a.C._InsertAllBut(int)>: 417888: 55 push rbp 417889: 48 8b ec mov rbp,rsp 41788c: 48 83 ec 38 sub rsp,0x38 417890: 53 push rbx 417891: 48 89 7d f0 mov QWORD PTR [rbp-0x10],rdi 417895: 48 bf 10 00 00 00 00 movabs rdi,0x10 41789c: 00 00 00 41789f: e8 10 22 00 00 call 419ab4 <_d_allocmemory> 4178a4: 48 89 45 e0 mov QWORD PTR [rbp-0x20],rax 4178a8: 48 8b 4d f0 mov rcx,QWORD PTR [rbp-0x10] 4178ac: 48 89 08 mov QWORD PTR [rax],rcx 4178af: 48 85 c9 test rcx,rcx ... can anyone confirm?
Nov 08 2013
next sibling parent reply "lomereiter" <lomereiter gmail.com> writes:
Indeed, disassembly reveals an allocation (with all three 
compilers => it's the front-end which generates this crap).

I guess the compiler incorrectly treats { node.value; } as a 
delegate and copies the node to GC heap.

void foo() {
     int* node = null;
     enum mutable = __traits(compiles, {node.value ;});
}

void main() {
     foo();
}
Nov 09 2013
parent Ellery Newcomer <ellery-newcomer utulsa.edu> writes:
On 11/09/2013 12:35 AM, lomereiter wrote:
 Indeed, disassembly reveals an allocation (with all three compilers =>
 it's the front-end which generates this crap).
ouch.
 I guess the compiler incorrectly treats { node.value; } as a delegate
 and copies the node to GC heap.

 void foo() {
      int* node = null;
      enum mutable = __traits(compiles, {node.value ;});
 }

 void main() {
      foo();
 }
oh, I see, it's the delegate doing that. that's helpful. https://d.puremagic.com/issues/show_bug.cgi?id=11483
Nov 09 2013
prev sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Ellery Newcomer:

 can anyone confirm?
I only see the class instance allocation in the main(). I use two modules, with your code. I compile on Windows 32, using no compilation flags, and I see this asm, with obj2asm: _D1a1C13_InsertAllButMFiZv: push EAX push EAX mov [ESP],0 add ESP,8 ret 4 __Dmain: L0: mov EAX,offset FLAT:_D1a1C7__ClassZ push EAX call near ptr __d_newclass push 1 mov ECX,[EAX] call dword ptr 014h[ECX] xor EAX,EAX add ESP,4 ret _main: L0: mov EAX,offset FLAT:__Dmain push EAX push dword ptr 0Ch[ESP] push dword ptr 0Ch[ESP] call near ptr __d_run_main add ESP,0Ch ret Bye, bearophile
Nov 09 2013