-
Notifications
You must be signed in to change notification settings - Fork 3k
Disable C++ static destructors in ARMC6 compiler #11726
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
Conversation
Static destruction code may still end up in a GCC image despite our efforts. Use of `SingletonPtr` will eliminate most, but references can still occur. For IAR and ARM toolchains they can be totally eliminated in the compiler (see ARMmbed#11726 for ARM). As the Clang attributes have been submitted for C++ standardisation (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1247r0.html), it seems likely that one day GCC will also support this, so it seems not worthwhile to create a `NotDestroyed<T>` template wrapper as a workaround, as I was originally intending. In the meantime, GCC is inserting calls to `__eabi_atexit`. That is stubbed out, so whatever it attempts to register will not be called. We can't stop it putting the calls in, but we can transparently slightly reduce code size by telling it to use `atexit` instead. It needs to pass fewer parameters for us to ignore, so the callsites become a little smaller.
This is just for static destructors correct? At some point it might be nice to have the peripheral API destructors safely disable and release resources associated with the peripherals. |
Yes. We currently never actually call static destructors anyway, this just eliminates them earlier and harder. I should clarify that in #11727 too - that's actually "optimising GCC static non-destruction".
Indeed, but that wouldn't happen if those were declared globally. There's no system shutdown. You could make it happen dynamically by using
That concept then extends to multiple modes... |
Obviously, more simply, this would work, but only for stuff small enough to fit on the stack, and assuming you're not doing something event driven. Would make sense though if you had a reserved thread, so the storage would effectively be that thread's stack:
|
Saving for this in a cloud client example was
So −0.2% on ROM. |
@kjbracey-arm I was thinking more from a standpoint of using the destructors as a standard way to clean up peripheral states and buffers when no longer using them. The various considerations of entering sleep, resetting, low power, and very direct control of hardware resources whose memory cannot be "freed" lead me to the conclusion that it would probably better to manage peripheral control via dedicated member functions? |
92886d7
to
f3417f2
Compare
That should work. And can work, if you're in a position to destroy the object. I think
We're also adding |
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.
Looks ok to me
f3417f2
to
2f1eedd
Compare
Just occurred to me that the option should really be under |
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.
Looks great. If I understand correctly a variable with the attribute [clang::always_destroy]]
will continue to register the destructor in atexit
?
CI started |
Yes, it will continue to register it with |
Ah, as per the note at the bottom of the description, I think this will fail until the compiler is updated. I was getting link errors with 6.11. You'll probably need to stick a "needs: preceding PR" or something on. But let's check what happens. |
Test run: FAILEDSummary: 1 of 4 test jobs failed Failed test jobs:
|
I believe CI is now on ARM Compiler 6.13, so this should pass now. |
Bunch of spurious failures?
|
CI restarted |
Exporters contain failures for ARMC6, related. I'll check what is the current version, although recently the exporters were disabled... so this might pass if restarted |
We disable C++ static destructors as best as possible in the various toolchains, trying to eliminate unwanted destructor code. We don't want to waste RAM or ROM on the C++-standard-specified behaviour of running all statically-constructed objects' destructors in reverse order on exit; we're never going to perform an orderly exit. Techniques used include: * `SingletonPtr` - makes an object be "lazily constructed" on first use, and also eliminates its destructor. Lazy construction adds ROM+RAM overhead. * `__eabi_atexit` is stubbed out, preventing RAM usage by run-time registration of static destructors. * GCC has `exit` wrapped to kill shutdown code * IAR has static destructors disabled in the compiler flags Killing static destructors in the compiler is the optimum; if we only stub out `__eabi_atexit`, the compiler is still inserting calls to it, and referencing the destructors in those call. Clang 8 added the compiler option `-fno-c++-static-destructors` (and the object attributes `[[clang::no_destroy]]` and `[[clang::always_destroy]]` for fine control). We can hence enable that option in ARMC6 tool profiles, matching IAR. This option appears to exist in ARM Compiler 6.11, but generates an apparently spurious linker error about `EthernetInterface`. It works in ARM Compiler 6.13, so this PR needs to wait until the compiler is updated.
2f1eedd
to
1ecd960
Compare
I was able to rebase this, will restart tests now |
Pull request has been modified.
Just a rebase, all approved previosly |
Sorry but I didn't understand the command. Hey, I reacted but my real name is @Mergifyio |
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.
Looks ok to me
Test run: FAILEDSummary: 1 of 6 test jobs failed Failed test jobs:
|
Test restarted |
This PR does not contain release version label after merging. |
Description
We disable C++ static destructors as best as possible in the various toolchains, trying to eliminate unwanted destructor code. We don't want to waste RAM or ROM on the C++-standard-specified behaviour of running all statically-constructed objects' destructors in reverse order on exit; we're never going to perform an orderly exit.
Techniques used include:
SingletonPtr
- makes an object be "lazily constructed" on first use, and also eliminates its destructor. Lazy construction adds ROM+RAM overhead.__eabi_atexit
is stubbed out, preventing RAM usage by run-time registration of static destructors.exit
wrapped to kill shutdown codeKilling static destructors in the compiler is the optimum; if we only stub out
__eabi_atexit
, the compiler is still inserting calls to it, and referencing the destructors in those call.Clang 8 added the compiler option
-fno-c++-static-destructors
(and the object attributes[[clang::no_destroy]]
and[[clang::always_destroy]]
for fine control).We can hence enable that option in ARMC6 tool profiles, matching IAR.
This option appears to exist in ARM Compiler 6.11, but generates an apparently spurious linker error about
EthernetInterface
. It works in ARM Compiler 6.13, so this PR needs to wait until the compiler is updated.Pull request type
Reviewers
@pan-, @bulislaw, @0xc0170