3
votes

Fichier CMakeList pour générer un fichier bitcode LLVM à partir du fichier source C

J'essaie de générer un fichier bytecode LLVM à partir d'un fichier source C (hello.c) en utilisant CMake. Et ci-dessous est mon fichier CMakeLists.

###### CMakelists.txt ############
cmake_minimum_required(VERSION 2.8.9)
set(CMAKE_C_COMPILER "clang")
set(CMAKE_C_FLAGS "-emit-llvm")

project (hello)
add_executable(hello hello.c)
  1. Je suis nouveau sur CMake et je ne sais pas si c'est la bonne manière. Je n'ai trouvé aucune règle à créer * .bc dans le MakeFile généré . Veuillez me corriger ici. J'ai aussi essayé "-save-temps"

  2. Considérant cela pour un seul fichier .c. Ce serait vraiment utile si vous pouviez me donner quelques conseils pour générer la même chose pour un projet C complet.


0 commentaires

3 Réponses :


2
votes

Je pense que ce que vous voulez en fin de compte, c'est de pouvoir créer un programme C projet avec CMake et clang dans lequel les fichiers source sont compilés en bitcode LLVM et l'exécutable est lié à partir des fichiers bitcode.

Avec CMake, demander à clang to de lier des fichiers bitcode signifie lui demander de créer un lien dans mode LTO , avec l'option de liaison -flto .

Et vous pouvez obtenir clang pour compiler en bitcode LLVM avec la compilation -flto ou avec l'option -emit-llvm .

Pour illustration, voici un projet Hello World comprenant deux fichiers source et un en-tête:

$ rm CMakeFiles/hello.dir/hello.c.o
$ make VERBOSE=1 hello.o
make -f CMakeFiles/hello.dir/build.make CMakeFiles/hello.dir/hello.c.o
make[1]: Entering directory '/home/imk/develop/so/scrap/build'
Building C object CMakeFiles/hello.dir/hello.c.o
clang   -flto -o CMakeFiles/hello.dir/hello.c.o   -c /home/imk/develop/so/scrap/hello.c
make[1]: Leaving directory '/home/imk/develop/so/scrap/build'

Voici le:

CMakeLists.txt

$ egrep  -A3 'hello.o.*:.*hello.c.o' Makefile 
hello.o: hello.c.o

.PHONY : hello.o

Cela fonctionnera aussi bien avec:

$ rm CMakeFiles/hello.dir/hello.c.o
$ make VERBOSE=1 hello.c.o
make -f CMakeFiles/hello.dir/build.make CMakeFiles/hello.dir/hello.c.o
make[1]: Entering directory '/home/imk/develop/so/scrap/build'
Building C object CMakeFiles/hello.dir/hello.c.o
clang   -flto -o CMakeFiles/hello.dir/hello.c.o   -c /home/imk/develop/so/scrap/hello.c
make[1]: Leaving directory '/home/imk/develop/so/scrap/build'

Créer un répertoire de construction pour CMake et allez-y:

$ make VERBOSE=1 hello.c.o
make -f CMakeFiles/hello.dir/build.make CMakeFiles/hello.dir/hello.c.o
make[1]: Entering directory '/home/imk/develop/so/scrap/build'
make[1]: 'CMakeFiles/hello.dir/hello.c.o' is up to date.
make[1]: Leaving directory '/home/imk/develop/so/scrap/build'

Générez le système de construction:

$ file $(find -name '*.o')
./CMakeFiles/hello.dir/hello.c.o: LLVM IR bitcode
./CMakeFiles/hello.dir/main.c.o:  LLVM IR bitcode

Build:

$ ./hello 
Hello World!

Vous ne trouverez aucune cible * .bc dans les Makefiles, ni aucun fichier * .bc généré:

$ file $(find -name '*.o')
./CMakeFiles/hello.dir/hello.c.o: LLVM IR bitcode
./CMakeFiles/hello.dir/main.c.o:  LLVM IR bitcode

car l'option de compilation -flto ou -emit-llvm aboutit à une sortie file:

CMakeFiles/hello.dir/main.c.o
CMakeFiles/hello.dir/hello.c.o

qui adhère à la convention de dénomination habituelle de CMake mais qui n'est en fait pas un fichier objet mais un fichier de bitcode LLVM, comme vous le voyez:

$ egrep -r '.*\.bc'; echo Done
Done
$ find -name '*.bc'; echo Done
Done

Le programme fait la chose habituelle:

$ make
Scanning dependencies of target hello
[ 33%] Building C object CMakeFiles/hello.dir/main.c.o
[ 66%] Building C object CMakeFiles/hello.dir/hello.c.o
[100%] Linking C executable hello
[100%] Built target hello

Plus tard

Quand j'essaye "make hello.o", il devrait générer le fichier objet, n'est-ce pas? la cmd s'exécute avec succès mais n'a pas pu trouver le fichier objet généré. Est-ce que je le fais correctement?

Vous le faites d'une manière correcte, mais pas de la seule manière correcte, mais vos attentes sont fausses. Regardez à nouveau:

$ cmake ..

