www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - How does inlining work for ref parameters?

reply "Janice Caron" <caron800 googlemail.com> writes:
I get how inlining works for non-ref parameters. If I do

    int foo(int x, int y)
    {
        return x + y;
    }

    int a = ...;
    int b = ...;
    int c = foo(a,b);

Then this can turn into

    int a = ...;
    int b = ...;
        int x = a;
        int y = b;
        int t = a + b;
    int c = t;

which can subsequently be optimised to

    int a = ...;
    int b = ...;
    int c = a + b;

But I don't get how it all hangs together with reference arguments.
Here's an example:

    int bar(ref int* p, ref int* q)
    {
        return *p++ + *q++;
    }

    int* ap = ...;
    int* bp = ...;
    int c = bar(ap,bp);

My complete lack of understanding of how inlining works suggests to me
that this would translate into

    int* ap = ...;
    int* bp = ...;
        int** p = &ap;
        int** q = &bp;
        int t = *(*p)++ + *(*q)++;
    int c = t;

which doesn't really optimise, except for the elimination of t. And
yet, we would /hope/ to end up with is:

    int* ap = ...;
    int* bp = ...;
    int c = *ap++ + *bp++;

This can certainly be achieved by another means. Specifically:

    string bar(string r, string p, string q)
    {
        return r~"= *"~p~"++ + *"~q~"++;"
    }

    int* ap = ...;
    int* bp = ...;
    int c;
    mixin(bar("c","ap","bp"));

But to my mind, the code is less readable.

So - can someone tell me - if a function takes lots of parameters
declared "ref", and that function is inlined, is the "ref" part
(passing the address and then dereferencing when needed) completely
eliminated, or not? Does anyone have a definitive answer?
May 17 2008
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Janice Caron wrote:
 I get how inlining works for non-ref parameters. If I do
 
     int foo(int x, int y)
     {
         return x + y;
     }
 
     int a = ...;
     int b = ...;
     int c = foo(a,b);
 
 Then this can turn into
 
     int a = ...;
     int b = ...;
         int x = a;
         int y = b;
         int t = a + b;
     int c = t;
 
 which can subsequently be optimised to
 
     int a = ...;
     int b = ...;
     int c = a + b;
 
 But I don't get how it all hangs together with reference arguments.
 Here's an example:
 
     int bar(ref int* p, ref int* q)
     {
         return *p++ + *q++;
     }
 
     int* ap = ...;
     int* bp = ...;
     int c = bar(ap,bp);
 
 My complete lack of understanding of how inlining works suggests to me
 that this would translate into
 
     int* ap = ...;
     int* bp = ...;
         int** p = &ap;
         int** q = &bp;
         int t = *(*p)++ + *(*q)++;
     int c = t;
 
 which doesn't really optimise, except for the elimination of t. 

IANACG, but a reference parameter is pretty much like passing an alias to the original value, so I'm not sure the optimizer would need to make those intermediate p,q arguments. It can just go straight to the version below where ap and bp are substituted in directly. But either way, inlining does eliminate a function call. I believe that's mostly the point of inlining. That's orthogonal to the other sorts of optimization tricks you can do after the code is inlined.
 And yet, we would /hope/ to end up with is:
 
     int* ap = ...;
     int* bp = ...;
     int c = *ap++ + *bp++;

 [...]

 So - can someone tell me - if a function takes lots of parameters
 declared "ref", and that function is inlined, is the "ref" part
 (passing the address and then dereferencing when needed) completely
 eliminated, or not? Does anyone have a definitive answer?

Sadly, I believe what I have heard mentioned here on the NG is that DMD does *not* currently inline *any* functions that have ref args. Which is a serious problem, since performance is one reason you would switch to ref args in the first place. (To avoid passing large structs by value.) This is heresay, though. Can anyone confirm? It really would be nice to get a "performance tips" page up somewhere describing what sorts of things DMD can and cannot inline. --bb
May 17 2008
parent reply downs <default_357-line yahoo.de> writes:
Bill Baxter wrote:
 Sadly, I believe what I have heard mentioned here on the NG is that DMD
 does *not* currently inline *any* functions that have ref args.  Which
 is a serious problem, since performance is one reason you would switch
 to ref args in the first place.  (To avoid passing large structs by value.)
 

