Compilation C++ JIT avec LLVM

Corriger le crash du ModulePassManager

Emmanuel ROCHE

Traducteur

Thierry Jeanneret

L'objectif de ce tutoriel d'Emmanuel Roche est de vous apprendre à construire un compilateur C++ JIT avec LLVM.

Pour réagir au contenu de ce tutoriel, un espace de dialogue vous est proposé sur le forum. Commentez

- Titre : Compilation C++ JIT avec LLVM

- Auteur : Emmanuel ROCHE

- Traducteur : Thierry Jeanneret

- Parution : 10 août 2020

- Licence : Le contenu de cet article est rédigé par Emmanuel Roche et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Voir également :

- Compilation C++ JIT avec LLVM - Partie 4 : dépendance à l’environnement d’exécution C

- Compilation C++ avec LLVM - Partie 2 : construire un compilateur C++ JIT fonctionnel avec LLVM

- Compilation C++ avec LLVM et clang - Partie 1 : générer du code C++ dynamiquement depuis Lua

Sommaire

- I. Introduction

- II. Mise à jour du mécanisme d’optimisation IR

- III. Reconstruire l’outil LLVM opt

- IV. Configuration de construction naïve

- V. Configuration de construction correcte

- VI. Mettre à jour la configuration de construction de nvLLVM

- VII. Remerciements Developpez.com

I - Introduction

Si vous avez lu mon article précédent sur ce sujet (Compilateur C++ JIT avec LLVM - Partie 2) vous avez probablement noté qu’il y avait un sérieux problème avec l’implémentation de NervJIT que j’ai décrite (et aussi dans l’implémentation expérimentale de runClang() décrite dans le tout premier article) : je ne pouvais pas libérer les ressources de la passe d’optimisation IR (Intermediate Representation : représentation intermédiaire), ni non plus l’objet llvm::orc::LLJIT, car tenter de le faire produisait des plantées silencieuses. Dans ce nouveau post, nous nous concentrerons sur les étapes que j’ai suivies pour me débarrasser finalement de ce problème.

Alors, allons-y !????

II - Mise à jour du mécanisme d’optimisation IR

Si vous vous en souvenez, dans le destructeur de notre NervJITImpl, nous devions explicitement « libérer » les pointeurs en question (en entrainant ainsi malheureusement une fuite de mémoire…) comme suit :

1. NervJITImpl::~NervJITImpl()

2. {

3. // Note: the memory for the LLJIT object will leak here because we get a crash when we try to delete it.

4. std::cout << "Releasing undestructible pointers." << std::endl;

5. lljit.release();

7. moduleAnalysisManager.release();

8. cGSCCAnalysisManager.release();

9. functionAnalysisManager.release();

10. loopAnalysisManager.release();

12. std::cout << "Done releasing undestructible pointers." << std::endl;

13. }

Par conséquent, j’ai décidé d’examiner en premier lieu ces quatre objets AnalysisManager. Avant de me demander « pourquoi est-ce que je ne peux pas les supprimer proprement ? », je me suis plutôt posé la question : « Ai-je réellement besoin de ces trucs ? » Je suis donc parti à la recherche des techniques discutées en ligne sur l’optimisation des modules LLVM IR.

Le problème à ce niveau est que LLVM évolue continuellement (rapidement) et donc qu’il n’est pas simple de trouver quelque chose qui soit à jour avec la version Git que j’utilise. J’ai finalement trouvé cette page sur Stack Overflow : https://stackoverflow.com/questions/53738883/run-default-optimization-pipeline-using-modern-llvm/53739108.

Même si cela concernait LLVM version 7.0, la bonne chose était qu’il faisait référence à un des outils de LLVM fourni comme modèle de base : l’outil opt, que je peux trouver dans l’arborescence de la version courante des sources de LLVM !

J’ai jeté un coup d’œil rapide aux sources trouvées dans llvm\tool\opt (surtout le fichier opt.cpp) et, de là, vous pouvez facilement confirmer que le code présenté dans l’article de Stack Overflow était encore largement similaire à ce qui est actuellement fait dans opt. Ainsi, j’ai pu heureusement sauter, plein d’espoir, dans ce train et implémenter ma propre mise à jour de la logique de « l’optimisation de module » pour mon composant NervJIT :

1. void addOptPasses(

2. llvm::legacy::PassManagerBase &passes,

3. llvm::legacy::FunctionPassManager &fnPasses,

4. llvm::TargetMachine *machine

5. ) {

6. llvm::PassManagerBuilder builder;

7. builder.OptLevel = 3;

8. builder.SizeLevel = 0;

9. builder.Inliner = llvm::createFunctionInliningPass(3, 0, false);

10. builder.LoopVectorize = true;

11. builder.DisableUnrollLoops = false;

12. builder.SLPVectorize = true;

13. machine->adjustPassManager(builder);

15. builder.populateFunctionPassManager(fnPasses);

16. builder.populateModulePassManager(passes);

17. }

19. void addLinkPasses(llvm::legacy::PassManagerBase &passes) {

20. llvm::PassManagerBuilder builder;

21. builder.VerifyInput = true;

22. builder.Inliner = llvm::createFunctionInliningPass(3, 0, false);

23. builder.populateLTOPassManager(passes);

24. }

26. void NervJITImpl::optimizeModule(llvm::Module *module) {

27. module->setTargetTriple(targetMachine->getTargetTriple().str());

28. module->setDataLayout(targetMachine->createDataLayout());

30. // DEBUG_MSG("Creating legacy pass manager.");

31. llvm::legacy::PassManager passes;

32. passes.add(new llvm::TargetLibraryInfoWrapperPass(targetMachine->getTargetTriple()));

33. passes.add(llvm::createTargetTransformInfoWrapperPass(targetMachine->getTargetIRAnalysis()));

35. // DEBUG_MSG("Creating legacy function pass manager.");

36. llvm::legacy::FunctionPassManager fnPasses(module);

37. fnPasses.add(llvm::createTargetTransformInfoWrapperPass(targetMachine->getTargetIRAnalysis()));

39. addOptPasses(passes, fnPasses, targetMachine.get());

40. addLinkPasses(passes);

42. // DEBUG_MSG("Optimizing functions");

43. fnPasses.doInitialization();

44. for (llvm::Function &func : *module) {

45. fnPasses.run(func);

46. }

47. fnPasses.doFinalization();

49. passes.add(llvm::createVerifierPass());

50. // DEBUG_MSG("Running PassManager.");

51. passes.run(*module);

52. // DEBUG_MSG("Optimization done.");

53. }