Vous pouvez voir ici que les fichiers .o qui sont faits à partir de hello.c et main.c par le makefile généré par CMake ne sont pas appelés hello.o et main.o mais hello.c.o et main.c.o . CMake préfère un nom de fichier compilé pour conserver l'extension du fichier source et ajoutez .o . C'est une pratique assez courante. Alors si tu voulais pour utiliser le makefile pour compiler hello.c , la manière la plus appropriée serait make hello.c.o .

Voyons ce qui se passe réellement. Dans mon répertoire de construction CMake:

$ mkdir build
$ cd build

Il n'y avait rien à faire, car mon hello.c.o était à jour. Alors je vais supprimez-le et répétez:

#target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -flto)
target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -emit-llvm)

Maintenant, il a été recompilé.

Cependant, parce que beaucoup de gens - comme vous - s'attendraient à bonjour.o à compiler à partir de hello.c , CMake définit utilement hello.o comme un .PHONY target cela dépend de hello.co:

cmake_minimum_required(VERSION 3.0.2)
project (hello)
set(CMAKE_C_COMPILER clang)
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} "-flto")
add_executable(hello main.c hello.c)
target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -flto)
#target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -emit-llvm)

Donc en fait je peux faire:

$ ls -R
.:
CMakeLists.txt  hello.c  hello.h  main.c

make hello.o est une autre façon de faire hello.co


3 commentaires

Merci pour l'explication détaillée. J'ai essayé et j'ai eu une erreur liée à Gold Linker. "erreur de chargement du plugin: /usr/bin/../lib/LLVMgold.so: impossible d'ouvrir le fichier objet partagé: aucun fichier ou répertoire de ce type" Je travaille dessus Mais, quand j'essaye "make hello.o", il devrait générer le droit de fichier objet? le cmd s'exécute avec succès mais n'a pas pu trouver le fichier objet généré. Suis-je en train de le faire correctement?


Merci Monsieur, j'ai compris cela. Je veux vous demander une aide supplémentaire. L'objectif principal est de générer un seul fichier bitcode LLVM complet. Et LLVMgold doit être exclu. Nous avons donc maintenant les fichiers bitcode, ce qu'il faut ajouter dans CMakelists.txt pour qu'il utilise llvm-link pour générer un fichier bitcode final reliant les précédents. Pourriez-vous s'il vous plaît m'aider avec ça? Je vous remercie


@ manojh93 Désolé, sur Stackoverflow, vous ne pouvez pas continuer à transformer une question en nouvelles questions. Voir Que dois-je faire lorsque quelqu'un répond à ma question? . Si vous rencontrez un problème au-delà de la question d'origine que vous ne pouvez pas résoudre vous-même, vous devez poser une nouvelle question



2
votes

Le problème est que l'utilisation de l'indicateur -emit-llvm ne produit pas de binaire final et arrête les tests de configuration que CMake effectue une fois que cet indicateur est utilisé.

En dehors de ce qui est déjà ont été écrits sur l'utilisation de l'infrastructure LTO , vous avez 3 (ou 2 et demi) autres alternatives .

La première consiste à utiliser Whole-Program LLVM et à utiliser les commandes fourni pour extraire les parties de bitcode pertinentes.

L'autre consiste à configurer manuellement des cibles personnalisées (voir add_custom_target et add_custom_command ) sur vos cibles binaires CMake, qui seront déclenchées lors des changements et reproduira le résultat souhaité comme s'il était exécuté manuellement sur la ligne de commande à chaque fois.

Maintenant, sur ce dernier point, j'avais un besoin similaire alors j'ai créé un projet CMake qui fournit cette fonctionnalité ( llvm-ir-cmake-utils ), mais vous permet de connecter ces cibles personnalisées à des ceux que vous voulez et que vous voulez sans avoir à tout réécrire à partir de zéro à chaque fois.

Il existe des exemples dans le dépôt, mais en bref, il vous permet d'attacher des cibles personnalisées sur des cibles CMake déjà existantes, par exemple

[...]
add_executable(qux ${SOURCES})

[...]
# this will create a bitcode generating target 
# and allow it to depend on the initial target in order to detect source code changes
llvmir_attach_bc_target(qux_bc qux)
add_dependencies(qux_bc qux)
[...]


0 commentaires

1
votes

Après la création,

clang  -flto  -flto <hello.c.o> ..

si set (CMAKE_C_FLAGS "-emit-llvm")

écrit avant

###### CMakelists.txt ############
cmake_minimum_required(VERSION 2.8.9)
project (hello)
set(CMAKE_C_COMPILER "clang")
set(CMAKE_C_FLAGS "-flto")
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} "-flto")
add_executable(hello hello.c)
target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -flto)

Afin d'obtenir le bitcode IR, j'ai écrit:

project (hello)

J'ai travaillé plusieurs heures afin de faire travailler un Makefile à compiler depuis IR code en natif en utilisant lld, puis avec cmake c'était beaucoup plus rapide. Puis en lisant le Makefile généré par cmake, j'ai pu corriger mon Makefile:

$>file CMakeFiles/hello.dir/hello.c.o
CMakeFiles/hello.dir/hello.c.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

cela a fonctionné mais je ne sais pas pourquoi -flto est écrit deux fois.

Merci beaucoup pour ce post, montrant clang comme le frontal centralisé de diverses commandes fournies par llvm.


0 commentaires