digitalmars.D - How does inlining work for ref parameters?
- Janice Caron (55/55) May 17 2008 I get how inlining works for non-ref parameters. If I do
- Bill Baxter (16/75) May 17 2008 IANACG, but a reference parameter is pretty much like passing an alias
- downs (20/25) May 19 2008 FWIW, GDC does inline ref-arg functions.
- Sean Kelly (4/28) May 19 2008 It apparently inlines functions containing asm blocks as well. Score
- Frits van Bommel (10/32) May 21 2008 Not necessarily. Inlining just 'rtest' will produce the same ESP value
- Sean Kelly (6/40) May 21 2008 Yes, I remember the same thing. I had thought that perhaps GDC
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 = ≈ 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
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 = ≈ 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
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
== Quote from downs (default_357-line yahoo.de)'s articleBill Baxter wrote:It apparently inlines functions containing asm blocks as well. Score two points for GDC. SeanSadly, 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
May 19 2008
Sean Kelly wrote:== Quote from downs (default_357-line yahoo.de)'s articleNot 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.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: BFC57840It apparently inlines functions containing asm blocks as well. Score two points for GDC.
May 21 2008
Frits van Bommel wrote:Sean Kelly wrote:Oops, you're right.== Quote from downs (default_357-line yahoo.de)'s articleNot 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).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: BFC57840It apparently inlines functions containing asm blocks as well. Score two points for 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.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