www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - pragma(inline, true) errors?

reply Steven Schveighoffer <schveiguy gmail.com> writes:
Was diagnosing an issue on raylib-d with inlining. I have found some 
curious behavior.

Observe the following code:

```d
extern(C) pragma(inline, true) static float EaseBounceOut(float t, float 
b, float c, float d)
{
     if ((t/=d) < (1.0f/2.75f))
     {
         return (c*(7.5625f*t*t) + b);
     }
     else if (t < (2.0f/2.75f))
     {
         float postFix = t-=(1.5f/2.75f);
         return (c*(7.5625f*(postFix)*t + 0.75f) + b);
     }
     else if (t < (2.5/2.75))
     {
         float postFix = t-=(2.25f/2.75f);
         return (c*(7.5625f*(postFix)*t + 0.9375f) + b);
     }
     else
     {
         float postFix = t-=(2.625f/2.75f);
         return (c*(7.5625f*(postFix)*t + 0.984375f) + b);
     }
}

void main()
{
     auto f = EaseBounceOut(0.5, 1, 1, 1);
}
```

Here is a relevant run.dlang.io snippet: https://run.dlang.io/is/UEtyE8

Some interesting things about this:

1. With -w, I get no errors.
2. With -wi, I get a warning that the function can be inlined
3. Using ASM view, it appears the function is NOT inlined in either case.

I checked historical compilers, and I found that this behavior stems 
from 2.094. I looked at the changelog, and see this [1].

So I feel like this is a bug, but I'm not sure. I'm assuming the 
intended behavior is to inline at all costs, and error if it cannot. But 
I'm not sure of the intended uses of pragma(inline). I feel like if we 
"fix" this, it will break *a lot* of code.

In general, my thoughts on the pragma(inline) feature are that it is a 
complete failure. It either doesn't do what I want, or it is so fragile 
that I don't want to use it. My only use case is for a shim that should 
never really be emitted into the object file. But D tries to inline not 
just the shim, but every function it calls as well.

-Steve

[1] https://dlang.org/changelog/2.094.0.html#hasAlwaysInlines
Apr 01
next sibling parent reply tsbockman <thomas.bockman gmail.com> writes:
On Thursday, 1 April 2021 at 18:38:45 UTC, Steven Schveighoffer 
wrote:
 Here is a relevant run.dlang.io snippet: 
 https://run.dlang.io/is/UEtyE8

 Some interesting things about this:

 1. With -w, I get no errors.
 2. With -wi, I get a warning that the function can be inlined
 3. Using ASM view, it appears the function is NOT inlined in 
 either case.
DMD's inliner often gets confused by multiple `return` statements. The solution is to either convert to single-`return` style, or use LDC/GDC which do not have this problem. ```D extern(C) pragma(inline, true) static float EaseBounceOut(float t, float b, float c, float d) { float ret; if ((t/=d) < (1.0f/2.75f)) { ret = (c*(7.5625f*t*t) + b); } else if (t < (2.0f/2.75f)) { float postFix = t-=(1.5f/2.75f); ret = (c*(7.5625f*(postFix)*t + 0.75f) + b); } else if (t < (2.5/2.75)) { float postFix = t-=(2.25f/2.75f); ret = (c*(7.5625f*(postFix)*t + 0.9375f) + b); } else { float postFix = t-=(2.625f/2.75f); ret = (c*(7.5625f*(postFix)*t + 0.984375f) + b); } return ret; } ``` (I have no comment on the larger issues that you raised. Also, hurray for rich text formatting!)
Apr 01
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 4/1/21 3:13 PM, tsbockman wrote:
 On Thursday, 1 April 2021 at 18:38:45 UTC, Steven Schveighoffer wrote:
 Here is a relevant run.dlang.io snippet: https://run.dlang.io/is/UEtyE8

 Some interesting things about this:

 1. With -w, I get no errors.
 2. With -wi, I get a warning that the function can be inlined
 3. Using ASM view, it appears the function is NOT inlined in either case.
DMD's inliner often gets confused by multiple `return` statements. The solution is to either convert to single-`return` style, or use LDC/GDC which do not have this problem. ```D extern(C) pragma(inline, true) static float EaseBounceOut(float t, float b, float c, float d) {    float ret;    if ((t/=d) < (1.0f/2.75f))    {        ret = (c*(7.5625f*t*t) + b);    }    else if (t < (2.0f/2.75f))    {        float postFix = t-=(1.5f/2.75f);        ret = (c*(7.5625f*(postFix)*t + 0.75f) + b);    }    else if (t < (2.5/2.75))    {        float postFix = t-=(2.25f/2.75f);        ret = (c*(7.5625f*(postFix)*t + 0.9375f) + b);    }    else    {        float postFix = t-=(2.625f/2.75f);        ret = (c*(7.5625f*(postFix)*t + 0.984375f) + b);    }    return ret; } ```
Yeah, I'm not too concerned with the fact that this can't be inlined, though I probably should just remove that from the lib (looks like that was a recent addition). I'm more concerned with the larger issue that it's not doing what it says it should be doing.
 (I have no comment on the larger issues that you raised. Also, hurray 
 for rich text formatting!)