Pour pouvoir compiler le code ci-dessus, la seule chose nouvelle dont j’ai eu besoin fut un pointeur llvm::TargetMachine approprié : ça m’a pris un peu de temps pour trouver comment en obtenir un, mais à la fin c’est assez direct (de nouveau, je construis cet objet dans le constructeur de mon NervJITImpl) :

auto jtmb = CHECK_LLVM(JITTargetMachineBuilder::detectHost());
targetMachine = CHECK_LLVM(jtmb.createTargetMachine());

Puis, tout ce que j’ai dû faire fut de remplacer les appels à la fonction ModulePassManager::run() à l’intérieur de la méthode NervJITImpl::loadModule() par ma nouvelle fonction optimizeModule():

1. // We run the optimizations:

2. DEBUG_MSG("Optimizing module...");

3. //modulePassManager.run(*module, *moduleAnalysisManager);

4. optimizeModule(module.get());

6. DEBUG_MSG("Module function list: ");

… Et… cela a planté LAMENTABLEMENT… Un crash silencieux quelque part après l’affichage « Optimizing module… » (Arrff, je pense que mes espoirs étaient trop élevés…).

Donc, retour aux investigations, innombrables tests “changement/construction/exécution”, et j’ai finalement tracé le problème jusqu’à, une fois encore, que sa source soit liée au module pass manager! En fait, j’ai pu faire marcher l’étape d’optimisation en commentant ces deux lignes (respectivement tout à la fin de addOptPasses()et addLinkPasses()) :

1. // in addOptPasses():

2. // builder.populateModulePassManager(passes);

4. // in addLinkPasses():

5. // builder.populateLTOPassManager(passes);

Bien sûr ce n’était pas une solution acceptable ; ce code fonctionnait bien dans l’outil opt de LLVM, après tout ! Il était donc temps de changer ma perspective et de me focaliser plutôt sur l’outil opt lui-même en tentant de comprendre ce que je faisais faux dans mon propre projet.

III - Reconstruire l’outil LLVM opt

Pendant que je tournais autour de opt de LLVM, je l’ai exécuté avec succès depuis la ligne de commande sur un fichier de test LLVM pris au hasard (que j’ai trouvé ailleurs dans l’arborescence des sources de LLVM), en utilisant ce genre de commande :

opt --O3 --debugify-each --verify-each bcsection.ll -o result.bc

Ce qui a donné ce genre de résultat :

1. CheckModuleDebugify [Force set function attributes]: PASS

2. CheckModuleDebugify [Infer set function attributes]: PASS

3. CheckFunctionDebugify [Call-site splitting]: PASS

4. CheckModuleDebugify [Interprocedural Sparse Conditional Constant Propagation]: PASS

5. CheckModuleDebugify [Called Value Propagation]: PASS

6. CheckModuleDebugify [Global Variable Optimizer]: PASS

7. CheckFunctionDebugify [Promote Memory to Register]: PASS

8. CheckModuleDebugify [Dead Argument Elimination]: PASS

9. CheckFunctionDebugify [Combine redundant instructions]: PASS

10. CheckFunctionDebugify [Simplify the CFG]: PASS

11. CheckModuleDebugify [Globals Alias Analysis]: PASS

12. CheckFunctionDebugify [SROA]: PASS

13. CheckFunctionDebugify [Early CSE w/ MemorySSA]: PASS

14. CheckFunctionDebugify [Speculatively execute instructions if target has divergent branches]: PASS

15. CheckFunctionDebugify [Jump Threading]: PASS

16. CheckFunctionDebugify [Value Propagation]: PASS

17. CheckFunctionDebugify [Simplify the CFG]: PASS

18. CheckFunctionDebugify [Combine pattern based expressions]: PASS

19. CheckFunctionDebugify [Combine redundant instructions]: PASS

20. CheckFunctionDebugify [Conditionally eliminate dead library calls]: PASS

21. CheckFunctionDebugify [PGOMemOPSize]: PASS

22. CheckFunctionDebugify [Tail Call Elimination]: PASS

23. CheckFunctionDebugify [Simplify the CFG]: PASS

24. CheckFunctionDebugify [Reassociate expressions]: PASS

25. CheckFunctionDebugify [Simplify the CFG]: PASS

26. CheckFunctionDebugify [Combine redundant instructions]: PASS

27. CheckFunctionDebugify [MergedLoadStoreMotion]: PASS

28. CheckFunctionDebugify [Global Value Numbering]: PASS

29. CheckFunctionDebugify [MemCpy Optimization]: PASS

30. CheckFunctionDebugify [Sparse Conditional Constant Propagation]: PASS

31. CheckFunctionDebugify [Bit-Tracking Dead Code Elimination]: PASS

32. CheckFunctionDebugify [Combine redundant instructions]: PASS

33. CheckFunctionDebugify [Jump Threading]: PASS

34. CheckFunctionDebugify [Value Propagation]: PASS

35. CheckFunctionDebugify [Dead Store Elimination]: PASS

36. CheckFunctionDebugify [Aggressive Dead Code Elimination]: PASS

37. CheckFunctionDebugify [Simplify the CFG]: PASS

38. CheckFunctionDebugify [Combine redundant instructions]: PASS

39. CheckModuleDebugify [A No-Op Barrier Pass]: PASS

40. CheckModuleDebugify [Eliminate Available Externally Globals]: PASS

41. CheckModuleDebugify [Deduce function attributes in RPO]: PASS

42. CheckModuleDebugify [Global Variable Optimizer]: PASS

43. CheckModuleDebugify [Dead Global Elimination]: PASS

44. CheckModuleDebugify [Globals Alias Analysis]: PASS

45. CheckFunctionDebugify [Float to int]: PASS