FWIW, GDC does inline ref-arg functions. Proof: gentoo-pc ~ $ cat test42.d && echo "----" && gdc test42.d -o test42 -O3 -frelease && ./test42 module test42; import std.stdio; void test() { void* foo; asm { mov foo, ESP; } writefln("SP: ", foo); } void rtest(ref int x) { x++; test(); } void main() { int i = 0; test(); rtest(i); } ---- SP: BFC57840 SP: BFC57840 --downs
May 19 2008
parent reply Sean Kelly <sean invisibleduck.org> writes:
== Quote from downs (default_357-line yahoo.de)'s article
 Bill Baxter wrote:
 Sadly, I believe what I have heard mentioned here on the NG is that DMD
 does *not* currently inline *any* functions that have ref args.  Which
 is a serious problem, since performance is one reason you would switch
 to ref args in the first place.  (To avoid passing large structs by value.)

Proof: gentoo-pc ~ $ cat test42.d && echo "----" && gdc test42.d -o test42 -O3 -frelease && ./test42 module test42; import std.stdio; void test() { void* foo; asm { mov foo, ESP; } writefln("SP: ", foo); } void rtest(ref int x) { x++; test(); } void main() { int i = 0; test(); rtest(i); } ---- SP: BFC57840 SP: BFC57840

It apparently inlines functions containing asm blocks as well. Score two points for GDC. Sean
May 19 2008
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Sean Kelly wrote:
 == Quote from downs (default_357-line yahoo.de)'s article
 FWIW, GDC does inline ref-arg functions.
 Proof:
 gentoo-pc ~ $ cat test42.d && echo "----" && gdc test42.d -o test42 -O3
-frelease && ./test42
 module test42;
 import std.stdio;
 void test() {
   void* foo; asm { mov foo, ESP; }
   writefln("SP: ", foo);
 }
 void rtest(ref int x) { x++; test(); }
 void main() {
   int i = 0;
   test();
   rtest(i);
 }
 ----
 SP: BFC57840
 SP: BFC57840

It apparently inlines functions containing asm blocks as well. Score two points for GDC.

Not necessarily. Inlining just 'rtest' will produce the same ESP value for both invocations of 'test' as well, even though it's not inlined. (since both invocations of test() have the same stack frame size they get decremented equally from the same base value). To really test that, you'd need to also manually inline test() into main and compare against the other two results. I just tried this, and it seems test() is indeed NOT inlined (at least, on x86-64 with Ubuntu's GDC). I seem to remember gcc's extended asm syntax being claimed to be more inlining-friendly. GDC is supposed to support it, so you could try that.
May 21 2008
parent Sean Kelly <sean invisibleduck.org> writes:
Frits van Bommel wrote:
 Sean Kelly wrote:
 == Quote from downs (default_357-line yahoo.de)'s article
 FWIW, GDC does inline ref-arg functions.
 Proof:
 gentoo-pc ~ $ cat test42.d && echo "----" && gdc test42.d -o test42 
 -O3 -frelease && ./test42
 module test42;
 import std.stdio;
 void test() {
   void* foo; asm { mov foo, ESP; }
   writefln("SP: ", foo);
 }
 void rtest(ref int x) { x++; test(); }
 void main() {
   int i = 0;
   test();
   rtest(i);
 }
 ----
 SP: BFC57840
 SP: BFC57840

It apparently inlines functions containing asm blocks as well. Score two points for GDC.

Not necessarily. Inlining just 'rtest' will produce the same ESP value for both invocations of 'test' as well, even though it's not inlined. (since both invocations of test() have the same stack frame size they get decremented equally from the same base value). To really test that, you'd need to also manually inline test() into main and compare against the other two results. I just tried this, and it seems test() is indeed NOT inlined (at least, on x86-64 with Ubuntu's GDC).

Oops, you're right.
 I seem to remember gcc's extended asm syntax being claimed to be more 
 inlining-friendly. GDC is supposed to support it, so you could try that.

Yes, I remember the same thing. I had thought that perhaps GDC converted the asm code under the covers before GCCs inlining took place, but perhaps not. Sean
May 21 2008