Maybe use `gnu::noclone` to reduce program size
jputcu opened this issue · 9 comments
This is just an experiment I did with something new I found, so it could have consequences I'm didn't foresee.
Trying to get my code from avr-gcc-7.3
to avr-gcc-14.1
to fit my microcontroller, I had to get rid of some heavy cloned
methods.
Researching the internet I found the following compiler attribute: gnu::noclone
.
Appliying this technique with avr-gcc-7.3
and ArduinoJson-6.21.4
on a sample method CollectionData::getSlot
:
0000206e 00000070 t ArduinoJson::V6214IA1::detail::VariantSlot* ArduinoJson::V6214IA1::detail::CollectionData::getSlot<ArduinoJson::V6214IA1::detail::FlashString>(ArduinoJson::V6214IA1::detail::FlashString) const [clone .isra.119]
00005aa4 00000072 t ArduinoJson::V6214IA1::detail::VariantSlot* ArduinoJson::V6214IA1::detail::CollectionData::getSlot<ArduinoJson::V6214IA1::detail::FlashString>(ArduinoJson::V6214IA1::detail::FlashString) const [clone .isra.67]
Adding the [[gnu::noclone]]
to the relevant method in ArduinoJson/Collection/CollectionImpl.hpp
:
template <typename TAdaptedString>
[[gnu::noclone]]
inline VariantSlot* CollectionData::getSlot(TAdaptedString key) const {
Leaving me with a single instance:
00002df8 00000074 W ArduinoJson::V6214IA1::detail::VariantSlot* ArduinoJson::V6214IA1::detail::CollectionData::getSlot<ArduinoJson::V6214IA1::detail::FlashString>(ArduinoJson::V6214IA1::detail::FlashString) const
Flash size went from 74364 to 74286 bytes.
I don't know why the compiler, especially the newer one, decides to clone
large methods, but it probably has to do with locality to the source files where the methods are used.
Maybe lto
could fix this?
Some other duplicate entries:
00001f24 00000038 t ArduinoJson::V6214IA1::Converter<char const*, void>::toJson(char const*, ArduinoJson::V6214IA1::JsonVariant) [clone .isra.109]
00006060 00000038 t ArduinoJson::V6214IA1::Converter<char const*, void>::toJson(char const*, ArduinoJson::V6214IA1::JsonVariant) [clone .isra.70]
000080ba 0000001e t ArduinoJson::V6214IA1::Converter<unsigned char, void>::toJson(unsigned char, ArduinoJson::V6214IA1::JsonVariant) [clone .isra.130]
00009c2c 0000001e t ArduinoJson::V6214IA1::Converter<unsigned char, void>::toJson(unsigned char, ArduinoJson::V6214IA1::JsonVariant) [clone .isra.191]
0000601a 0000001e t ArduinoJson::V6214IA1::Converter<unsigned char, void>::toJson(unsigned char, ArduinoJson::V6214IA1::JsonVariant) [clone .isra.61]
00001f06 0000001e t ArduinoJson::V6214IA1::Converter<unsigned char, void>::toJson(unsigned char, ArduinoJson::V6214IA1::JsonVariant) [clone .isra.90]
00000283 00000018 V ArduinoJson::V6214IA1::detail::FloatTraits<float, 4u>::negativeBinaryPowersOfTen()::factors
0000029b 00000018 V ArduinoJson::V6214IA1::detail::FloatTraits<float, 4u>::positiveBinaryPowersOfTen()::factors
How did you install avr-gcc 14.1?
I tried installing with apt on Ubuntu 24.04 and got avr-gcc 7.3.0.
I've been carefull to report this issue using the 7.3, which is provided by Arduino/Microchip and I'm currently using in production.
To get the newer compiler I use ZakKemble's avr-gcc build (https://github.com/ZakKemble/avr-gcc-build). I've created a conan package for the different variants: Microchip/Arduino and ZakKemble and just use different profiles: https://github.com/jputcu/avr_conan.
I compiled JsonParserExample.ino
with the Arduino IDE targeting the Arduino UNO.
As far as I can tell, this is using avr-gcc 7.3.0.
Yet, when I run avr-nm --size-sort -C -r "<path-to-the-elf-file>"
, I don't see any clone.
Can you provide a complete reproduction procedure?
I was thinking about that, will see what I can do to trigger cloning
with a minimal example
I found another workaround: The reason for the 2 getSlot
was a custom DateTimeTm converter that was called from 2 different source files. When moving the functions calling these conversions into the same source file the clone goes aways:
Flash 74348 -> 74238
namespace ArduinoJson {
template <> struct Converter<DateTimeTm> {
[[gnu::noinline]] static DateTimeTm fromJson(JsonVariantConst src) {
return DateTimeTm{src[JKEY("datetime")].as<const char *>()};
}
}
I'm trying to reproduce this situation with a simple Arduino program, but I notice it is using lto
.
Ok, enabled lto
on the newer avr-gcc-14.2 on msys2. This gave me 73586 instead of 76598 but there were some lto compilation warnings. Duplicates are gone now and the application still works
noclone
works, but lto
seems to be the correct way to avoid clones (and also template reuse).
lto
is enabled by Arduino on the avr-gcc-7.3, even with debug, so I think it is a safe bet.
I did see more stack usage but this is probably due to some methods now no longer being inlined.
Thank you very much for the update.