46. CheckFunctionDebugify [Lower constant intrinsics]: PASS

47. CheckFunctionDebugify [Loop Distribution]: PASS

48. CheckFunctionDebugify [Loop Vectorization]: PASS

49. CheckFunctionDebugify [Optimize scalar/vector ops]: PASS

50. CheckFunctionDebugify [Early CSE]: PASS

51. CheckFunctionDebugify [Loop Load Elimination]: PASS

52. CheckFunctionDebugify [Combine redundant instructions]: PASS

53. CheckFunctionDebugify [Simplify the CFG]: PASS

54. CheckFunctionDebugify [SLP Vectorizer]: PASS

55. CheckFunctionDebugify [Combine redundant instructions]: PASS

56. CheckFunctionDebugify [Combine redundant instructions]: PASS

57. CheckFunctionDebugify [Warn about non-applied transformations]: PASS

58. CheckFunctionDebugify [Alignment from assumptions]: PASS

59. CheckModuleDebugify [Strip Unused Function Prototypes]: PASS

60. CheckModuleDebugify [Dead Global Elimination]: PASS

61. CheckModuleDebugify [Merge Duplicate Global Constants]: PASS

62. CheckFunctionDebugify [Remove redundant instructions]: PASS

63. CheckFunctionDebugify [Hoist/decompose integer division and remainder]: PASS

64. CheckFunctionDebugify [Simplify the CFG]: PASS

Pour être honnête, je n’étais toujours pas sûr de si ce programme se terminait élégamment ou se plantait silencieusement après la dernière ligne « CheckFunctionDebugify » rapportée ci-dessus, mais c’était déjà un bon point de référence de toute façon et je devais pouvoir au moins reproduire ces résultats si je reconstruisais cet outil moi-même à partir des sources, non ?

IV - Configuration de construction naïve

À ce stade, on se référera naturellement aux fichiers CMake de l’outil opt de l’arborescence des sources de LLVM pour comprendre comment configurer un projet clone à partir « des sources LLVM ». Habituellement, CMake est un langage assez simple à utiliser, d’accord ? Eh bien… malheureusement, cette fois, sauf si vous êtes un gourou absolu de CMake, je crains que cette voie ne vous conduise pas bien loin : les fichiers CMake de LLVM sont réellement cryptiques, je vous souhaite donc bonne chance pour essayer d’extraire l’information dont vous avez besoin de ce type de contenu :

1. set(LLVM_LINK_COMPONENTS

2. AllTargetsAsmParsers

3. AllTargetsCodeGens

4. AllTargetsDescs

5. AllTargetsInfos

6. AggressiveInstCombine

7. Analysis

8. BitWriter

9. CodeGen

10. Core

11. Coroutines

12. IPO

13. IRReader

14. InstCombine

15. Instrumentation

16. MC

17. ObjCARCOpts

18. Remarks

19. ScalarOpts

20. Support

21. Target

22. TransformUtils

23. Vectorize

24. Passes

25. )

27. add_llvm_tool(opt

28. AnalysisWrappers.cpp

29. BreakpointPrinter.cpp

30. GraphPrinters.cpp

31. NewPMDriver.cpp

32. PassPrinters.cpp

33. PrintSCC.cpp

34. opt.cpp

36. ENABLE_PLUGINS

38. DEPENDS

39. intrinsics_gen

40. SUPPORT_PLUGINS

41. )

42. export_executable_symbols_for_plugins(opt)

44. if(LLVM_BUILD_EXAMPLES)

45. target_link_libraries(opt PRIVATE ExampleIRTransforms)

46. endif(LLVM_BUILD_EXAMPLES)

