This is an archive of the discontinued LLVM Phabricator instance.

[clang] [MinGW] Add the option -fno-auto-import
ClosedPublic

Authored by mstorsjo on May 8 2019, 2:54 AM.

Details

Summary

In GCC, the .refptr stubs are only generated for x86_64, and only
for code models medium and larger (and medium is the default for
x86_64 since this was introduced). They can be omitted for
projects that are conscious about performance and size, and don't
need automatically importing dll data members, by passing -mcmodel=small.

In Clang/LLVM, such .refptr stubs are generated for any potentially
symbol reference that might end up autoimported. The .refptr stubs
are emitted for three separate reasons:

  • Without .refptr stubs, undefined symbols are mostly referenced with 32 bit wide relocations. If the symbol ends up autoimported from a different DLL, a 32 bit relative offset might not be enough to reference data in a different DLL, depending on runtime loader layout.
  • Without .refptr stubs, the runtime pseudo relocation mechanism will need to temporarily make sections read-write-executable if there are such relocations in the text section
  • On ARM and AArch64, the immediate addressing encoded into instructions isn't in the form of a plain 32 bit relative offset, but is expressed with various bits scattered throughout two instructions - the mingw runtime pseudo relocation mechanism doesn't support updating offsets in that form.

If autoimporting is known not to be needed, the user can now
compile with -fno-auto-import, avoiding the extra overhead of
the .refptr stubs.

However, omitting them is potentially fragile as the code
might still rely on automatically importing some symbol without
the developer knowing. If this happens, linking still usually
will succeed, but users may encounter issues at runtime.

Therefore, if the new option -fno-auto-import is passed to the compiler
when driving linking, it passes the flag --disable-auto-import to
the linker, making sure that no symbols actually are autoimported
when the generated code doesn't expect it.

Diff Detail

Event Timeline

mstorsjo created this revision.May 8 2019, 2:54 AM
Herald added a project: Restricted Project. · View Herald TranscriptMay 8 2019, 2:54 AM
rnk added a comment.May 8 2019, 11:35 AM

I'm a little concerned about overloading the code model this way. Currently we have the levels tiny, small, medium, large. Default is the same as small in every way so far as I'm aware. On COFF, the code models are currently untested, so far as I know, and I've been assuming that any usage of the non-small code model more or less implies ELF, and that it would be reasonable to emit wrong code or a fatal error on such codepaths during LLVM codegen. Maybe we have some use cases for the other code models in MCJIT.

With this change, "default" is now a new level that implies not small, but not medium either. Basically, I'm concerned that if we add this feature under -mcode-model=small, users will play with -mcode-model=medium, and they'll run into LLVM backend fatal errors, crashes, or wrong code. So, before we do this, I'd like to do some testing of COFF with the other code models, and make sure that LLVM does something reasonable in the other models, before we encourage users to use this flag.

Fair enough.

On GCC, where the mingw x86_64 default code model is medium, switching it to small gave a small but not insignificant save in code size (around 9KB on a 1,3 MB DLL). On clang, where the default code model is small, getting rid of the extra refptrs didn't gain more than around 512 bytes on the same DLL. So I think it's fair to say this change isn't really important, especially not with the iffy handling of code model that it requires.

rnk added a comment.May 8 2019, 11:51 AM

Well, I'm curious what meaning GCC ascribes to a medium code model for COFF. Do they generate code to allow PE images larger than 2GB, or is it more like the ELF small code model, where they assume everything can be reached with RIP relative addressing?

In D61670#1495535, @rnk wrote:

Well, I'm curious what meaning GCC ascribes to a medium code model for COFF. Do they generate code to allow PE images larger than 2GB, or is it more like the ELF small code model, where they assume everything can be reached with RIP relative addressing?

