[Issue 13492] New: Last Postblit call optimization
via Digitalmars-d-bugs
digitalmars-d-bugs at puremagic.com
Thu Sep 18 04:09:19 PDT 2014
https://issues.dlang.org/show_bug.cgi?id=13492
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 at puremagic.com
Reporter: wazar.leollone at yahoo.com
Now r-values aren't postblitted into functions:
struct Foo
{
this(int)
{
writeln("ctor");
}
this(this)
{
writeln("postblit");
}
~this()
{
writeln("dtor");
}
}
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,
impl.buckets[idx]);
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,
value2);
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()
//arg.__dtor()
}
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()
}
Resume:
If
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
pointer).
--
More information about the Digitalmars-d-bugs
mailing list