Si vous voulez vraiment tenter le coup et voulez trouver les définitions et le comportement de toutes les fonctions/macros utilisées dans ce fichier, un point de départ est le fichier ${votre répertoire d’installation de LLVM}/lib/cmake/llvm/AddLLVM.cmake. Mais vous aurez été prévenu… (Bon, amusement ! et assurez-vous d'arrêter de lire ce fichier avant de vous pendre dans votre salle de bain…)

J’ai donc décidé qu’il serait aussi bien de prendre juste les fichiers source de ce répertoire racine et de construire le fichier CMake moi-même : après tout, j’ai déjà pu construire un nvLLVM « fonctionnel » (bon, à l’exception d’un plantage silencieux de ton «module fonctionnel» ou d’une fuite mémoire, Manu, certes !) avec un fichier CMakeLists.txt fait main, je devrais donc peut-être pouvoir l’utiliser comme modèle et voir comment ça marche…

Et c’est exactement ce que j’ai fait : construire un sous-projet test que j’ai nommé test_llvm_opt , lier avec toutes les bibliothèques de LLVM et utiliser les mêmes paramètres CXX que dans mon précédent sous-projet nvLLVM .

Étonnamment, faire compiler ce clone de l’outil opt de LLVM n’a pas été trop difficile finalement, mais ensuite est venu le moment de l’essayer (en utilisant exactement la même entrée qu’avec le binaire officiel évidemment) :

1. test_llvm_opt.exe --O3 --debugify-each --verify-each bcsection.ll -o result.bc

2. CheckModuleDebugify [Force set function attributes]: PASS

3. CheckModuleDebugify [Infer set function attributes]: PASS

4. CheckFunctionDebugify [Call-site splitting]: PASS

5. CheckModuleDebugify [Interprocedural Sparse Conditional Constant Propagation]: PASS

6. CheckModuleDebugify [Called Value Propagation]: PASS

7. CheckModuleDebugify [Global Variable Optimizer]: PASS

8. CheckFunctionDebugify [Promote Memory to Register]: PASS

9. CheckModuleDebugify [Dead Argument Elimination]: PASS

10. CheckFunctionDebugify [Combine redundant instructions]: PASS

11. CheckFunctionDebugify [Simplify the CFG]: PASS

12. CheckModuleDebugify [Globals Alias Analysis]: PASS

13. CheckFunctionDebugify [SROA]: PASS

14. CheckFunctionDebugify [Early CSE w/ MemorySSA]: PASS

15. CheckFunctionDebugify [Speculatively execute instructions if target has divergent branches]: PASS

16. CheckFunctionDebugify [Jump Threading]: PASS

17. CheckFunctionDebugify [Value Propagation]: PASS

18. CheckFunctionDebugify [Simplify the CFG]: PASS

19. CheckFunctionDebugify [Combine pattern based expressions]: PASS

20. CheckFunctionDebugify [Combine redundant instructions]: PASS

21. CheckFunctionDebugify [Conditionally eliminate dead library calls]: PASS

22. CheckFunctionDebugify [PGOMemOPSize]: PASS

23. CheckFunctionDebugify [Tail Call Elimination]: PASS

24. CheckFunctionDebugify [Simplify the CFG]: PASS

25. CheckFunctionDebugify [Reassociate expressions]: PASS

26. CheckFunctionDebugify [Simplify the CFG]: PASS

27. CheckFunctionDebugify [Combine redundant instructions]: PASS

28. CheckFunctionDebugify [MergedLoadStoreMotion]: PASS

29. CheckFunctionDebugify [Global Value Numbering]: PASS

30. CheckFunctionDebugify [MemCpy Optimization]: PASS

31. CheckFunctionDebugify [Sparse Conditional Constant Propagation]: PASS

32. CheckFunctionDebugify [Bit-Tracking Dead Code Elimination]: PASS

33. CheckFunctionDebugify [Combine redundant instructions]: PASS

34. CheckFunctionDebugify [Jump Threading]: PASS

35. CheckFunctionDebugify [Value Propagation]: PASS

36. CheckFunctionDebugify [Dead Store Elimination]: PASS

Et CATABOUM ! Le processus meurt exactement à la même place à chaque fois que j’exécute cette commande (alors que l’outil opt officiel continue depuis ici avec beaucoup de passes additionnelles, comme [Aggressive Dead Code Elimination]).

Arrivé là, j’étais réellement convaincu que quelque chose était faux dans ma configuration de CMake, mais quoi ?

V - Configuration de construction correcte

J’ai repris les fichiers CMake officiels de opt, en essayant de me concentrer au maximum sur tous les éléments de configuration, les macros, les conditions, etc. C’était clairement énorme et ingérable. Je commençais à perdre la foi, mais j’ai alors réalisé quelque chose qui a sauvé ma journée : CMake finit par simplement écrire un Makefile (à peu près) « normal » ! (Au moins lorsqu’on utilise le générateur Nmake comme je le fais.)

De toute façon, il ne me restait pas d’autre option, je me suis donc plongé dans les fichiers de construction générés par CMake pour mes sources Git de LLVM, ceci m’a réellement ouvert les yeux !

J’ai d’abord trouvé le fichier tools\opt\CMakeFiles\opt.dir\build.make, dans lequel vous trouverez ce type de contenu :

1. tools\opt\CMakeFiles\opt.dir\AnalysisWrappers.cpp.obj: tools\opt\CMakeFiles\opt.dir\flags.make

2. tools\opt\CMakeFiles\opt.dir\AnalysisWrappers.cpp.obj: W:\Projects\NervSeed\deps\build\llvm-20200409\llvm\tools\opt\AnalysisWrappers.cpp

3. @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=W:\Projects\NervSeed\deps\build\llvm-20200409\build\CMakeFiles --progress-num=$(CMAKE_PROGRESS_1) "Building CXX object tools/opt/CMakeFiles/opt.dir/AnalysisWrappers.cpp.obj"

4. cd W:\Projects\NervSeed\deps\build\llvm-20200409\build\tools\opt

5. D:\Apps\VisualStudio2017_CE\VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64\cl.exe @<<

6. /nologo /TP $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) /FoCMakeFiles\opt.dir\AnalysisWrappers.cpp.obj /FdCMakeFiles\opt.dir\ /FS -c W:\Projects\NervSeed\deps\build\llvm-20200409\llvm\tools\opt\AnalysisWrappers.cpp

Et au début de ce fichier, nous avons :

1. # Include any dependencies generated for this target.

2. include tools\opt\CMakeFiles\opt.dir\depend.make

4. # Include the progress variables for this target.

5. include tools\opt\CMakeFiles\opt.dir\progress.make

7. # Include the compile flags for this target's objects.

8. include tools\opt\CMakeFiles\opt.dir\flags.make

Donc, naturellement, vous vérifiez le fichier frère flags.make, et là, bingo !

1. # compile CXX with D:/Apps/VisualStudio2017_CE/VC/Tools/MSVC/14.16.27023/bin/Hostx64/x64/cl.exe

2. # compile RC with C:/Program Files (x86)/Windows Kits/10/bin/10.0.18362.0/x64/rc.exe

3. CXX_FLAGS = /DWIN32 /D_WINDOWS   /Zc:inline /Zc:strictStrings /Oi /Zc:rvalueCast /W4 -wd4141 -wd4146 -wd4244 -wd4267 -wd4291 -wd4351 -wd4456 -wd4457 -wd4458 -wd4459 -wd4503 -wd4624 -wd4722 -wd4100 -wd4127 -wd4512 -wd4505 -wd4610 -wd4510 -wd4702 -wd4245 -wd4706 -wd4310 -wd4701 -wd4703 -wd4389 -wd4611 -wd4805 -wd4204 -wd4577 -wd4091 -wd4592 -wd4319 -wd4709 -wd4324 -w14062 -we4238 /Gw /MD /O2 /Ob2 /DNDEBUG    /EHs-c- /GR-

5. CXX_DEFINES = -DGTEST_HAS_RTTI=0 -DUNICODE -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS -D_HAS_EXCEPTIONS=0 -D_SCL_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS

7. CXX_INCLUDES = -IW:\Projects\NervSeed\deps\build\llvm-20200409\build\tools\opt -IW:\Projects\NervSeed\deps\build\llvm-20200409\llvm\tools\opt -IW:\Projects\NervSeed\deps\build\llvm-20200409\build\include -IW:\Projects\NervSeed\deps\build\llvm-20200409\llvm\include

- Avec ces lignes, vous savez exactement quelles commandes sont exécutées pour compiler les fichiers cpp du répertoire en fichiers objet.

