GCC, MSVC, LLVM et probablement d'autres chaînes d'outils prennent en charge l'optimisation du temps de liaison (programme entier) pour permettre l'optimisation des appels entre les unités de compilation.
Y a-t-il une raison pour ne pas activer cette option lors de la compilation d'un logiciel de production?
Je suppose que par "logiciel de production" vous entendez un logiciel que vous expédiez aux clients/entre en production. Les réponses à Pourquoi ne pas toujours utiliser l'optimisation du compilateur? (aimablement souligné par Mankarse ) s'appliquent principalement aux situations dans lesquelles vous souhaitez déboguer votre code (donc le logiciel est toujours en la phase de développement - pas en production).
La seule bonne raison valable à laquelle je peux penser est que l'optimisation du temps de liaison peut introduire des bogues subtils, voir Optimisation du temps de lien pour le noya . En supposant que vous disposiez de tests appropriés pour vérifier l'exactitude de votre logiciel que vous êtes sur le point d'expédier, je ne vois aucune raison de ne pas utiliser LTO par défaut. (LTO devient plus mature avec le temps, alors espérons que ces bogues subtils seront de moins en moins fréquents.)
Cette question récente soulève un autre cas possible (mais plutôt spécifique) dans lequel le LTO peut avoir des effets indésirables: si le code en question est instrumenté pour le timing, et des unités de compilation distinctes ont été utilisées pour essayer de préserver le relatif ordonner les instructions instrumentées et instrumentantes, alors LTO a de bonnes chances de détruire l'ordre nécessaire.
J'ai dit que c'était spécifique.
Si vous avez un code bien écrit, cela ne devrait être que avantageux. Vous pouvez rencontrer un bogue du compilateur/éditeur de liens, mais cela vaut pour tous les types d'optimisation, c'est rare.
Le plus gros inconvénient est qu'il augmente considérablement le temps de liaison.
Un scénario où l'optimisation du temps de liaison peut entraîner un comportement inattendu pour un code incorrect est le suivant:
Imaginez que vous avez deux fichiers source read.c
et client.c
que vous compilez dans des fichiers objets séparés. Dans le fichier read.c
il existe une fonction read
qui ne fait rien d'autre que de lire à partir d'une adresse mémoire spécifique. Le contenu de cette adresse doit cependant être marqué comme volatile
, mais malheureusement cela a été oublié. De client.c
la fonction read
est appelée plusieurs fois depuis la même fonction. Étant donné que read
n'effectue qu'une seule lecture à partir de l'adresse et qu'il n'y a pas d'optimisation au-delà des limites de la fonction read
, read
fera toujours, lorsqu'elle sera appelée, accéder à l'emplacement de mémoire respectif. Par conséquent, chaque fois que read
est appelé depuis client.c
, le code dans client.c
obtient une valeur fraîchement lue de l'adresse, comme si volatile
avait été utilisé.
Maintenant, avec l'optimisation du temps de liaison, la minuscule fonction read
de read.c
est susceptible d'être inséré partout où il est appelé depuis client.c
. En raison du volatile
manquant, le compilateur se rendra maintenant compte que le code lit plusieurs fois à partir de la même adresse, et peut donc optimiser les accès mémoire. Par conséquent, le code commence à se comporter différemment.
En dehors de this ,
Prenons un exemple typique d'un système embarqué,
void function1(void) { /*Do something*/} //located at address 0x1000
void function2(void) { /*Do something*/} //located at address 0x1100
void function3(void) { /*Do something*/} //located at address 0x1200
Avec des fonctions adressées prédéfinies, vous pouvez appeler des adresses relatives comme ci-dessous,
void (*ptr)(void) = function1;
(ptr + 0x100)(); //expected to call function2
(ptr + 0x200)(); //expected to call function3
LOT peut conduire à un comportement inattendu.