June 09, 2020
On Monday, 8 June 2020 at 23:19:55 UTC, Jonathan M Davis wrote:
> On Monday, June 8, 2020 8:09:04 AM MDT Manu via Digitalmars-d wrote:
>> On Mon, Jun 8, 2020 at 8:20 PM Walter Bright via Digitalmars-d <
>> > C/C++ inline has always been a hint to the compiler, not a command.
>>
>> It's not a hint at all. It's a mechanical tool; it marks symbols with internal linkage, and it also doesn't emit them if it's never referenced. The compiler may not choose to ignore that behaviour, it's absolutely necessary, and very important.
>
> It is my understanding that in C++, inline is a hint to the compiler with regards to whether a particular function call is actually inlined.

That's a common misconception, and one that exists due to that being its original intended purpose. But nowawadays? Nope:

https://en.cppreference.com/w/cpp/language/inline

June 09, 2020
On Tuesday, 9 June 2020 at 10:19:44 UTC, Dukc wrote:
> On Tuesday, 9 June 2020 at 00:36:18 UTC, Manu wrote:
>> for instance, if you have a leaf function (does not allocate any stack memory), it's only possible to make calls from that function where the callee is inlined... and if inlining fails, your caller will lose its no-stack-frame requirement.
>
> Out of interest: What does implementing a function without a stack frame enable?

Better predictability of memory access patterns for example.
Knowing that you can't have a stack overflow.

June 09, 2020
On Tuesday, 9 June 2020 at 09:29:47 UTC, Walter Bright wrote:
> On 6/8/2020 7:09 AM, Manu wrote:
>> [...]
> The C/C++ inline semantics revolve around the mechanics of .h files because it doesn't have modules. These reasons are irrelevant for D.
>
> > [...]
>
> For .h files, sure. Why for D, though?
>
>> [...]
>
> Why are 0 or N copies fine, but 1 is not?
>
>
>> [...]
>
> Why? What problem are you solving?
>
>
>> [...]
>
> I know I'm being boring, but why is it important? Also, check out -gc-sections:
>
> https://gcc.gnu.org/onlinedocs/gnat_ugn/Compilation-options.html
>
> Which is a general solution, not a kludge.
>
>
>> [...]
>
> Executables (on virtual memory systems) are not loaded into memory and then run. They are memory-mapped into memory, and then pages are read off of disk on demand. Unmapped code consumes neither memory nor resources.
>
>
>> [...]
>
> The default, and pragma(inline,true) are sufficient for all use cases except which ones?
>
>> [...]
>
> You won't get a sea of warnings unless you put pragma(inline,true) on a sea of functions that can't be inlined.
>
>> [...]
>
> Why?
>
>
>> [...]
>
> Inlining is 100% about optimization.

You forgot the case where it is about pessimization :-)

The thing with inlining is that it often looks good in benchmarks but not that good in whole applications because of the impact it can have on code size.
In my modest experience, the worse offenders where always the people that insisted very much on forcing inlining. I had once a colleague that had put inlines everywhere in his lib without realizing that nesting inlined functions has a multiplying effect. I calculated the equivalent in line of codes that were inlined and it was around 6000, for a library that had only 13000 lines to begin with.
The effect on the instruction cache was huge.

>
>
>> [...]
>
> Optimizing things that don't matter is wasting your valuable time. Optimizing things that are more effectively and thoroughly done with the linker (-gc-sections) - it's like chipping wood with a hatchet rather than a woodchipper.