Et finalement de retour à build.make, vous trouverez aussi la définition de la cible d’édition des liens :

bin\opt.exe: tools\opt\CMakeFiles\opt.dir\objects1.rsp
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --bold --progress-dir=W:\Projects\NervSeed\deps\build\llvm-20200409\build\CMakeFiles --progress-num=$(CMAKE_PROGRESS_9) "Linking CXX executable ..\..\bin\opt.exe"
cd W:\Projects\NervSeed\deps\build\llvm-20200409\build\tools\opt
W:\Projects\NervSeed\tools\windows\cmake-3.9.2\bin\cmake.exe -E vs_link_exe --intdir=CMakeFiles\opt.dir --manifests  -- D:\Apps\VisualStudio2017_CE\VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64\link.exe /nologo @CMakeFiles\opt.dir\objects1.rsp @<<
/out:..\..\bin\opt.exe /implib:..\..\lib\opt.lib /pdb:W:\Projects\NervSeed\deps\build\llvm-20200409\build\bin\opt.pdb /version:0.0  /machine:x64 /STACK:10000000 /INCREMENTAL:NO /subsystem:console ..\..\lib\LLVMAArch64AsmParser.lib ..\..\lib\LLVMAMDGPUAsmParser.lib ..\..\lib\LLVMARMAsmParser.lib ..\..\lib\LLVMAVRAsmParser.lib ..\..\lib\LLVMBPFAsmParser.lib ..\..\lib\LLVMHexagonAsmParser.lib ..\..\lib\LLVMLanaiAsmParser.lib ..\..\lib\LLVMMipsAsmParser.lib ..\..\lib\LLVMMSP430AsmParser.lib ..\..\lib\LLVMPowerPCAsmParser.lib ..\..\lib\LLVMRISCVAsmParser.lib ..\..\lib\LLVMSparcAsmParser.lib ..\..\lib\LLVMSystemZAsmParser.lib ..\..\lib\LLVMWebAssemblyAsmParser.lib ..\..\lib\LLVMX86AsmParser.lib ..\..\lib\LLVMAArch64CodeGen.lib ..\..\lib\LLVMAMDGPUCodeGen.lib ..\..\lib\LLVMARMCodeGen.lib ..\..\lib\LLVMAVRCodeGen.lib ..\..\lib\LLVMBPFCodeGen.lib ..\..\lib\LLVMHexagonCodeGen.lib ..\..\lib\LLVMLanaiCodeGen.lib ..\..\lib\LLVMMipsCodeGen.lib ..\..\lib\LLVMMSP430CodeGen.lib ..\..\lib\LLVMNVPTXCodeGen.lib ..\..\lib\LLVMPowerPCCodeGen.lib ..\..\lib\LLVMRISCVCodeGen.lib ..\..\lib\LLVMSparcCodeGen.lib ..\..\lib\LLVMSystemZCodeGen.lib ..\..\lib\LLVMWebAssemblyCodeGen.lib ..\..\lib\LLVMX86CodeGen.lib ..\..\lib\LLVMXCoreCodeGen.lib ..\..\lib\LLVMAArch64Desc.lib ..\..\lib\LLVMAMDGPUDesc.lib ..\..\lib\LLVMARMDesc.lib ..\..\lib\LLVMAVRDesc.lib ..\..\lib\LLVMBPFDesc.lib ..\..\lib\LLVMHexagonDesc.lib ..\..\lib\LLVMLanaiDesc.lib ..\..\lib\LLVMMipsDesc.lib ..\..\lib\LLVMMSP430Desc.lib ..\..\lib\LLVMNVPTXDesc.lib ..\..\lib\LLVMPowerPCDesc.lib ..\..\lib\LLVMRISCVDesc.lib ..\..\lib\LLVMSparcDesc.lib ..\..\lib\LLVMSystemZDesc.lib ..\..\lib\LLVMWebAssemblyDesc.lib ..\..\lib\LLVMX86Desc.lib ..\..\lib\LLVMXCoreDesc.lib ..\..\lib\LLVMAArch64Info.lib ..\..\lib\LLVMAMDGPUInfo.lib ..\..\lib\LLVMARMInfo.lib ..\..\lib\LLVMAVRInfo.lib ..\..\lib\LLVMBPFInfo.lib ..\..\lib\LLVMHexagonInfo.lib ..\..\lib\LLVMLanaiInfo.lib ..\..\lib\LLVMMipsInfo.lib ..\..\lib\LLVMMSP430Info.lib ..\..\lib\LLVMNVPTXInfo.lib ..\..\lib\LLVMPowerPCInfo.lib ..\..\lib\LLVMRISCVInfo.lib ..\..\lib\LLVMSparcInfo.lib ..\..\lib\LLVMSystemZInfo.lib ..\..\lib\LLVMWebAssemblyInfo.lib ..\..\lib\LLVMX86Info.lib ..\..\lib\LLVMXCoreInfo.lib ..\..\lib\LLVMAggressiveInstCombine.lib ..\..\lib\LLVMAnalysis.lib ..\..\lib\LLVMBitWriter.lib ..\..\lib\LLVMCodeGen.lib ..\..\lib\LLVMCore.lib ..\..\lib\LLVMCoroutines.lib ..\..\lib\LLVMipo.lib ..\..\lib\LLVMIRReader.lib ..\..\lib\LLVMInstCombine.lib ..\..\lib\LLVMInstrumentation.lib ..\..\lib\LLVMMC.lib ..\..\lib\LLVMObjCARCOpts.lib ..\..\lib\LLVMRemarks.lib ..\..\lib\LLVMScalarOpts.lib ..\..\lib\LLVMSupport.lib ..\..\lib\LLVMTarget.lib ..\..\lib\LLVMTransformUtils.lib ..\..\lib\LLVMVectorize.lib ..\..\lib\LLVMPasses.lib ..\..\lib\LLVMAArch64Utils.lib ..\..\lib\LLVMAMDGPUUtils.lib ..\..\lib\LLVMMIRParser.lib ..\..\lib\LLVMARMUtils.lib ..\..\lib\LLVMHexagonAsmParser.lib ..\..\lib\LLVMHexagonDesc.lib ..\..\lib\LLVMHexagonInfo.lib ..\..\lib\LLVMLanaiAsmParser.lib ..\..\lib\LLVMLanaiDesc.lib ..\..\lib\LLVMLanaiInfo.lib ..\..\lib\LLVMRISCVUtils.lib ..\..\lib\LLVMMCDisassembler.lib ..\..\lib\LLVMCFGuard.lib ..\..\lib\LLVMGlobalISel.lib ..\..\lib\LLVMX86Utils.lib ..\..\lib\LLVMAsmPrinter.lib ..\..\lib\LLVMDebugInfoDWARF.lib ..\..\lib\LLVMSelectionDAG.lib ..\..\lib\LLVMCodeGen.lib ..\..\lib\LLVMCoroutines.lib ..\..\lib\LLVMipo.lib ..\..\lib\LLVMBitWriter.lib ..\..\lib\LLVMIRReader.lib ..\..\lib\LLVMAsmParser.lib ..\..\lib\LLVMFrontendOpenMP.lib ..\..\lib\LLVMLinker.lib ..\..\lib\LLVMInstrumentation.lib ..\..\lib\LLVMScalarOpts.lib ..\..\lib\LLVMAggressiveInstCombine.lib ..\..\lib\LLVMInstCombine.lib ..\..\lib\LLVMTarget.lib ..\..\lib\LLVMVectorize.lib ..\..\lib\LLVMTransformUtils.lib ..\..\lib\LLVMAnalysis.lib ..\..\lib\LLVMProfileData.lib ..\..\lib\LLVMObject.lib ..\..\lib\LLVMMCParser.lib ..\..\lib\LLVMMC.lib ..\..\lib\LLVMDebugInfoCodeView.lib ..\..\lib\LLVMDebugInfoMSF.lib ..\..\lib\LLVMBitReader.lib ..\..\lib\LLVMTextAPI.lib ..\..\lib\LLVMCore.lib ..\..\lib\LLVMRemarks.lib ..\..\lib\LLVMBitstreamReader.lib ..\..\lib\LLVMBinaryFormat.lib ..\..\lib\LLVMSupport.lib psapi.lib shell32.lib ole32.lib uuid.lib advapi32.lib delayimp.lib -delayload:shell32.dll -delayload:ole32.dll ..\..\lib\LLVMDemangle.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib

