www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 13492] New: Last Postblit call optimization


          Issue ID: 13492
           Summary: Last Postblit call optimization
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: enhancement
          Priority: P1
         Component: DMD
          Assignee: nobody puremagic.com
          Reporter: wazar.leollone yahoo.com

Now r-values aren't postblitted into functions:

struct Foo



void bar(Foo arg);

bar(Foo(42)); //Ok. Do not call postblit, only constructor

However, if bar pass arg forth, redundant postblit calls are possible.

Let see the next example:

We have an AA implementation similar current D AA.

struct AA(Key, Value)
   this(T...)(T args)
      buckets.length = T.length;
      foreach (i; Step2Tuple!(T.length))
            alias key = args[i];
            alias value = args[i+1];
            size_t key_hash = hashOf(key);
            size_t idx = key_hash % T.length;
            auto e = new Entry(key_hash, key, value, 
            buckets[idx] = e;


    private static struct Entry
        size_t hash;
        Key key;
        Value value;
        Entry* next;

    Entry*[] buckets;

Also we have factory method aaLiteral, which constructs our AA (compiler
replaces AA literals with aaLiteral call):

//[key1: value1, key2: value2] -> aaLiteral(Key, Value)(key1, value1, key2,
AA!(Key, Value) aaLiteral(Key, Value, T...)(auto ref T args)
    return AA!(Key, Value)(args);

Now we call aaLiteral with rvalue args:
auto aa = aaLiteral!(Foo, int)(Foo(1), 1, Foo(2), 2);
When Foo(1) passed to aaLiteral, postblit isn't called.
However, when we pass it further (from aaLiteral to AA.__ctor) and (from
AA.__ctor to Entry.__ctor) postblit is called.
Passing Foo by ref can't solve our problem, because anyway we need to do copy
of argument when we are initializing Entry.

Now let see, how compiler calls constructors, postblits and destructors:
When we pass argument to a function, caller function calls postblit for the arg
(except case, when the argument is r-value).
Callee function uses this argument, calls postblit when pass it further, and it
calls dtor before finishing:

void test2(Foo);
Foo global;

struct Bar
   Foo f;

Bar* globalbar;
void test(Foo arg)
    test2(arg); // call arg.__postblit()
    global = arg; // call arg.__postblit()
    globalbar = new Bar(arg); // arg.__postblit()

However we can remove last potblit call and last dtor call, is postblitted
object isn't changed after the last postblit call.

void test(Foo arg)
    test2(arg); // call arg.__postblit()
    global = arg; // call arg.__postblit()
    globalbar = new Bar(arg); // no postblit call
    // no dtor call

This optimization has a pitfalls. If test function passed arg by ref anywhere
or takes pointer to the arg, we should reject this optimization:

void test3(ref Foo arg);
void test4(Foo* arg);
void test(Foo arg)
    test2(arg); // call arg.__postblit()
    test3(arg); //escapes the arg reference
    test4(&arg); //escapes the arg reference
    global = arg; // call arg.__postblit()
    globalbar = new Bar(arg); // we must call arg.__postblit() ...
    // and arg.__dtor()

1. function has a non-ref VarDeclaration (including argument)
2. this VarDeclaration passed to anywhere by value (with postblit call)
3. this VarDeclaration isn't used after last postblit call
4. reference to this VarDeclaration doesn't escape a function (as ref argument,
or as pointer)
then we can remove last postblit call and final destructor call.

This optimization isn't modify the language and can 90% simulate ะก++ r-value
reference (remaining 10% is a copying object to stack instead of pass a

Sep 18 2014