June 10, 2020
On Tue, Jun 9, 2020 at 7:30 PM Walter Bright via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On 6/8/2020 7:09 AM, Manu wrote:
> > On Mon, Jun 8, 2020 at 8:20 PM Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com <mailto:digitalmars-d@puremagic.com>>
> wrote:
> >
> >     On 6/7/2020 11:14 PM, Manu wrote:
> >      > I think a first part of the conversation to understand, is that
> since D
> >     doesn't
> >      > really have first-class `inline` (just a pragma, assumed to be
> low-level
> >      > compiler control), I think most people bring their conceptual
> definition
> >     over
> >      > from C/C++, and that definition is a little odd (although it is
> immensely
> >      > useful), but it's not like what D does.
> >
> >     C/C++ inline has always been a hint to the compiler, not a command.
> >
> >
> > It's not a hint at all. It's a mechanical tool; it marks symbols with
> internal
> > linkage, and it also doesn't emit them if it's never referenced. The compiler may not choose to ignore that behaviour,
> The C/C++ inline semantics revolve around the mechanics of .h files
> because it
> doesn't have modules. These reasons are irrelevant for D.
>

That's completely incorrect. It's 100% as relevant for D as it is for C++
for exactly the same reasons.
You'll need to support your claim.

 > it's absolutely necessary, and very important.
>
> For .h files, sure. Why for D, though?
>

Because in this category of use case, inlining is a concept related to
native languages with binary linkage, and not really anything to do with
the language specifically.
Calling an inline function from a foreign module does not require that I
link that module's compiled code, because the inline function is emit
locally to the calling CU.
This is just as relevant in D as it is in C++, although the technical
manifestations are slightly different; in C++, a function defined in a
header emits a hard-copy of the function in each CU, and link errors
because multiple defined symbol. In D, you end up with no copy of the
function anywhere, and link errors because undefined symbol. `Inline`
addresses that same issue in both cases the same way.

There are a whole lot of reasons this comes up in binary ecosystems. Build
systems and dev tooling is a really complex topic, which tends to take
years and years of iteration for millions-loc software, and there are
frequently awkward requirements for various reasons. We need to fit in with
existing expectations.
It must be painless to plug into existing native code ecosystems; we have
invested heavily in extern C and C++, but compatibility with those
ecosystems also involves integrating into complex existing legacy build,
link, and distribution environments.

>     Why does it matter where it is emitted? Why would you want multiple
> copies of
> >     the same function in the binary?
> > I want zero copies if it's never called. That's very important.
>
> Why are 0 or N copies fine, but 1 is not?
>

No, that's not what I've said; I expect exactly N copies of x() for N CU's
which reference x(). And they should appropriately be marked with internal
linkage.
Any other result is just 'weird', and while it might be workable, it's just
asking for trouble. (1 redundant copy in the owning CU despite being
un-referenced MIGHT be link-stripped if the surrounding tooling all works
as we hope... but it also might not, as I have demonstrated on multiple
occasions)
There's just no reason to invite this problem... and no advantage.

> I also want copies to appear locally when it is referenced; inline functions
> > should NOT require that you link something to get the code... that's not
> inline
> > at all.
>
> Why? What problem are you solving?
>

Literally inline function calling. No-link libs are a really common and extremely useful thing.

>>     Why? What is the problem with the emission of one copy where it was
> defined?
> > That's the antithesis of inline. If I wanted that, I wouldn't mark it
> inline.
> > I don't want a binary full of code that shouldn't be there. It's very
> important
> > to be able to control what code is in your binaries.
>
> I know I'm being boring, but why is it important? Also, check out -gc-sections:
>
> https://gcc.gnu.org/onlinedocs/gnat_ugn/Compilation-options.html
>
> Which is a general solution, not a kludge.
>

I guess my key issue is that I have complained before because I have experienced multiple counts of the link stripping not working like you say. There is no reason to invite that problem; we can trivially eliminate the problem at the source. I don't care if it's fixable, I don't want the problem to exist. NOBODY wants to be tasked to solve awkward build ecosystem issues... we already have a working build ecosystem, and this D thing is making it harder than it already is. That's a really bad thing, and I would entertain excuses for this if not for the fact that it's trivially avoidable. We do not need to waste anybodies time this way; we won't win any friends by wasting their time with problems they HATE to have.

The secondary issue is, I want my binaries to contain what I put in there, and not what I don't. Rogue symbols that I specified shouldn't exist only bloat the binary, invite possibility of link collision, and raise the probability of the issues I mentioned above.