Je pense que c’est une leçon vraiment très importante que chaque développeur devrait garder à l’esprit avec ce genre d’expérience : si vous vous sentez dépassé par les fonctions CMake utilisées dans un projet donné que vous voulez répliquer quelque part ailleurs, vérifiez les fichiers générés par CMake pour trouver les commandes de construction brutes et les paramètres pour le compilateur et l’éditeur de liens.

Bien sûr, avec ces nouvelles informations, j’ai immédiatement mis à jour mon fichier CMakeLists.txt pour test_llvm_opt ainsi :

1. SET(TARGET_NAME "test_llvm_opt")

2. SET(TARGET_DIR "./")

4. SET(LLVM_LIBS LLVMAArch64AsmParser.lib LLVMAMDGPUAsmParser.lib LLVMARMAsmParser.lib LLVMAVRAsmParser.lib LLVMBPFAsmParser.lib LLVMHexagonAsmParser.lib LLVMLanaiAsmParser.lib LLVMMipsAsmParser.lib LLVMMSP430AsmParser.lib LLVMPowerPCAsmParser.lib LLVMRISCVAsmParser.lib LLVMSparcAsmParser.lib LLVMSystemZAsmParser.lib LLVMWebAssemblyAsmParser.lib LLVMX86AsmParser.lib LLVMAArch64CodeGen.lib LLVMAMDGPUCodeGen.lib LLVMARMCodeGen.lib LLVMAVRCodeGen.lib LLVMBPFCodeGen.lib LLVMHexagonCodeGen.lib LLVMLanaiCodeGen.lib LLVMMipsCodeGen.lib LLVMMSP430CodeGen.lib LLVMNVPTXCodeGen.lib LLVMPowerPCCodeGen.lib LLVMRISCVCodeGen.lib LLVMSparcCodeGen.lib LLVMSystemZCodeGen.lib LLVMWebAssemblyCodeGen.lib LLVMX86CodeGen.lib LLVMXCoreCodeGen.lib LLVMAArch64Desc.lib LLVMAMDGPUDesc.lib LLVMARMDesc.lib LLVMAVRDesc.lib LLVMBPFDesc.lib LLVMHexagonDesc.lib LLVMLanaiDesc.lib LLVMMipsDesc.lib LLVMMSP430Desc.lib LLVMNVPTXDesc.lib LLVMPowerPCDesc.lib LLVMRISCVDesc.lib LLVMSparcDesc.lib LLVMSystemZDesc.lib LLVMWebAssemblyDesc.lib LLVMX86Desc.lib LLVMXCoreDesc.lib LLVMAArch64Info.lib LLVMAMDGPUInfo.lib LLVMARMInfo.lib LLVMAVRInfo.lib LLVMBPFInfo.lib LLVMHexagonInfo.lib LLVMLanaiInfo.lib LLVMMipsInfo.lib LLVMMSP430Info.lib LLVMNVPTXInfo.lib LLVMPowerPCInfo.lib LLVMRISCVInfo.lib LLVMSparcInfo.lib LLVMSystemZInfo.lib LLVMWebAssemblyInfo.lib LLVMX86Info.lib LLVMXCoreInfo.lib LLVMAggressiveInstCombine.lib LLVMAnalysis.lib LLVMBitWriter.lib LLVMCodeGen.lib LLVMCore.lib LLVMCoroutines.lib LLVMipo.lib LLVMIRReader.lib LLVMInstCombine.lib LLVMInstrumentation.lib LLVMMC.lib LLVMObjCARCOpts.lib LLVMRemarks.lib LLVMScalarOpts.lib LLVMSupport.lib LLVMTarget.lib LLVMTransformUtils.lib LLVMVectorize.lib LLVMPasses.lib LLVMAArch64Utils.lib LLVMAMDGPUUtils.lib LLVMMIRParser.lib LLVMARMUtils.lib LLVMHexagonAsmParser.lib LLVMHexagonDesc.lib LLVMHexagonInfo.lib LLVMLanaiAsmParser.lib LLVMLanaiDesc.lib LLVMLanaiInfo.lib LLVMRISCVUtils.lib LLVMMCDisassembler.lib LLVMCFGuard.lib LLVMGlobalISel.lib LLVMX86Utils.lib LLVMAsmPrinter.lib LLVMDebugInfoDWARF.lib LLVMSelectionDAG.lib LLVMCodeGen.lib LLVMCoroutines.lib LLVMipo.lib LLVMBitWriter.lib LLVMIRReader.lib LLVMAsmParser.lib LLVMFrontendOpenMP.lib LLVMLinker.lib LLVMInstrumentation.lib LLVMScalarOpts.lib LLVMAggressiveInstCombine.lib LLVMInstCombine.lib LLVMTarget.lib LLVMVectorize.lib LLVMTransformUtils.lib LLVMAnalysis.lib LLVMProfileData.lib LLVMObject.lib LLVMMCParser.lib LLVMMC.lib LLVMDebugInfoCodeView.lib LLVMDebugInfoMSF.lib LLVMBitReader.lib LLVMTextAPI.lib LLVMCore.lib LLVMRemarks.lib LLVMBitstreamReader.lib LLVMBinaryFormat.lib LLVMSupport.lib psapi.lib shell32.lib ole32.lib uuid.lib advapi32.lib delayimp.lib LLVMDemangle.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib)

