-
-
Notifications
You must be signed in to change notification settings - Fork 607
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix Issue 19570 - pragma(inline) is emitting symbols #9235
Conversation
Thanks for your pull request and interest in making D better, @thewilsonator! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please see CONTRIBUTING.md for more information. If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment. Bugzilla references
|
Interesting that this causes link errors... as if things are depending on external inline functions. I think as a first step to resist breakage, we should probably emit the functions as usual, but only if the function is actually referenced... |
Bummer, although I can't say I'm surprised. |
I feel like every error emit here is a bug... |
I question the validity of this as a bug, let alone a critical bug. I posted the reasons to bugzilla. Please continue discussion about that there, the discussion here should be about problems with the PR itself, not the validity of the issue. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the reported issue is a bug.
Also:
There should only be a symbol in the object file in the case it failed to inline, and only if it's actually called.
What about separate compilation ? If it's a non-templated function, the compiler assumes it was emitted when the module was compiled.
This is the kind of optimization that is better fitted for link time IMHO.
@@ -309,7 +309,8 @@ void toObjFile(Dsymbol ds, bool multiobj) | |||
override void visit(FuncDeclaration fd) | |||
{ | |||
// in glue.c | |||
FuncDeclaration_toObjFile(fd, multiobj); | |||
if (fd.inlining != PINLINE.always) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if i want to always inline excepted that one time where i take its address as a function pointer ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you take the address of an inline function, you emit it locally. You certainly don't extern to an inline...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm really confused here... what do people expect inline to do?
Isn't this also how templates work?
It's not "a non-templated function", it's a
This isn't an optimisation, it's a pragma that controls how and where code is to be emitted. |
The semantic I'm going for in gdc is same as C |
@ibuclaw I think that implements the proper semantic effect. If C implements what you describe, then I can confirm that implementation strategy works as expected: maevans@MAEVANS2-W10:~$ cat test.cpp
inline int x() { return 10; }
int y() { return 20; }
maevans@MAEVANS2-W10:~$ gcc -S test.cpp
maevans@MAEVANS2-W10:~$ cat test.s
.file "test.cpp"
.text
.globl _Z1yv
.type _Z1yv, @function
_Z1yv:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $20, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size _Z1yv, .-_Z1yv
.ident "GCC: (Ubuntu 7.3.0-16ubuntu3) 7.3.0"
.section .note.GNU-stack,"",@progbits This is as expected, |
Ah actually, thinking on it, I don't know if the C implementation applies to D verbatim. Difference appears when you have cross-module referencing. In C, In D, if you have an inline in module Is that how GDC works? |
lib.d
-----
module lib;
pragma(inline, true) int fun() { return 10; } main.d
------
import lib;
int main() { return fun(); } > gdc main.d Do I get a link error? I expect to NOT get a link error, because the inline imported from C implicitly implements this semantic in conjunction with |
I guess you said "regardless of which module it comes from", so that implies that you DO emit a copy into the calling module? Maybe ignore everything I said? ;) |
I successfully fetched gdc from the ubuntu package manager, so I ran that test on my machine, it's not correct: maevans@MAEVANS2-W10:~$ gdc --version
gdc (Ubuntu 8-20180414-1ubuntu2) 8.0.1 20180414 (experimental) [trunk revision 259383]
maevans@MAEVANS2-W10:~$ gdc main.d -S && cat main.s | grep fun
.type main, @function
.type _Dmain, @function
call _D3lib3funFZi@PLT ; <- call to extern... but where's the definition?
.type gdc.dso_ctor, @function
.type gdc.dso_dtor, @function
.type _GLOBAL__I_4main, @function
.type _GLOBAL__D_4main, @function Compiling maevans@MAEVANS2-W10:~$ gdc main.d
/tmp/ccc0qOXR.o: In function `_Dmain':
main.d:(.text+0x2e): undefined reference to `_D3lib3funFZi'
collect2: error: ld returned 1 exit status No surprises there... although disappointing. But I also noticed this, which is wrong: maevans@MAEVANS2-W10:~$ gdc lib.d -S && cat lib.s | grep fun
.globl _D3lib3funFZi
.type _D3lib3funFZi, @function
_D3lib3funFZi:
.size _D3lib3funFZi, .-_D3lib3funFZi
.type gdc.dso_ctor, @function
.type gdc.dso_dtor, @function
.type _GLOBAL__I_3lib, @function
.type _GLOBAL__D_3lib, @function Compiling TL;DR, inline doesn't work... it doesn't really do anything at all. |
lib.d
-----
module lib;
pragma(inline, true) int fun() { return 10; } main.d
------
import lib;
int main() { return fun(); }
The odd thing is that with the Otherwise not sure if C/C++ should be followed for inlining, part of the reason they need to be inlined in such as way is the way C++ does headers. Took a look at what rust does and I found you don't even have imported libraries in that sense, it is handled very differently. Anyways when you generate a header for the lib function above you get the following output without the body, which is odd. // D import file generated from 'lib.d'
module lib;
pragma (inline, true)int fun(); |
Your example works by hiding the issue. All the semantic issues I mentioned are excluded from your example above, you don't emit any object code with symbols in the wrong places. My examples above show improper placement in object files. |
It was your example, it illustrates there's a problem when not using For generating code in each separate module that's what I'm saying might not be a good idea to follow in C++'s footsteps. Inline in C++ exists the way it does because there are no concepts of modules. I've been trying to find a modern language that has inlining that works in the same way that C/C++ does, but I just can't find one. It seems like that kind of behavior is an artifact of having header and source files, not just a single source file like virtually every modern language. And like I said with rust, you can't even create an object file from a single source file. It has to have the entire source available to it and it compiles everything to the object file. Which removes this problem of inlining, but obviously you have to compile the entire source. So their solution is to use things like crates to separate their code. |
Well you changed my example by removing everything that my example was about (compiling with I don't see this as 'what C++ does', because C++ has text includes, and doesn't have modules.
I think it's an artifact of having object files and a linker. WRT Rust; yes, you can see their solution is that they do whole-program compilation. And if they didn't do that, I'm certain that their inline would behave the way I'm expecting, because there's no other reasonable way for it to behave. I don't think there's actually choices here; there's literally only one solution that works, unless you start to talk about changing D so that it must do whole program compilation, and that we don't emit I'm also not sure why this is even a problem. Are there any issues that arise from the obvious solution? I can't think of any. It really seems trivial; |
FYI: gcc and clang don't emit the symbol with optimizations if a function is used inline only. cl does (as a COMDAT). |
Yes, it may not be the problem you illustrated anymore, but it is part of the problem that is blocking this pull request from passing.
The way it is implemented in C/C++ is as a result of there simply being no other alternative. #ifndef SOME_NASTY_HEADER_GUARD_H
#define SOME_NASTY_HEADER_GUARD_H
inline int foo() {
return 10;
}
#endif In the above example, what anchor point could be used as a means so that C++ knows to put the actual generated code into the object file and expose the symbol for the function? There's nothing it can use, this is their problem. Their solution to this problem is that they generate code and a symbol for each translation unit as a workaround. Now looking at D: module noguardyay;
pragma(inline, true) void foo() {
return 10;
} We know the function is part of the module Anyways back to the problem at hand. You are trying to stop the C++ symbol from being included as part of phobos, so that it can still be used without having to link to the C++ runtime. You can just use a template for that, it does pretty much the exact same thing. No symbol or code generation happens unless it is instigated by being called somewhere. The other issue you were having with nothrow is more or less D's mistake in allowing you to define two functions that only different because of the
I'm not so sure, though that's more in the fact that they probably won't ever implement it that way in the first place. But anyways I still can't find any other language that does, it seems to be more or less unique to C/C++. I've given up on my search, I'd be welcome to be proven wrong though :P.
What if there's an embedded global variable inside? Should that be followed with C++ as well? If we do follow C++ then depending on how it is compiled those types will reference different global variables. It then just becomes much more complicated with different behavior based on how it is compiled, and down the rabbit hole we go... |
Right, it is at the discretion of the caller whether to emit a symbol to the local CU or not. If they inline it and don't emit a symbol, that is fine and proper. |
I mean, the thing you say is true, but I don't think the reason is necessary to explain it; it's just that it naturally works out that way, and in C++, it happens to match the conceptual model you suggest.
** something to anchor the function declaration to. This is in the world of the languages AST, and not very relevant to the conversation.
Right... and that's the whole problem here!
I'm not sure what C++ has to do with this behaviour. It's a different language with a totally different thing going on in this respect. Although the output is indeed the same, a flat symbol table in an object file that gets collated by the linker. Even if this could be done in C++, why would you do it? It would be broken, just like it is in D!
Right, and this is fine, assuming the inlining does take place. It's at the discretion of the compiler to actually inline a thing. If it chooses not to, it needs to emit the symbol.
This point is weird... it has nothing to do with C++, I'm not sure why you make that leap? This is an artifact of the compiler choosing not to inline the function. It's at the discretion of the compiler to inline or not, and if not, it may choose to emit a hard copy locally. There are reasons why this may occur; perhaps you take the address of the function? Heuristics determine that it's a disadvantage to inline it, etc. I don't see how or why language has anything to say about this... there's no other arrangement I can imagine that works.
It's just one problem that the design manifests in this case. In this case, we certainly don't want a hard-reference to a foreign library in druntime, which leads to a guaranteed link error because code that makes a hard-call to a lib appears in the lib, and the linker wants to resolve the symbol. This is a trivial wrapper, it should be inline, and the code should be generated only if it's ever called.
Right, that's the lame workaround because Why use a hacky workaround, when inline should just do what's written on the tin? What's a disadvantage to fixing this? It will just make certain classes of problem better. I'm pretty sure there are no disadvantages.
What other languages have you considered? Pascal certainly works this way. I tested Crystal, that does too. I struggled to make Go and Nim just compile a single source file into a
Do we support inline variables? I think we don't. That's a tangent conversation.
We're not talking about C++.
We're not talking about global variables. It's hard to know what an inline 'global' variable even means? |
It's a matter of where the function generated code should be placed so that it gets linked to correctly. If it can only be generated in one place then you don't have to worry about there being two of the functions. In C++ you can easily define the same function in multiple source files and get a linker error. Doing that in D isn't really possible and that in part is because of the module system. The structure of the language prevents what would otherwise be a linker error.
I'd be interested to see some examples of these.
For the most part it does the same thing. A template instantiation would be similar to what you get with code being generated per module, and is only generated when it is used. What are you doing with it other than calling the function that makes it undesirable?
If there was even an inline feature in C++ that did that, rather than __inline,
Pascal is almost as old as C++, same goes for Fortran. Kind of odd it doesn't have an inline feature.
That's what I've found too, modern native languages are doing away with .o files because of the problems they cause.
You don't get a linker error in C++ though. // foo.h
inline int& foo()
{
static int value;
return value;
} If you use this header file in |
I'm going to close this due to lack of activity. The PR is linked in the bug report so anyone that wishes to continue work on this PR can get it from there. |
cc @TurkeyMan