>>     The PR I have on this makes it an informational warning. You can
> choose to be
> >>     notified if inlining fails.
> > That's not sufficient though for all use cases. This is a different kind
> of
> > inline (I think it's 'force inline').
>
> The default, and pragma(inline,true) are sufficient for all use cases
> except
> which ones?
>

Recursive calls or taking the address of functions (and probably other
situations) are incompatible with a hard-error based inline.
CU-inline is the overwhelmingly common case.
Absolutely-must-call-site-inline is extremely rare by contrast, but very
important in the rare instance it's necessary.

I suggest, the default should model the common case, and the rare niche case can be the 3rd 'force' state by explicit request.

> This #3 mechanic is rare, and #1/2 are overwhelmingly common. You don't want a
> > sea of warnings to apply to cases of 1/2.
>
> You won't get a sea of warnings unless you put pragma(inline,true) on a
> sea of
> functions that can't be inlined.
>

There are classes of modules where EVERY function is inline. No-link libs are a very common and useful tool.

> I think it's important to be able to distinguish #3 from the other 2 cases.
>
> Why?
>

Because they have different use cases; the guarantees in #3 are critical
when required, and absolutely not desired in cases of #1-2.
The C++ design which only satisfies #1-2 leaves #3 in hope-for-the-best
territory, which you need to manually validate, and then have no way to
detect when the conditions or context changes.
If we only have one, it should model #1-2, but I think it's useful to model
#3 in addition; that would helpfully improve the reliability of code that
depends on #3.
And it's kinda what we tried to model 9 years ago, except we lost #1-2
along the way...

>>   At its root, inlining is an optimization, like deciding which
> variables go into
> >>   registers.
> > No, actually... it's not. It's not an 'optimisation' in any case except
> maaaaybe
> > #2; it's about control of the binary output and code generation.
>
> Inlining is 100% about optimization.
>

No. I feel like I couldn't make this case clearer... It's got almost
nothing to do with optimisation, it's about codegen control. In the very
rare event that I disagree with an optimisers heuristic, it's nice to have
an override hint, but that's like a 0.1% use case. Inline provides
mechanical control over codegen, and this is a native systems language. We
must have that control.
GDC/LDC have it, but it really should be specced to have uniform behaviour
across D compilers. We don't have macro's like C++ does to wrangle cases
where implementations differ as easily.

> Low level control of code generation is important in native languages; that's
> > why we're here.
>
> Optimizing things that don't matter is wasting your valuable time.
> Optimizing
> things that are more effectively and thoroughly done with the linker
> (-gc-sections) - it's like chipping wood with a hatchet rather than a
> woodchipper.
>

This isn't about optimisation, it's about controlling the output of the compiler. Taking that control away and forcing us to try and reproduce that expected functionality with external tooling within a deeply complex build ecosystem is wasting our valuable time.


June 10, 2020
On Tue, Jun 9, 2020 at 8:20 PM Dukc via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Tuesday, 9 June 2020 at 00:36:18 UTC, Manu wrote:
> > for instance, if you have a leaf function (does not allocate any stack memory), it's only possible to make calls from that function where the callee is inlined... and if inlining fails, your caller will lose its no-stack-frame requirement.
>
> Out of interest: What does implementing a function without a stack frame enable?
>

Leaf functions have a massive performance advantage, and in certain
situations, functions can run in a zero-ram or zero-stack environment;
micro controllers, within scheduler implementations while performing
operations like context switching, come constructs of hardware exception
handling, etc.
The most common case is maintaining a leaf function's leaf-ness.


June 09, 2020
On Tuesday, 9 June 2020 at 09:29:47 UTC, Walter Bright wrote:
> On 6/8/2020 7:09 AM, Manu wrote:
>> On Mon, Jun 8, 2020 at 8:20 PM Walter Bright via Digitalmars-d It's not a hint at all. It's a mechanical tool; it marks symbols with internal linkage, and it also doesn't emit them if it's never referenced.
>> The compiler may not choose to ignore that behaviour,
> The C/C++ inline semantics revolve around the mechanics of .h files because it doesn't have modules. These reasons are irrelevant for D.
>
> > it's absolutely necessary, and very important.
>
> For .h files, sure. Why for D, though?

The same is true for .di files. There's no option for the compiler to tell the compiler to include the source of a function for the header files in D. So there's no chance for it to inline. I know .di files aren't used much, but they are still supported nonetheless.

Note: This is different than the argument Manu is making, but it is as relevant for D as it is for C++ for the compiler to actually be able to inline the source.



June 09, 2020
On 6/9/2020 4:31 AM, Atila Neves wrote:
> On Monday, 8 June 2020 at 23:19:55 UTC, Jonathan M Davis wrote:
>> On Monday, June 8, 2020 8:09:04 AM MDT Manu via Digitalmars-d wrote:
>>> On Mon, Jun 8, 2020 at 8:20 PM Walter Bright via Digitalmars-d <
>>> > C/C++ inline has always been a hint to the compiler, not a > command.
>>>
>>> It's not a hint at all. It's a mechanical tool; it marks symbols with internal linkage, and it also doesn't emit them if it's never referenced. The compiler may not choose to ignore that behaviour, it's absolutely necessary, and very important.
>>
>> It is my understanding that in C++, inline is a hint to the compiler with regards to whether a particular function call is actually inlined.
> 
> That's a common misconception, and one that exists due to that being its original intended purpose. But nowawadays? Nope:
> 
> https://en.cppreference.com/w/cpp/language/inline


"Since this meaning of the keyword inline is non-binding, compilers are free to use inline substitution for any function that's not marked inline, and are free to generate function calls to any function marked inline. Those optimization choices do not change the rules regarding multiple definitions and shared statics listed above."

I.e. it is a hint.
June 09, 2020
On 6/9/2020 4:30 AM, Stanislav Blinov wrote:
> That is no reason for another [instance of the same] compiler not to have access to symbol table,

Since the linker has this information already, there's no reason to not do the job in the linker.
June 09, 2020
On Tuesday, 9 June 2020 at 21:26:38 UTC, Walter Bright wrote:
> On 6/9/2020 4:30 AM, Stanislav Blinov wrote:
>> That is no reason for another [instance of the same] compiler not to have access to symbol table,
>
> Since the linker has this information already, there's no reason to not do the job in the linker.

So, "linkers have to do it because linkers have to do it". Let's just stick to outdated tech forever.
June 10, 2020
On Wed, Jun 10, 2020 at 7:15 AM Walter Bright via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On 6/9/2020 4:31 AM, Atila Neves wrote:
> > On Monday, 8 June 2020 at 23:19:55 UTC, Jonathan M Davis wrote:
> >> On Monday, June 8, 2020 8:09:04 AM MDT Manu via Digitalmars-d wrote:
> >>> On Mon, Jun 8, 2020 at 8:20 PM Walter Bright via Digitalmars-d <
> >>> > C/C++ inline has always been a hint to the compiler, not a > command.
> >>>
> >>> It's not a hint at all. It's a mechanical tool; it marks symbols with internal linkage, and it also doesn't emit them if it's never
> referenced. The
> >>> compiler may not choose to ignore that behaviour, it's absolutely
> necessary,
> >>> and very important.
> >>
> >> It is my understanding that in C++, inline is a hint to the compiler
> with
> >> regards to whether a particular function call is actually inlined.
> >
> > That's a common misconception, and one that exists due to that being its original intended purpose. But nowawadays? Nope:
> >
> > https://en.cppreference.com/w/cpp/language/inline
>
>
> "Since this meaning of the keyword inline is non-binding, compilers are
> free to
> use inline substitution for any function that's not marked inline, and are
> free
> to generate function calls to any function marked inline. Those
> optimization
> choices do not change the rules regarding multiple definitions and shared
> statics listed above."
>
> I.e. it is a hint.
>

It's like you skipped over ALL OF THE OTHER TEXT, where it details numerous precise behavioural requirements :/