6. SET(CMAKE_CXX_FLAGS "/std:c++14 -DGTEST_HAS_RTTI=0 -DUNICODE -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS -D_HAS_EXCEPTIONS=0 -D_SCL_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS /DWIN32 /D_WINDOWS   /Zc:inline /Zc:strictStrings /Oi /Zc:rvalueCast /W4 -wd4141 -wd4146 -wd4244 -wd4267 -wd4291 -wd4351 -wd4456 -wd4457 -wd4458 -wd4459 -wd4503 -wd4624 -wd4722 -wd4100 -wd4127 -wd4512 -wd4505 -wd4610 -wd4510 -wd4702 -wd4245 -wd4706 -wd4310 -wd4701 -wd4703 -wd4389 -wd4611 -wd4805 -wd4204 -wd4577 -wd4091 -wd4592 -wd4319 -wd4709 -wd4324 -w14062 -we4238 /Gw /MD /O2 /Ob2 /DNDEBUG  /EHs-c- /GR-")

8. include_directories(${LLVM_CLANG_DIR}/include)

9. LINK_DIRECTORIES(${LLVM_CLANG_DIR}/lib)

11. FILE(GLOB_RECURSE SOURCE_FILES "*.cpp" )

12. set(SOURCE_FILES ${SOURCE_FILES} windows_version_resource.rc)

14. ADD_EXECUTABLE (${TARGET_NAME} ${SOURCE_FILES})

15. TARGET_LINK_LIBRARIES(${TARGET_NAME} ${LLVM_LIBS})

17. INSTALL(TARGETS ${TARGET_NAME}

18. RUNTIME DESTINATION ${TARGET_DIR}

19. LIBRARY DESTINATION ${TARGET_DIR})

De nouveau, le projet test compile juste bien, mais, cette fois, en exécutant la commande test_llvm_opt.exe -O3 -debugify-each -verify-each bcsection.ll -o result.bc, on obtient le même résultat que celui produit par l’outil opt officiel de LLVM : yyeeeeepppeeee ! Victoire ! ????

VI - Mettre à jour la configuration de construction de nvLLVM

À partir de là, la voie a commencé à se déblayer d’elle-même : mettre à jour les fichiers de construction CMake a corrigé le plantage silencieux dans mon projet test_llvm_opt ; naturellement, la prochaine étape logique a été d’appliquer les mêmes mises à jour au projet nvLLVM et son fichier principal. À partir de là, la voie a commencé à se déblayer d’elle-même : mettre à jour les fichiers de construction CMake a corrigé le plantage silencieux dans mon projet test_llvm_opt ; naturellement, la prochaine étape logique a été d’appliquer les mêmes mises à jour au projet nvLLVM et son fichier principal CMakeLists.txt a donc été modifié ainsi :

1. SET(TARGET_DIR "./")

3. include_directories(${LLVM_CLANG_DIR}/include)

4. LINK_DIRECTORIES(${LLVM_CLANG_DIR}/lib)

6. INCLUDE_DIRECTORIES(../nvCore/include)

8. SET(CMAKE_CXX_FLAGS "/std:c++14 -DGTEST_HAS_RTTI=0 -DUNICODE -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS -D_HAS_EXCEPTIONS=0 -D_SCL_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS /DWIN32 /D_WINDOWS   /Zc:inline /Zc:strictStrings /Oi /Zc:rvalueCast /W4 -wd4141 -wd4146 -wd4244 -wd4267 -wd4291 -wd4351 -wd4456 -wd4457 -wd4458 -wd4459 -wd4503 -wd4624 -wd4722 -wd4100 -wd4127 -wd4512 -wd4505 -wd4610 -wd4510 -wd4702 -wd4245 -wd4706 -wd4310 -wd4701 -wd4703 -wd4389 -wd4611 -wd4805 -wd4204 -wd4577 -wd4091 -wd4592 -wd4319 -wd4709 -wd4324 -w14062 -we4238 /Gw /MD /O2 /Ob2 /DNDEBUG  /EHs-c- /GR-")

9. ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS)

10. ADD_DEFINITIONS(-DNOMINMAX)

12. # Note: used llvm-config.exe --libs to retrieve the list of libraries below:

13. # LLVM version 11.0.0git