Not entirely sure TBH (and browsing GCC code to find out isn't the easiest thing). The strange thing is that when outputting assembly with -S, gcc produces the exact same thing regardless of the -mcmodel parameter, even if the emitted object file is vastly different.

Any update on this? The Wine project would like to omit the refptr symbols, see https://gitlab.winehq.org/wine/wine/-/merge_requests/3109

Herald added a project: Restricted Project. · View Herald TranscriptJun 23 2023, 11:08 AM

I'm looking to pick this up again - hopefully @rnk has time to discuss what would be a good way forward.

So taking it from the top; both GCC and Clang generate .refptr stubs when referencing variables that might be autoimported. The exact circumstances differ a little bit though.

In GCC this is done by making GCC for Windows/x86_64 default to a medium code model, and under this code model, references to variables without a visible definition get indirection via a .refptr stub. This doesn't happen for i386. By building with -mcmodel=small, one can opt out of them.

In Clang, we generate such .refptr stubs with similar heuristics (variables without a visible definition), but we do it for all architectures. There are three different reasons for wanting to do this:

  • Normally, when code references a variable (in the default code model), it is done with a 32 bit relative (or absolute) relocation. If the variable turns out to end up imported from a different DLL, in a 64 bit address space it may end up loaded too far away for a 32 bit relocation.
  • If we don't use a .refptr stub, the mingw runtime pseudo relocation mechanism needs to patch the address at runtime. If this is referenced in a code section, it means that the code section needs to be mapped RWX while patching it. Changing the memory protection to RWX is generally undesireable (and is disallowed outright, within the UWP app model).
  • The runtime pseudo relocation format works on N-bit relative or absolute addresses. With the x86 instruction encoding, this turns out to work fine - an instruction that refers to a different memory location generally is a couple bytes of instruction prefix, followed by a 32 bit relative or absolute address, easily patchable. In the case of ARM and AArch64, there are no such instructions, and loading e.g. a 32 bit immediate constant is often done by a pair of instructions, with the actual payload bits scattered throughout the instructions. The runtime pseudo relocation format obviously doesn't support patching this.

Therefore, with Clang we generate .refptr stubs on all architectures. However it would be good to be able to opt out from them. When the variables actually end up autoimported, LLD has got a couple neat tricks that makes the actual pseudo relocations go away in most cases (when it can alias the .refptr.variable stub with the __imp_variable IAT entry). But for the cases when the variable isn't autoimported, the .refptr stub has to be kept, and it produces larger/slower code and more data than necessary. And for low level projects like Wine, it might be desireable to be able to tune exactly what is done.

So within the Clang context, it is not entirely about range (where the code model might be a reasonable fit), but about whether to be prepared for autoimports or not.

Within Clang, it is handled by marking variables we know are in the same DLL as dso_local, while variables that might be autoimported don't get that flag. Within LLVM, references to variables that aren't marked dso_local get indirection via a .refptr stub.

Possible ways of handling it would be to invent a new flag for this purpose; e.g. -fno-autoimport (with the corresponding -fautoimport meaning the default mode). That makes the use fairly clear, but its role is also a bit unclear; there are linker flags --disable-auto-import and --disable-runtime-pseudo-reloc which disable either autoimports of all sorts, or only autoimports that end up with an actual pseudo reloc (allowing the zero-cost ones that are aliased to IAT entries).

If we make the flag only affect code generation, having it named -fno-autoimport when the linker still may do something different (ending up with a pseudo relocation in executable code, which possibly still works fine, just less elegant) is unclear. If we on the other hand make the same flag imply either of --disable-auto-import or --disable-runtime-pseudo-reloc, then that name is rather clear.

If we only make it affect code generation, something like -fdso-local or -fno-refptr might be more precise.

If we make the flag imply linker options, then it becomes much clearer to spot the error, if you enabled this but the code base still would need autoimports somewhere. (This has happened - see https://code.videolan.org/videolan/dav1d/-/merge_requests/1303#note_301859, https://code.videolan.org/videolan/dav1d/-/merge_requests/1349 and https://code.videolan.org/videolan/dav1d/-/merge_requests/1361.) If we make the flag only affect code generation, it becomes a more clear match for projects using -mcmodel=small with GCC today.

I'm open for opinions on how to name these options, @alvinhochun @mati865 @jeremyd2019 and @jacek. Also requesting early guidance from @aaron.ballman.

If we make the flag imply linker options, then it becomes much clearer to spot the error, if you enabled this but the code base still would need autoimports somewhere. (This has happened - see https://code.videolan.org/videolan/dav1d/-/merge_requests/1303#note_301859, https://code.videolan.org/videolan/dav1d/-/merge_requests/1349 and https://code.videolan.org/videolan/dav1d/-/merge_requests/1361.) If we make the flag only affect code generation, it becomes a more clear match for projects using -mcmodel=small with GCC today.

LLVM already differs quite a bit from GNU in more "visible" parts like TLS so I'm don't see compelling reason to degrade user's experience just to be more compatible.
-fno-autoimport sounds fine for me.

jacek added a comment.Jul 25 2023, 6:54 AM

-fno-autoimport sounds good to me. For linker implications, using --disable-auto-import in this case would seem consistent to me.

mstorsjo updated this revision to Diff 551957.Aug 21 2023, 3:24 AM

Updated as discussed, adding a new option -fno-autoimport (and the corresponding positive one, -fautoimport, which is the implicit default), which affects code generation and linking.

The existing linker option --disable-auto-import (which is passed if this new option is set), splits "auto import" into two words - should the new Clang option try to harmonize with this spelling, making it -fno-auto-import instead?

mstorsjo retitled this revision from [RFC] [MinGW] Allow opting out from .refptr stubs to [clang] [MinGW] Add the option -fno-autoimport.Aug 21 2023, 3:27 AM
mstorsjo edited the summary of this revision. (Show Details)
mstorsjo added inline comments.Aug 21 2023, 3:34 AM
clang/include/clang/Driver/Options.td
1480

I see that most similar BoolFOptions for features that are DefaultTrue only document the NegFlag, which makes only the negative flag show up in clang --help. This is probably what one most commonly wants - but I see that the option ends up undocumented in ClangCommandLineReference.rst unless it has documentation for the PosFlag. So therefore I wrote documentation for both options here, and in a form where the PosFlag docs mostly is intended for the generated reference docs.

rnk accepted this revision.Aug 21 2023, 10:16 AM
rnk added subscribers: aeubanks, jyknight.

lgtm

cc +@aeubanks @jyknight to consider using the code model for this purpose

This revision is now accepted and ready to land.Aug 21 2023, 10:16 AM
In D61670#4604145, @rnk wrote:

cc +@aeubanks @jyknight to consider using the code model for this purpose

Hmm, I don't quite understand this comment; do you suggest that we after all should use the code model for controlling the use of .refptr stubs too - didn't we conclude earlier that it isn't a great fit for that?

rnk added a comment.Aug 21 2023, 12:55 PM
In D61670#4604145, @rnk wrote:

cc +@aeubanks @jyknight to consider using the code model for this purpose

Hmm, I don't quite understand this comment; do you suggest that we after all should use the code model for controlling the use of .refptr stubs too - didn't we conclude earlier that it isn't a great fit for that?

Sorry, let me elaborate. Since the first round of review, Arthur and James have been looking at x86 code models, in particular the medium code model. I guess what I really want to ask is, Arthur and James, do you agree with our decision that the code model should not control the formation of these COFF refptr stubs?

I expected the answer would be "yes", so I said "lgtm" and then phrased my question very awkwardly.

I expected the answer would be "yes", so I said "lgtm" and then phrased my question very awkwardly.

Ah, thanks for the clarification!

Any opinion on the name, -fno-autoimport vs -fno-auto-import, given the existing linker option --disable-auto-import?

I expected the answer would be "yes", so I said "lgtm" and then phrased my question very awkwardly.

Ah, thanks for the clarification!

Any opinion on the name, -fno-autoimport vs -fno-auto-import, given the existing linker option --disable-auto-import?

Is there an official name, "auto-import"/"autoimport"/"auto import"? Since cc1 flags are changeable and this hasn't landed yet, I think it'd be nice to standardize before we commit on a stable name.
If there isn't already a name, I like the dash in "auto-import".

In D61670#4604554, @rnk wrote:
In D61670#4604145, @rnk wrote:

cc +@aeubanks @jyknight to consider using the code model for this purpose

Hmm, I don't quite understand this comment; do you suggest that we after all should use the code model for controlling the use of .refptr stubs too - didn't we conclude earlier that it isn't a great fit for that?

Sorry, let me elaborate. Since the first round of review, Arthur and James have been looking at x86 code models, in particular the medium code model. I guess what I really want to ask is, Arthur and James, do you agree with our decision that the code model should not control the formation of these COFF refptr stubs?

I expected the answer would be "yes", so I said "lgtm" and then phrased my question very awkwardly.

Yeah, at least with my understanding code models shouldn't change whether or not things are dso_local or not. They should only affect the instruction sequence we use to access globals/functions.

I expected the answer would be "yes", so I said "lgtm" and then phrased my question very awkwardly.

Ah, thanks for the clarification!

Any opinion on the name, -fno-autoimport vs -fno-auto-import, given the existing linker option --disable-auto-import?

Is there an official name, "auto-import"/"autoimport"/"auto import"?

Not really, no - it's all very much ad-hoc.

Since cc1 flags are changeable and this hasn't landed yet, I think it'd be nice to standardize before we commit on a stable name.
If there isn't already a name, I like the dash in "auto-import".

Ok, sounds reasonable - I'll change it into that form then.

mstorsjo updated this revision to Diff 553624.Aug 25 2023, 2:16 PM
mstorsjo edited the summary of this revision. (Show Details)

Updated to use the form -fno-auto-import and similar split it into two separate words everywhere.

I don't have all the context here, but seems fine once the commit description is updated with the new spelling

I don't have all the context here, but seems fine once the commit description is updated with the new spelling

Thanks. Yeah I've updated the commit message locally (I wonder if the arc tool can resync the patch description? Although that's becoming irrelevant real soon now anyway.) I'll update the description here anyway, and I'll probably go ahead and push it in a few days if nobody else wants to comment on it.

mstorsjo retitled this revision from [clang] [MinGW] Add the option -fno-autoimport to [clang] [MinGW] Add the option -fno-auto-import.Aug 29 2023, 1:19 PM
mstorsjo edited the summary of this revision. (Show Details)
This revision was automatically updated to reflect the committed changes.