Grrrr... I want formatting too :(. Vladimir, we need an X-DFormat = markdown header or something I can use in Thunderbird. -Steve
Apr 01
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
Nobody has comments on this? It was not an April fool's joke.

To recap:

pragma(inline, true)

means nothing in the current compiler. Well, it doesn't mean nothing, it 
only means that in the case of configuring the compiler to treat 
warnings as informational-only, you will get an informational warning. 
In the case that warnings are treated as an error, your code always 
compiles, and the function is only inlined based on implementation 
definitions.

Technically, this is according to spec, as it says what the compiler 
does if a pragma(inline, true) function cannot be inlined is 
implementation defined. But it does say "an error message is typical". 
Given that there is only one front end, the typical (and in fact 
universal) behavior now is, do nothing.

-Steve
Apr 02
parent reply Johan Engelen <j j.nl> writes:
On Friday, 2 April 2021 at 14:40:00 UTC, Steven Schveighoffer 
wrote:
 pragma(inline, true)

 means nothing in the current compiler. Well, it doesn't mean 
 nothing, it only means that in the case of configuring the 
 compiler to treat warnings as informational-only, you will get 
 an informational warning. In the case that warnings are treated 
 as an error, your code always compiles, and the function is 
 only inlined based on implementation definitions.

 Technically, this is according to spec, as it says what the 
 compiler does if a pragma(inline, true) function cannot be 
 inlined is implementation defined. But it does say "an error 
 message is typical". Given that there is only one front end, 
 the typical (and in fact universal) behavior now is, do nothing.
I'm pretty sure LDC will _never_ give any warning or error on this pragma. It will almost always inline the function into the caller (I don't know of cases where it can't). I question the value of knowing whether a function was inlined or not. Note that `pragma(inline)` is very different functionality from "not emitting a function to object file", which _is_ useful functionality but there is no method to do that in D that I know of (and should not be called "inline"). cheers, Johan
Apr 02
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 4/2/21 12:27 PM, Johan Engelen wrote:
 On Friday, 2 April 2021 at 14:40:00 UTC, Steven Schveighoffer wrote:
 pragma(inline, true)

 means nothing in the current compiler. Well, it doesn't mean nothing, 
 it only means that in the case of configuring the compiler to treat 
 warnings as informational-only, you will get an informational warning. 
 In the case that warnings are treated as an error, your code always 
 compiles, and the function is only inlined based on implementation 
 definitions.

 Technically, this is according to spec, as it says what the compiler 
 does if a pragma(inline, true) function cannot be inlined is 
 implementation defined. But it does say "an error message is typical". 
 Given that there is only one front end, the typical (and in fact 
 universal) behavior now is, do nothing.
I'm pretty sure LDC will _never_ give any warning or error on this pragma. It will almost always inline the function into the caller (I don't know of cases where it can't).
So technically for LDC it will never encounter this case.
 I question the value of knowing whether a function was inlined or not.
I don't really care about inlining or not, but I'm forced to reckon with this, because I've taken over maintenance of a package that puts "pragma(inline, true):" at the top of a module, and therefore warnings are being spat out. So I want to ask the question, is this a bug? What is the expectation? Should we just change the description to "issue a informational warning in some cases when the function cannot be inlined"? Does LDC make any inlining decisions based on this flag?
 Note that `pragma(inline)` is very different functionality from "not 
 emitting a function to object file", which _is_ useful functionality but 
 there is no method to do that in D that I know of (and should not be 
 called "inline").
Yeah, I'm not necessarily invested in a debate about the feature, I just want to know what it really means, so I can make a decision whether to yank the attribute or not. -Steve
Apr 02
next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Friday, 2 April 2021 at 17:00:35 UTC, Steven Schveighoffer 
wrote:
 [snip]

 I don't really care about inlining or not, but I'm forced to 
 reckon with this, because I've taken over maintenance of a 
 package that puts "pragma(inline, true):" at the top of a 
 module, and therefore warnings are being spat out. So I want to 
 ask the question, is this a bug? What is the expectation? 
 Should we just change the description to "issue a informational 
 warning in some cases when the function cannot be inlined"?
 [snip]
It only does warnings when you use the -wi flag right? Otherwise it's an error. Is there a problem with the error not being generated when you don't have warnings turned on?
Apr 02
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On Friday, 2 April 2021 at 17:25:43 UTC, jmh530 wrote:
 On Friday, 2 April 2021 at 17:00:35 UTC, Steven Schveighoffer 
 wrote:
 [snip]

 I don't really care about inlining or not, but I'm forced to 
 reckon with this, because I've taken over maintenance of a 
 package that puts "pragma(inline, true):" at the top of a 
 module, and therefore warnings are being spat out. So I want 
 to ask the question, is this a bug? What is the expectation? 
 Should we just change the description to "issue a 
 informational warning in some cases when the function cannot 
 be inlined"?
 [snip]
It only does warnings when you use the -wi flag right? Otherwise it's an error. Is there a problem with the error not being generated when you don't have warnings turned on?
Yes that is exactly the case. With -w or no switch there is no error or warning. And no inlining. -Steve
Apr 02
parent jmh530 <john.michael.hall gmail.com> writes:
On Friday, 2 April 2021 at 18:21:59 UTC, Steven Schveighoffer 
wrote:
 [snip]

 Yes that is exactly the case. With -w or no switch there is no 
 error or warning. And no inlining.

 -Steve
So the argument is that it is consistent with the spec, but not with the expected behavior after 2.094.0. For whatever reason, I assumed that the change in 2.094.0 was for the spec too, and not just DMD. A little hard to keep track...
Apr 02
prev sibling parent reply Johan Engelen <j j.nl> writes:
On Friday, 2 April 2021 at 17:00:35 UTC, Steven Schveighoffer 
wrote:
 On 4/2/21 12:27 PM, Johan Engelen wrote:
 On Friday, 2 April 2021 at 14:40:00 UTC, Steven Schveighoffer 
 wrote:
 pragma(inline, true)

 means nothing in the current compiler. Well, it doesn't mean 
 nothing, it only means that in the case of configuring the 
 compiler to treat warnings as informational-only, you will 
 get an informational warning. In the case that warnings are 
 treated as an error, your code always compiles, and the 
 function is only inlined based on implementation definitions.

 Technically, this is according to spec, as it says what the 
 compiler does if a pragma(inline, true) function cannot be 
 inlined is implementation defined. But it does say "an error 
 message is typical". Given that there is only one front end, 
 the typical (and in fact universal) behavior now is, do 
 nothing.
I'm pretty sure LDC will _never_ give any warning or error on this pragma. It will almost always inline the function into the caller (I don't know of cases where it can't).
So technically for LDC it will never encounter this case.
Pretty much. When the function is recursive, it may prevent inlining.
 Does LDC make any inlining decisions based on this flag?
Yes: the flag sets the `alwaysinline` attribute on a function, meaning that it will be inlined whenever possible, also at `-O0`. https://d.godbolt.org/z/GEe8zxrro -Johan
Apr 03
parent reply Max Haughton <maxhaton gmail.com> writes:
On Saturday, 3 April 2021 at 10:32:18 UTC, Johan Engelen wrote:
 On Friday, 2 April 2021 at 17:00:35 UTC, Steven Schveighoffer 
 wrote:
 On 4/2/21 12:27 PM, Johan Engelen wrote:
 On Friday, 2 April 2021 at 14:40:00 UTC, Steven Schveighoffer 
 wrote:
 [...]
I'm pretty sure LDC will _never_ give any warning or error on this pragma. It will almost always inline the function into the caller (I don't know of cases where it can't).
So technically for LDC it will never encounter this case.
Pretty much. When the function is recursive, it may prevent inlining.
 Does LDC make any inlining decisions based on this flag?
Yes: the flag sets the `alwaysinline` attribute on a function, meaning that it will be inlined whenever possible, also at `-O0`. https://d.godbolt.org/z/GEe8zxrro -Johan
LDC will sometimes give up inlining on functions containing naked asm also, but there is a flag to tell LLVM that it's safe. GCC has a similar set of flags, including ` attribute("flatten")` which inlines the called functions rather than the the function in it's caller.
Apr 03
parent reply Johan Engelen <j j.nl> writes:
On Saturday, 3 April 2021 at 12:04:18 UTC, Max Haughton wrote:
 GCC has a similar set of flags, including 
 ` attribute("flatten")` which inlines the called functions 
 rather than the the function in it's caller.
Interesting! Is this useful for anyone? We can implement something close to this fairly easily in LDC, but I'd rather wait until this LLVM PR: https://reviews.llvm.org/D70366 -Johan
Apr 03
parent Max Haughton <maxhaton gmail.com> writes:
On Saturday, 3 April 2021 at 20:29:59 UTC, Johan Engelen wrote:
 On Saturday, 3 April 2021 at 12:04:18 UTC, Max Haughton wrote:
 GCC has a similar set of flags, including 
 ` attribute("flatten")` which inlines the called functions 
 rather than the the function in it's caller.
Interesting! Is this useful for anyone? We can implement something close to this fairly easily in LDC, but I'd rather wait until this LLVM PR: https://reviews.llvm.org/D70366 -Johan
I've used it maybe once or twice in some synthetic benchmarks - I'm not entirely sure, however being able to tune the optimizer (particularly the inliner) at source level without extremely ugly things like in C++ could be a huge win for the language e.g. the backend knows where to spend it's efforts (like in loops), but being able to spend more effort on some functions in hot loops is provably a win because you can see LLVM and GCC give up inlining in some examples on godbolt.
Apr 03