14. SET(LLVM_LIBS LLVMXRay LLVMWindowsManifest LLVMTableGen LLVMSymbolize LLVMDebugInfoPDB LLVMOrcJIT LLVMOrcError LLVMJITLink LLVMObjectYAML LLVMMCA LLVMLTO LLVMPasses LLVMCoroutines LLVMObjCARCOpts LLVMLineEditor LLVMLibDriver LLVMInterpreter LLVMFuzzMutate LLVMMCJIT LLVMExecutionEngine LLVMRuntimeDyld LLVMDWARFLinker LLVMDlltoolDriver LLVMOption LLVMDebugInfoGSYM LLVMCoverage LLVMXCoreDisassembler LLVMXCoreCodeGen LLVMXCoreDesc LLVMXCoreInfo LLVMX86Disassembler LLVMX86AsmParser LLVMX86CodeGen LLVMX86Desc LLVMX86Utils LLVMX86Info LLVMWebAssemblyDisassembler LLVMWebAssemblyCodeGen LLVMWebAssemblyDesc LLVMWebAssemblyAsmParser LLVMWebAssemblyInfo LLVMSystemZDisassembler LLVMSystemZCodeGen LLVMSystemZAsmParser LLVMSystemZDesc LLVMSystemZInfo LLVMSparcDisassembler LLVMSparcCodeGen LLVMSparcAsmParser LLVMSparcDesc LLVMSparcInfo LLVMRISCVDisassembler LLVMRISCVCodeGen LLVMRISCVAsmParser LLVMRISCVDesc LLVMRISCVUtils LLVMRISCVInfo LLVMPowerPCDisassembler LLVMPowerPCCodeGen LLVMPowerPCAsmParser LLVMPowerPCDesc LLVMPowerPCInfo LLVMNVPTXCodeGen LLVMNVPTXDesc LLVMNVPTXInfo LLVMMSP430Disassembler LLVMMSP430CodeGen LLVMMSP430AsmParser LLVMMSP430Desc LLVMMSP430Info LLVMMipsDisassembler LLVMMipsCodeGen LLVMMipsAsmParser LLVMMipsDesc LLVMMipsInfo LLVMLanaiDisassembler LLVMLanaiCodeGen LLVMLanaiAsmParser LLVMLanaiDesc LLVMLanaiInfo LLVMHexagonDisassembler LLVMHexagonCodeGen LLVMHexagonAsmParser LLVMHexagonDesc LLVMHexagonInfo LLVMBPFDisassembler LLVMBPFCodeGen LLVMBPFAsmParser LLVMBPFDesc LLVMBPFInfo LLVMAVRDisassembler LLVMAVRCodeGen LLVMAVRAsmParser LLVMAVRDesc LLVMAVRInfo LLVMARMDisassembler LLVMARMCodeGen LLVMARMAsmParser LLVMARMDesc LLVMARMUtils LLVMARMInfo LLVMAMDGPUDisassembler LLVMAMDGPUCodeGen LLVMMIRParser LLVMipo LLVMInstrumentation LLVMVectorize LLVMLinker LLVMIRReader LLVMAsmParser LLVMFrontendOpenMP LLVMAMDGPUAsmParser LLVMAMDGPUDesc LLVMAMDGPUUtils LLVMAMDGPUInfo LLVMAArch64Disassembler LLVMMCDisassembler LLVMAArch64CodeGen LLVMCFGuard LLVMGlobalISel LLVMSelectionDAG LLVMAsmPrinter LLVMDebugInfoDWARF LLVMCodeGen LLVMTarget LLVMScalarOpts LLVMInstCombine LLVMAggressiveInstCombine LLVMTransformUtils LLVMBitWriter LLVMAnalysis LLVMProfileData LLVMObject LLVMTextAPI LLVMBitReader LLVMCore LLVMRemarks LLVMBitstreamReader LLVMAArch64AsmParser LLVMMCParser LLVMAArch64Desc LLVMMC LLVMDebugInfoCodeView LLVMDebugInfoMSF LLVMBinaryFormat LLVMAArch64Utils LLVMAArch64Info LLVMSupport LLVMDemangle)

16. SET(FLAVOR_LIBS psapi.lib shell32.lib ole32.lib uuid.lib advapi32.lib delayimp.lib LLVMDemangle.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib)

18. SET(CLANG_LIBS clangAST clangBasic clangLex clangCodeGen clangFrontend clangEdit

19. clangSerialization clangSema clangDriver clangParse clangAnalysis)

21. INCLUDE_DIRECTORIES (include)

23. FILE(GLOB_RECURSE PUBLIC_HEADERS "include/*.h")

25. FILE(GLOB_RECURSE SOURCE_FILES "src/*.cpp" )

27. ADD_SUBDIRECTORY(src)

Notez que, dans le code ci-dessus, je n’ai même plus besoin que CMake appelle findPackage() pour importer les macros utilitaires…

Une fois de plus, tout s’est très bien passé : j’ai juste dû faire quelques changements mineurs dans le code, et ensuite, l’appel à la fonction optimizeModule() s’est exécuté juste parfaitement !

Et, cerise sur le gâteau, j’ai pu maintenant aussi supprimer le lljit.release() dans le destructeur de NervJITImpl !

- Comme résultat, il semble que mon module nvLLVM et la nouvelle classe NervJIT que j’ai construits ici ne plantent plus silencieusement et n’ont plus de fuite de mémoire ! Liiiiibre… Ça m’a donné du mal : j’ai eu une dure journée, mais ça en valait la peine !

C’est tout pour aujourd’hui, les gars ! À nouveau, si quelqu’un veut voir le code plus en détail, voici un package ZIP mis à jour contenant la version courante du module nvLLVM ainsi que les projets test_nvLLVM et test_llvm_opt : nv_llvm_20200416.zip.

Je ne les ai pas mentionnées ci-dessus, mais le code fourni dans le package ZIP contient aussi certaines petites mises à jour additionnelles, telles qu’un mécanisme approprié pour spécifier le chemin de recherche des fichiers d’en-tête, la suppression de la dépendance au module nvCore (ainsi il devrait être beaucoup plus facile pour quiconque de reconstruire quelque chose d’après ce code) et d’autres nettoyages mineurs.

VII - Remerciements Developpez.com

Ce tutoriel est la traduction de JIT Compiler with LLVM - Part 3 - Fixing the ModulePassManager crash. Nous tenons à remercier Thierry Jeanneret pour la traduction, Thibaut Cuvelier pour la relecture technique, Malick pour la mise au gabarit et Claude Leloup pour la relecture orthographique.