Manualinux
http://www.nvu.com http://www.gimp.org InicioPresentaciónActualizacionesManualesDescargasNoticiasAgradecimientoEnlaces

Entornos GráficosAplicaciones

DesarrolloEmuladoresInternetJuegosMultimediaSistema

Instalar Binutils desde ceroInstalar CMake desde cero

Instalar Clang desde cero

Página - 1Página - 2




Instalar Clang desde cero




Copyright

Copyright © José Luis Lara Carrascal  2012-2024   http://manualinux.es



Sumario

Introducción
Instalación
Configurar el sistema para el uso de Clang
Optimizaciones de CPU para Clang
Niveles de optimización soportados por Clang
Optimizaciones adicionales para Clang
LLD - El enlazador dinámico de LLVM
Libc++ - La librería estándar de C++ de LLVM
Comparativa de resultados de optimización entre Clang y GCC
Enlaces




Introducción  

Clang es un software creado para proveer al compilador LLVM de una interfaz para poder compilar código escrito en C, C++, Objective C y Objective C++. Compatible con GCC, es más rápido y utiliza menos memoria que este último, aunque no soporta de momento, todas las características de GCC. En este manual trataremos su instalación y la de LLVM, además de explicar la forma de configurar el sistema para su uso, y las diferencias respecto a GCC, en lo que concierne a las optimizaciones de procesador. A partir de la publicación de este manual, todos los manuales de la web que sean compatibles con Clang, incluirán la correspondiente información para su uso, tomando como referencia la instalación tratada en este manual. Desde el 2 de mayo de 2018, el manual se ha actualizado a una instalación en un sistema de 64 bits multiarquitectura.



Instalación

Dependencias

Herramientas de Compilación


Entre paréntesis la versión con la que se ha compilado Clang para la elaboración de este documento.

* GCC - (13.2.0) o Clang - (18.1.0)
* CMake - (3.28.3)
* Ninja - (1.11.1)
* Pkg-config - (0.29.2)

Librerías de Desarrollo

* GNU Binutils - (2.42)
* Elfutils - (0.191)
* Libffi - (3.4.4)
* Libxml2 - (2.11.7)
* Libzstd - (1.5.5)
* Ncurses - (6.4)
* SPIRV-Tools - (2023.2)
* Zlib - (1.3.1)

Intérpretes de Lenguaje de Programación

* Perl - (5.38.0)
* Python - (3.12.2)
   Pygments - (2.17.2)
   PyYAML - (6.0.1)

Aplicaciones

* Sphinx - (7.2.6)



Descarga

llvm-18.1.2.tar.xz

Firma Digital  Clave pública PGP

llvm-18.1.2.tar.xz.asc

Verificar la firma digital del paquete

$ gpg --import manualinux.asc
$ gpg --verify llvm-18.1.2.tar.xz.asc llvm-18.1.2.tar.xz

Optimizaciones

$ export {C,CXX}FLAGS='-O3 -march=znver3 -mtune=znver3'

Donde pone znver3 se indica el procesador respectivo de cada sistema seleccionándolo de la siguiente tabla:
Nota informativa sobre las optimizaciones para GCC
* La opción '-march=' establece el procesador mínimo con el que funcionará el programa compilado, la opción '-mtune=' el procesador específico para el que será optimizado. 

* Los valores separados por comas, son equivalentes, es decir, que lo mismo da poner '-march=k8' que '-march=athlon64'.

* En versiones de GCC 3.2 e inferiores se utiliza la opción '-mcpu=' en lugar de '-mtune='.
Nota informativa sobre las optimizaciones para Clang
* La opción '-mtune=' está soportada a partir de la versión 3.4 de Clang.

* Los valores de color azul no son compatibles con Clang.

* Las filas con el fondo de color amarillo son valores exclusivos de Clang y, por lo tanto, no son aplicables con GCC.
Valores CPU
Genéricos
Intel
AMD

Optimizaciones adicionales

Optimizaciones adicionales
GCC
Graphite
$ export {C,CXX}FLAGS+=' -floop-interchange -ftree-loop-distribution -floop-strip-mine -floop-block'
Clang
Polly
$ export {C,CXX}FLAGS+=' -O3 -mllvm -polly -mllvm -polly-vectorizer=stripmine'
Unified LTO
LTO >> ThinLTO
$ export {C,CXX}FLAGS+=' -funified-lto'
$ export LDFLAGS+=' -Wl,--lto=thin'
ThinLTO >> LTO
$ export {C,CXX}FLAGS+=' -funified-lto'
$ export LDFLAGS+=' -Wl,--lto=full'
La aplicación de esta optimización es aplicable, a partir de Clang 17, y sólo es combinable con LLD. En este manual concreto, hay que combinarla con la opción -DLLVM_ENABLE_LTO=ON del proceso de configuración del paquete, si utilizamos la primera opción o, -DLLVM_ENABLE_LTO=Thin, si utilizamos la segunda opción. Lo recomendable siempre, es utilizar la primera opción.

Parámetros adicionales para la versión de 32 bits  

Optimizaciones de CPU para Intel en sistemas de 64 bits multiarquitectura
$ export {C,CXX}FLAGS='-O3 -march=i686 -mtune=pentium4'

Optimizaciones de CPU para AMD en sistemas de 64 bits multiarquitectura
$ export {C,CXX}FLAGS='-O3 -march=i686 -mtune=athlon-xp'

Establecer la variable de entorno adecuada para pkg-config en sistemas de 64 bits multiarquitectura
$ export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig:/usr/share/pkgconfig:$PKG_CONFIG_PATH

Establecer la ruta de búsqueda de directorios de librerías en sistemas de 64 bits multiarquitectura
$ export LDFLAGS+=' -L/usr/lib -L/usr/local/lib -L/opt/gcc13/lib'

Establecer la variable de entorno de arquitectura de procesador requerida en sistemas de 64 bits multiarquitectura basados en el CLFS
$ export USE_ARCH=32

Establecer la variable de entorno de uso de compilador en modo de 32 bits, en sistemas de 64 bits multiarquitectura
GCC
$ export CC="gcc -m32" CXX="g++ -m32"
Clang
$ export CC="clang -m32" CXX="clang++ -m32"

Parámetros adicionales para la versión de 64 bits

Establecer la variable de entorno de uso de compilador para Clang
$ export CC=clang CXX=clang++

Parámetros adicionales globales

Parámetros adicionales de eliminación de avisos en el proceso de compilación
$ export {C,CXX}FLAGS+=' -w'

Establecer el uso de enlazador dinámico para LLD
Clang
$ export LDFLAGS+=' -fuse-ld=lld'
Optimizaciones complementarias LTO/ThinLTO de LLD
$ export LDFLAGS+=' -Wl,--lto-aa-pipeline=globals-aa -Wl,--lto-newpm-passes=memcpyopt'
Optimizaciones complementarias LTO de LLD
$ export LDFLAGS+=" -Wl,--lto-partitions=$(expr $(nproc) / 2)"
Optimizaciones complementarias ThinLTO de LLD
$ export LDFLAGS+=" -Wl,--thinlto-jobs=$(expr $(nproc) / 2)"

Extracción  Bloc de Notas Información general sobre el uso de los comandos

$ tar Jxvf llvm-18.1.2.tar.xz
$ cd llvm-18.1.2

1) Configuración de la versión de 32 bits
2) Configuración de la versión de 64 bits estática
3) Configuración de la versión de 64 bits compartida

1) Configuración de la versión de 32 bits

La compilación de la versión de 32 bits de LLVM sólo es necesaria si tenemos pensado compilar la versión de 32 bits de Mesa con soporte de LLVM, o somos usuarios de una tarjeta gráfica de AMD como es mi caso y tenemos que hacer uso del controlador de gráficos, AMDGPU. A diferencia de GCC, LLVM no compila las librerías de 32 bits en el mismo proceso de compilación, en un sistema de 64 bits multiarquitectura.

$ cmake -S llvm -B build32 -DLLVM_ENABLE_PROJECTS="clang;polly" -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/opt/llvm18 -DLLVM_TARGETS_TO_BUILD="X86;AMDGPU;BPF" \
-DBUILD_SHARED_LIBS=ON -DLLVM_ENABLE_RTTI=ON -DLLVM_ENABLE_FFI=ON -DOCAMLFIND=null \
-DCLANG_BUILD_TOOLS=OFF -DLLVM_ENABLE_DUMP=ON -DLLVM_SPIRV_INCLUDE_TESTS=OFF -G Ninja

Explicación de los comandos

-S llvm -B build32 : Establece el directorio del código fuente y crea de forma automática el directorio de compilación.

-DLLVM_ENABLE_PROJECTS="clang;polly": Activa la compilación de los proyectos clang y polly, requeridos para poder compilar el soporte de OpenCL en Mesa, de las tarjetas gráficas de AMD, utilizando el controlador Clover de Gallium.

-DCMAKE_BUILD_TYPE=Release : Compila la versión optimizada de LLVM.

-DCMAKE_INSTALL_PREFIX=/opt/llvm18 : Instala LLVM en /opt/llvm18.

-DLLVM_TARGETS_TO_BUILD="X86;AMDGDPU;BPF" : Compila sólo el soporte de la arquitectura de procesador x86, el soporte del controlador de gráficos de código abierto, AMDGPU (requerido para poder compilar Mesa con soporte de las tarjetas gráficas de AMD) y el soporte de BPF, requerido para la compilación del paquete v4l-utils, omitiendo otras como ARM, PowerPC, etc. Esto reduce el tiempo de compilación y el tamaño del directorio de instalación del compilador. 

-DBUILD_SHARED_LIBS=ON : Compila las librerías compartidas en lugar de las estáticas. 

-DLLVM_ENABLE_RTTI=ON : Parámetro requerido para poder compilar Mesa con soporte de LLVM con el sistema de compilación, Meson.

-DLLVM_ENABLE_FFI=ON : Activa el soporte de Libffi, librería de características similares a GMP.

-DOCAMLFIND=null : Al no tener el paquete una opción específica para desactivar la adaptación de LLVM para el lenguaje de programación OCaml, le indicamos una ruta nula al ejecutable ocamlfind, para poder desactivar el soporte de esta dependencia. Si no tenemos instalado OCaml en nuestro sistema, esto no hay que añadirlo.

-DLLVM_CCACHE_BUILD=ON : Activa el uso de Ccache para compilar el paquete.

-DCLANG_BUILD_TOOLS=OFF : Desactiva la compilación del binario ejecutable de Clang, ya que sólo necesitaremos las librerías para poder compilar el soporte de OpenCL en Mesa, de las tarjetas gráficas de AMD, utilizando el controlador Clover de Gallium.

-DLLVM_ENABLE_DUMP=ON : Requerido para poder compilar el soporte del experimental controlador gallium-rusticl de las librerías Mesa.

-DLLVM_SPIRV_INCLUDE_TESTS=OFF : Omite la compilación de los tests de SPIRV-LLVM-Translator.

-G Ninja : Utiliza Ninja en lugar de GNU Make para compilar el paquete (opcional).

Parámetros de configuración opcionales  

-DLLVM_CCACHE_BUILD=ON : Activa el uso de Ccache en el proceso de compilación. Si tenemos configurado Ccache con los correspondientes enlaces simbólicos, no es necesario añadir esta opción. Pero sí tenemos que tener activadas las opciones de Ccache, hash_dirrun_second_cpp, que vienen activadas por defecto.

-DLLVM_ENABLE_LTO=ON|Thin : Activa el uso de la optimización LTO, tanto con GCC como con Clang, siempre en combinación con el parámetro anteriormente descrito. Si vamos a utilizar Clang, muy recomendable hacer uso de ThinLTO en lugar de LTO, estableciendo Thin en lugar de ON, en el valor de este parámetro, para reducir el tiempo de compilación del paquete (en versiones inferiores a Clang 17). A partir de la versión 17 de Clang, es recomendable utilizar la opción LTO, en combinación con las optimizaciones Unified LTO, si se utiliza Clang para compilar el paquete.

-DLLVM_ENABLE_LLD=ON : Este parámetro sólo es necesario si utilizamos la optimización ThinLTO con Clang, y no tenemos configurado un directorio caché para LLD en nuestro sistema, cuyo uso hemos establecido con la correspondiente variable de entorno de uso de enlazador dinámico para LLD. El proceso de configuración creará uno en la raíz del directorio de compilación con el nombre lto.cache y almacenará en el mismo los archivos objeto que se generen en el proceso de enlazado, evitando utilizar la memoria física del sistema, si tenemos montado el directorio /tmp con el sistema de archivos, Tmpfs. Si se establece LLD como enlazador dinámico, antes del comando de configuración del paquete, esta opción no será funcional.

-DLLVM_PARALLEL_LINK_JOBS=$(nproc) : Establece el número de procesos en paralelo de enlazado (por defecto es 2), tomando como referencia la información del número de núcleos de nuestro procesador, proporcionada por el sistema. Este parámetro sólo es aplicable con Ninja. Si compilamos el paquete con Clang, estableciendo las correspondientes variables de entorno de LLD de este manual, es recomendable no utilizar esta opción.

-DCMAKE_STRIP=/usr/bin/strip : Sustituye llvm-strip por strip, en el caso de que compilemos el paquete con Clang. El segundo es bastante mejor que el primero, a la hora de reducir el tamaño de los binarios compilados.

Compilación

$ ninja -C build32

Parámetros de compilación opcionales  

-v : Muestra más información en el proceso de compilación.

-j$(nproc) : Establece el número de procesos de compilación en paralelo, en función del número de núcleos e hilos que tenga nuestro procesador, tomando como referencia la información mostrada por el sistema con el comando correspondiente. Si nuestro procesador es mononúcleo de un solo hilo, no añadir esta opción.

Instalación como root de la versión de 32 bits

$ su
# ninja -C build32 install/strip
# mv /opt/llvm18/bin/llvm-config{,-32}
# exit
$ cd ..

Explicación de los comandos

mv /opt/llvm18/bin/llvm-config{,-32} : Renombramos el ejecutable encargado de mostrar la información sobre la ubicación de las librerías de LLVM en los procesos de compilación en los que sea necesario su uso, siguiendo el método utilizado por el CLFS.

2) Configuración de la versión de 64 bits estática

$ mkdir -p build64/bin; echo '--gcc-toolchain=/opt/gcc13' | tee build64/bin/clang{,++}.cfg &> /dev/null
$ cmake -S llvm -B build64 -DLLVM_ENABLE_PROYECTS="clang;lld;polly;compiler-rt;openmp" \
-DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/opt/llvm18 -DLLVM_TARGETS_TO_BUILD="X86;AMDGPU;BPF" \
-DLLVM_BINUTILS_INCDIR=/usr/include -DLLVM_BUILD_DOCS=ON -DLLVM_ENABLE_SPHINX=ON \
-DSPHINX_WARNINGS_AS_ERRORS=OFF -DOCAMLFIND=null -DLLVM_LIBDIR_SUFFIX=64 \
-DLLVM_ENABLE_RTTI=ON -DLLVM_ENABLE_FFI=ON -DLLVM_INSTALL_UTILS=ON -DLLVM_ENABLE_DUMP=ON \ -DLIB{,CXX,CXXABI,UNWIND,}_INSTALL_LIBRARY_DIR=/opt/llvm18/lib64 \
-DLLVM_SPIRV_INCLUDE_TESTS=OFF -DLIBOMP_ARCHER_SUPPORT=OFF -G Ninja

Explicación de los comandos

mkdir -p build64/bin; echo '--gcc-toolchain=/opt/gcc13' | tee build64/bin/clang{,++}.cfg &> /dev/null : Establece la ruta de instalación de GCC, en el caso de que utilicemos otra versión diferente a la principal del sistema o ésta, esté ubicada en un directorio no habitual, creando dos archivos de configuración para clang, clang.cfg y clang++.cfg, para la versión de Clang que se generará en el proceso de compilación. Esto viene a sustituir a la opción de configuración de CMake, -DGCC_INSTALL_PREFIX=, que pasa ser obsoleta a partir de la versión 18 de Clang y se retirará en la versión 19.

Estos mismos archivos también deberán ser creados en el directorio final de instalación de los ejecutables de Clang. Cuando se actualice GCC a una versión completa superior, ya no será necesario recompilar Clang, bastará con editar estos archivos y cambiar la ruta a la nueva versión de GCC instalada.

-S llvm -B build64
: Establece el directorio del código fuente y el directorio de compilación.

-DLLVM_ENABLE_PROJECTS="clang;lld;polly;compiler-rt;openmp": Activa la compilación de los proyectos separados por punto y coma, incluidos junto a LLVM en el paquete proporcionado en este manual. Además de estos proyectos, el paquete incluye los siguientes: bolt, clang-tools-extra, flang, libc, libcl, lldb, mlir y pstl, que podemos añadir si así lo deseamos, a este parámetro de configuración, aunque algunos como libclc, no se compilan de forma efectiva y hay que compilarlos aparte.

-DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" : A partir de la versión 16 de LLVM, no es posible incluir todos los paquetes como proyectos y debemos añadir este parámetro en lo que respecta a los paquetes libcxx, libcxxabi y libunwind, que serán compilados por la versión de Clang, generada en el proceso de compilación.

-DCMAKE_BUILD_TYPE=Release : Compila la versión optimizada de LLVM.

-DCMAKE_INSTALL_PREFIX=/opt/llvm18 : Instala el compilador en /opt/llvm18.

-DLLVM_TARGETS_TO_BUILD="X86;AMDGDPU;BPF" : Compila sólo el soporte de la arquitectura de procesador x86, el soporte del controlador de gráficos de código abierto, AMDGPU (requerido para poder compilar Mesa con soporte de las tarjetas gráficas de AMD) y el soporte de BPF, requerido para la compilación del paquete v4l-utils, omitiendo otras como ARM, PowerPC, etc. Esto reduce el tiempo de compilación y el tamaño del directorio de instalación del compilador.

-DLLVM_BINUTILS_INCDIR=/usr/include : Le indicamos la ruta al archivo de cabecera plugin-api.h, proporcionado por el paquete Binutils cuando se instala con soporte de plugins. Esta versión especial es requerida para poder compilar el plugin LLVM Gold, que nos permite poder disponer de la optimización LTO, en las compilaciones que realicemos con Clang.

-DLLVM_BUILD_DOCS=ON : Activa la creación e instalación de la documentación en formato HTML y páginas de manual de LLVMClang.

-DLLVM_ENABLE_SPHINX=ON : Utiliza el programa Sphinx para crear la documentación en formato HTML y páginas de manual de LLVM y Clang

-DSPHINX_WARNINGS_AS_ERRORS=OFF : Evita que los avisos sean tratados como errores, al generar la documentación en formato HTML y páginas de manual de LLVM y Clang.

-DOCAMLFIND=null : Al no tener el paquete una opción específica para desactivar la adaptación de LLVM para el lenguaje de programación OCaml, le indicamos una ruta nula al ejecutable ocamlfind, para poder desactivar el soporte de esta dependencia. Si no tenemos instalado OCaml en nuestro sistema, esto no hay que añadirlo.

-DLLVM_LIBDIR_SUFFIX=64 : Establece el nombre del directorio de las librerías de LLVM en lib64, para que no sean sobrescritas en el proceso de instalación, las librerías de la versión de 32 bits, ubicadas en /opt/llvm18/lib.

-DLLVM_ENABLE_RTTI=ON : Parámetro requerido para poder compilar Mesa con soporte de LLVM con el sistema de compilación, Meson.

-DLLVM_ENABLE_FFI=ON : Activa el soporte de Libffi, librería de características similares a GMP.

-DLLVM_CCACHE_BUILD=ON : Activa el uso de Ccache para compilar el paquete. No es necesario si tenemos configurado Ccache en nuestro sistema con el uso de los enlaces simbólicos correspondientes. 

-DLLVM_INSTALL_UTILS=ON : Instala las utilidades binarias FileCheck, count, lli-child-target, llvm-PerfectShutffle, not y yaml-bench que, en el caso de FileCheck es requerido en la compilación de algún paquete de código fuente, cuyo nombre ahora no recuerdo. Estas utilidades se compilan por defecto, pero para instalarlas hay que añadir esta variable al comando de configuración del paquete.

-DLLVM_ENABLE_DUMP=ON : Requerido para poder compilar el soporte del experimental controlador gallium-rusticl de las librerías Mesa.

-DLIB{,CXX,CXXABI,UNWIND,}_INSTALL_LIBRARY_DIR=/opt/llvm18/lib64 : Evita a partir de LLVM 15, que las librerías libc++, libc++abi y libunwind, se instalen en el directorio /opt/llvm18/lib64/x86_64-unknown-linux-gnu, ubicándolas en el directorio predefinido de siempre.

-DLIBOMP_ARCHER_SUPPORT=OFF : Desactiva la compilación de libarcher como dependencia de Libomp, destinada a depurar la ejecución de aplicaciones compatibles con OpenMP. La no inclusión de esta opción, obligaba a enlazar contra libtsan (proporcionada por GCC), todas las aplicaciones que se compilaban con este soporte, en Clang 17.

-G Ninja : Utiliza Ninja en lugar de GNU Make para compilar el paquete (opcional).

Parámetros de configuración opcionales  

-DGCC_INSTALL_PREFIX=/opt/gcc13 : Le indicamos la ruta de instalación de GCC, en el caso de que utilicemos otra versión diferente a la principal del sistema o ésta, esté ubicada en un directorio no habitual. Esta opción pasa a ser obsoleta a partir de la versión 18 de Clang y se retirará en la versión 19. La alternativa a esta opción ya se explica en este manual.

-DLLVM_CCACHE_BUILD=ON : Activa el uso de Ccache en el proceso de compilación. Si tenemos configurado Ccache con los correspondientes enlaces simbólicos, no es necesario añadir esta opción. Pero sí tenemos que tener activadas las opciones de Ccache, hash_dirrun_second_cpp, que vienen activadas por defecto. En la 3ª fase de compilación sí que es necesario añadirla.

-DLLVM_BUILD_LLVM_DYLIB=ON: Compila una librería única compartida de LLVM, a partir de las estáticas, para poder enlazar contra la misma, paquetes como las librerías Mesa. Si no tenemos pensado compilar la versión compartida de LLVM, tendremos que añadir esta opción a las opciones de configuración del paquete.

-DLLVM_TOOL_SPIRV_LLVM_TRANSLATOR_BUILD=OFF : Omite la compilación de SPIRV-LLVM-Translator, si no tenemos pensado compilar Libclc.

-DLLVM_ENABLE_LTO=ON|Thin : Activa el uso de la optimización LTO, tanto con GCC como con Clang, siempre en combinación con el parámetro anteriormente descrito. La reducción de los archivos binarios es irrelevante, penalizado por el aumento de tamaño de las librerías estáticas, si utilizamos la optimización ThinLTO con Clang. La ganancia de velocidad (10/15 % en las comparativas de este manual) y el menor tamaño de los binarios que genera el compilador, independientemente de la optimización que se utilice, sí es relevante. Si vamos a utilizar Clang, muy recomendable hacer uso de ThinLTO en lugar de LTO, estableciendo Thin en lugar de ON, en el valor de este parámetro, para reducir el tiempo de compilación del paquete.
(en versiones inferiores a Clang 17). A partir de la versión 17 de Clang, es recomendable utilizar la opción LTO, en combinación con las optimizaciones Unified LTO, si se utiliza Clang para compilar el paquete.

-DLLVM_ENABLE_LLD=ON : Este parámetro sólo es necesario si utilizamos la optimización ThinLTO con Clang, y no tenemos configurado un directorio caché para LLD en nuestro sistema, cuyo uso hemos establecido con la correspondiente variable de entorno de uso de enlazador dinámico para LLD. El proceso de configuración creará uno en la raíz del directorio de compilación con el nombre lto.cache y almacenará en el mismo los archivos objeto que se generen en el proceso de enlazado, evitando utilizar la memoria física del sistema, si tenemos montado el directorio /tmp con el sistema de archivos, Tmpfs. Si se establece LLD como enlazador dinámico, antes del comando de configuración del paquete, esta opción no será funcional.

-DLLVM_PARALLEL_LINK_JOBS=$(nproc) : Establece el número de procesos en paralelo de enlazado de los binarios (por defecto es 2), tomando como referencia la información del número de núcleos de nuestro procesador, proporcionada por el sistema. Este parámetro sólo es aplicable con Ninja.
Si compilamos el paquete con Clang, estableciendo las correspondientes variables de entorno de LLD de este manual, es recomendable no utilizar esta opción.

-DCMAKE_STRIP=/usr/bin/strip : Sustituye llvm-strip por strip, en el caso de que compilemos el paquete con Clang. El segundo es bastante mejor que el primero, a la hora de reducir el tamaño de los binarios compilados.

Compilación

$ ninja -C build64

Parámetros de compilación opcionales

Instalación como root de la versión de 64 bits estática

$ su
# ninja -C build64 install/strip
# mv /opt/llvm18/bin/llvm-config{,-64}
# ln -sf /opt/llvm18/lib64/LLVMgold.so /usr/lib64/bfd-plugins
# echo '--gcc-toolchain=/opt/gcc13' | tee /opt/llvm18/bin/clang{,++}.cfg &> /dev/null

Explicación de los comandos

mv /opt/llvm18/bin/llvm-config{,-64} : Renombramos el ejecutable encargado de mostrar la información sobre la ubicación de las librerías de LLVM en los procesos de compilación en los que sea necesario su uso, siguiendo el método utilizado por el CLFS.

ln -sf /opt/llvm18/lib64/LLVMgold.so /usr/lib64/bfd-plugins : Crea un enlace simbólico del plugin LTO de LLVM al directorio de plugins de las GNU Binutils. Esto evita tener que establecer las variables de entorno AR, NM y RANLIB, en los procesos de compilación que se utiliza la optimización LTO, con GNU gold. La ubicación del directorio de plugins, pueden variar según la distribución que estemos utilizando.

echo '--gcc-toolchain=/opt/gcc13' | tee /opt/llvm18/bin/clang{,++}.cfg &> /dev/null : Ya explicado más arriba, establece la ruta de instalación de GCC, en el caso de que utilicemos otra versión diferente a la principal del sistema o ésta, esté ubicada en un directorio no habitual, creando dos archivos de configuración, clang.cfg y clang++.cfg, en el directorio de instalación de los ejecutables de Clang.

Crear el binario ejecutable selector de arquitectura de procesador siguiendo el método del CLFS

Nos descargamos este archivo y lo compilamos como root con el siguiente comando, para posteriormente crear el enlace simbólico correspondiente para poder alternar de una arquitectura a otra, en el uso de dependencias, en función del proceso de compilación en curso.

# gcc multiarch_wrapper.c -o /opt/llvm18/bin/multiarch_wrapper
# ln -s multiarch_wrapper /opt/llvm18/bin/llvm-config

Cuando compilemos código de 32 bits, que necesite de las dependencias de las librerías de 32 bits de LLVM, ejecutamos la siguiente variable de entorno, antes de configurar los paquetes de código fuente correspondientes.

$ export USE_ARCH=32

Configuración de Libclc


Este apartado sólo es para los usuarios de tarjetas gráficas de AMD, que quieran hacer uso de OpenCL con las librerías Mesa. Para ello necesitaremos instalar Libclc y SPIRV-LLVM-Translator (requerido sólo el binario ejecutable para compilar Libclc) y también, requerido para compilar el soporte de SPIRV en el controlador Clover de Gallium de las librerías Mesa. La versión de SPIRV-LLVM-Translator tiene que ser siempre la misma que la serie de LLVM, incluyendo claro está las versiones de corrección de errores de LLVM. En el paquete incluido en este manual de LLVM, ya existe una versión que he añadido para ahorrarnos más enlaces de descarga.

Utilizaremos los binarios ejecutables creados en la anterior compilación, estableciendo las correspondientes variables de entorno.

$ export PATH=$PWD/build64/bin:$PATH
$ export LLVM_DIR=$PWD/build64/lib64/cmake/llvm
$ cd libclc
$ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/llvm18 -G Ninja

Compilación

$ ninja -C build

Instalación como root

$ su -c "ninja -C build install"
$ unset LLVM_DIR
$ cd ..

3) Configuración de la versión de 64 bits compartida (sólo librerías de LLVM, Clang, Polly y SPIRV-LLVM-Translator)

Esta opción soluciona problemas de enlazado de paquetes como SPIRV-LLVM-Translator, que mezclan librerías de LLVM compartidas con estáticas en el proceso de enlazado con duplicación de símbolos, produciendo errores de ejecución y compilación en paquetes como Mesa, cuando compilamos el soporte de OpenCL en las mismas. Lo bueno de esta opción es que reduce la instalación anterior de LLVM en 1,3 GB. Se utilizan los binarios ejecutables creados en la anterior compilación y establecemos el PATH correspondiente de su ubicación. Si estamos utilizando la misma ventana de terminal de la compilación de Libclc, no es necesario establecer la variable de entorno PATH.

$ export PATH=$PWD/build64/bin:$PATH
$ cmake -S llvm -B build64s -DLLVM_ENABLE_PROYECTS="clang;polly" -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/opt/llvm18 -DBUILD_SHARED_LIBS=ON -DLLVM_TARGETS_TO_BUILD="X86;AMDGPU;BPF" \
-DLLVM_BINUTILS_INCDIR=/usr/include -DOCAMLFIND=null -DLLVM_LIBDIR_SUFFIX=64 \
-DLLVM_ENABLE_RTTI=ON -DLLVM_ENABLE_FFI=ON -DCLANG_BUILD_TOOLS=OFF \
-DLLVM_ENABLE_LTO=ON -DLLVM_CCACHE_BUILD=ON -DLLVM_ENABLE_DUMP=ON \
-DLLVM_SPIRV_INCLUDE_TESTS=OFF -G Ninja

Compilación

$ ninja -C build64s

Instalación como root de la versión de 64 bits compartida (sólo librerías de LLVM, Clang, Polly y SPIRV-LLVM-Translator)

$ su
# ninja -C build64s install/strip
# mv /opt/llvm18/bin/llvm-config{,-64}
# ln -s multiarch_wrapper /opt/llvm18/bin/llvm-config

Borrar las librerías estáticas de la 2ª fase de compilación

Esto es opcional, pero su instalación ocupa casi 500 MB. Al instalar la versión compartida de LLVM, éstas quedan anuladas en cualquier proceso de compilación que realicemos, a no ser que forcemos una compilación estática, algo poco habitual en un proceso de compilación en nuestro sistema.

# find /opt/llvm18/lib64 -maxdepth 1 -name '*.a' -type f -delete

Borrar los archivos de instalación que hacen referencia a la versión 18.1.0, sobrescrita por esta nueva versión

Hacer sólo esto si estamos actualizando desde la versión anterior instalada siguiendo las instrucciones de este manual. Con el siguiente comando, borramos los archivos de instalación de la versión anterior.

# for i in 18.1.0 ; do \
rm -f /opt/llvm18/lib{,64}/libclang.so.$i ; \
done

Estadísticas de Compilación e Instalación de LLVM

Estadísticas de Compilación e Instalación de LLVM
CPU AMD Ryzen 5 5500
MHz 3593.250 (BoostMax=4457.000)
RAM 32 GB
Sistema de archivos XFS
Versión del Kernel 6.8.1-ml SMP PREEMPT_DYNAMIC x86_64
Modo de frecuencia de la CPU performance
Planificador de CPU BORE
Versión de Glibc 2.39
Enlazador dinámico LLD 18.1.0
Compilador Clang 18.1.0 + Ccache 4.9.1
Parámetros de optimización del modo de 32 bits -03 -march=i686 -mtune=athlon-xp -mllvm -polly -mllvm -polly-vectorizer=stripmine -flto -funified-lto -Wl,--lto=thin -Wl,--thinlto-jobs=6 -Wl,--lto-aa-pipeline=globals-aa -Wl,--lto-newpm-passes=memcpyopt
Parámetros de optimización del modo de 64 bits -03 -march=znver3 -mtune=znver3 -mllvm -polly -mllvm -polly-vectorizer=stripmine -flto -funified-lto -Wl,--lto=thin -Wl,--thinlto-jobs=6 -Wl,--lto-aa-pipeline=globals-aa -Wl,--lto-newpm-passes=memcpyopt
Parámetros de compilación -v -j12
Ocupación de espacio en disco del proceso de compilación 10,1 GB
Tiempo de compilación de la versión de 32 bits 26' 54"
Tiempo de compilación de la versión de 64 bits estática (incluye libclc) 1h 00' 35"
Tiempo de compilación de la versión de 64 bits compartida 27' 04"
Tiempo total consumido 1h 54' 33"
Archivos instalados 8.294
Mostrar/Ocultar la lista de archivos instalados
Enlaces simbólicos creados 455
Mostrar/Ocultar la lista de enlaces simbólicos creados
Ocupación de espacio en disco 1,6 GB

Desinstalación como root

1) MODO TRADICIONAL

Este programa no tiene soporte para desinstalación con el comando 'ninja uninstall' 

2) MODO MANUALINUX

El principal inconveniente del comando anterior es que tenemos que tener el directorio de compilación en nuestro sistema para poder desinstalar el programa. En algunos casos esto supone muchos megas de espacio en disco. Con el paquete de scripts que pongo a continuación logramos evitar el único inconveniente que tiene la compilación de programas, y es el tema de la desinstalación de los mismos sin la necesidad de tener obligatoriamente una copia de las fuentes compiladas.

llvm-18.1.2-scripts.tar.gz

$ su
# tar zxvf llvm-18.1.2-scripts.tar.gz
# cd llvm-18.1.2-scripts
# ./Desinstalar_llvm-18.1.2

Copia de Seguridad como root

Con este otro script creamos una copia de seguridad de los binarios compilados, recreando la estructura de directorios de los mismos en un directorio de copias de seguridad (copibin) que se crea en el directorio /var. Cuando se haya creado el paquete comprimido de los binarios podemos copiarlo como usuario a nuestro home y borrar el que ha creado el script de respaldo, teniendo en cuenta que si queremos volver a restaurar la copia, tendremos que volver a copiarlo al lugar donde se ha creado.

$ su
# tar zxvf llvm-18.1.2-scripts.tar.gz
# cd llvm-18.1.2-scripts
# ./Respaldar_llvm-18.1.2

Restaurar la Copia de Seguridad como root

Y con este otro script (que se copia de forma automática cuando creamos la copia de respaldo del programa) restauramos la copia de seguridad como root cuando resulte necesario.

$ su
# cd /var/copibin/restaurar_copias
# ./Restaurar_llvm-18.1.2



Configurar el sistema para el uso de Clang

1) /etc/ld.so.conf

Añadimos la ruta a las librerías compartidas en el archivo /etc/ld.so.conf.

include ld.so.conf.d/*.conf
/usr/X11R6/lib
/usr/lib
/usr/lib/qt3/lib
/usr/local/lib
/opt/e17/lib
/opt/llvm18/lib64
/opt/llvm18/lib


Cuando lo hayamos editado y guardado ejecutamos la actualización de la caché de las librerías compartidas.

$ su -c "ldconfig -v"

2) Añadir la ruta a los binarios y las páginas de manual a nuestro PATH

2a) Variable de entorno PATH de usuario

Editamos el archivo de nuestro home, ~/.bashrc (si no existe lo creamos) y añadimos lo siguiente al final del mismo:

export PATH=/opt/llvm18/bin:$PATH
export MANPATH=/opt/llvm18/share/man:$MANPATH


2b) Variable de entorno PATH del sistema

Si queremos establecer una variable de entorno global del sistema, abrimos un editor de texto y añadimos lo siguiente:

#!/bin/sh

export PATH=/opt/llvm18/bin:$PATH
export MANPATH=/opt/llvm18/share/man:$MANPATH


Lo guardamos con el nombre clang.sh, y lo instalamos en /etc/profile.d.

$ su -c "install -m755 clang.sh /etc/profile.d"

Tenemos que cerrar el emulador de terminal y volverlo a abrir para que la variable de entorno aplicada sea efectiva. Es conveniente guardar una copia de este script para posteriores instalaciones de nuestro sistema. La ventaja de utilizar el directorio /etc/profile.d es que es común a todas las distribuciones y nos evita tener que editar otros archivos del sistema como por ejemplo, /etc/profile.

Para comprobar que el binario clang está incluido en el path, abrimos una ventana de terminal y ejecutamos el siguiente comando:

[jose@localhost ~]$ clang --version
clang version 18.1.2
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /opt/llvm18/bin
Configuration file: /opt/llvm18/bin/clang.cfg

2c) Lectura de las páginas de manual

$ man clang

2d) Lectura de la documentación en formato HTML

/opt/llvm18/share/doc/LLVM/llvm/html/index.html  |  /opt/llvm18/share/doc/LLVM/clang/html/index.html

2) Establecer la variable PKG_CONFIG_PATH requerida por el programa pkg-config

Establecemos la correspondiente variable de entorno para que el programa pkg-config encuentre los archivos de referencia (*.pc) de las dependencias requeridas en los procesos de compilación pertinentes. Sólo es necesario si hemos instalado las librerías LLVMSPIRVLib y Libclc, siguiendo este manual.

En el archivo que hemos creado anteriormente (clang.sh) añadimos lo que está en rojo:

#!/bin/sh

export PATH=/opt/llvm18/bin:$PATH
export MANPATH=/opt/llvm18/share/man:$MANPATH

export PKG_CONFIG_PATH=/opt/llvm18/lib64/pkgconfig:/opt/llvm18/share/pkgconfig:$PKG_CONFIG_PATH

En la versión de 32 bits de LLVMSPIRVLib y, además, Libclc, que no es una librería propiamente dicho, allí donde se requiera su dependencia lo haremos de forma manual, añadiendo la ruta correspondiente a la variable de entorno PKG_CONFIG_PATH a establecer, un ejemplo:

$ export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig:\
/opt/llvm18/lib/pkgconfig:/opt/llvm18/share/pkgconfig:/usr/share/pkgconfig:$PKG_CONFIG_PATH



Optimizaciones de CPU para Clang  

Los procesadores soportados por Clang, son practicamente los mismos que los de GCC, con las excepciones en las que se permite más de una definición para hacer referencia a un determinado procesador. Por ejemplo, barcelona no está soportado por Clang, hasta la versión 3.6, con lo que tendremos que utilizar el otro término alternativo: amdfam10. El parámetro '-mtune' ha sido introducido a partir de la versión 3.4 de Clang. Si utilizamos versiones anteriores, tendremos que utilizar '-mcpu'.

La tabla que pongo a continuación no difiere mucho de la utilizada en los manuales de instalación ubicados en la web, las filas con el color amarillo indican las definiciones de procesador soportadas por Clang, que no aparecen en las opciones de optimización de GCC.

$ export {C,CXX}FLAGS='-O3 -march=znver3 -mtune=znver3'

Donde pone znver3 se indica el procesador respectivo de cada sistema seleccionándolo de la siguiente tabla:
Nota informativa sobre las optimizaciones para GCC
* La opción '-march=' establece el procesador mínimo con el que funcionará el programa compilado, la opción '-mtune=' el procesador específico para el que será optimizado. 

* Los valores separados por comas, son equivalentes, es decir, que lo mismo da poner '-march=k8' que '-march=athlon64'.

* En versiones de GCC 3.2 e inferiores se utiliza la opción '-mcpu=' en lugar de '-mtune='.
Nota informativa sobre las optimizaciones para Clang
* La opción '-mtune=' está soportada a partir de la versión 3.4 de Clang.

* Los valores de color azul no son compatibles con Clang.

* Las filas con el fondo de color amarillo son valores exclusivos de Clang y, por lo tanto, no son aplicables con GCC.
Valores CPU
Genéricos
Intel
AMD

Niveles de optimización soportados por Clang

Niveles de optimización soportados por Clang
-O0
Sin optimizaciones. Es el nivel más rápido para compilar programas y genera el código más depurable.
-O1
Un nivel intermedio entre -O0 y -O2.
-O2 Un nivel moderado de optimización.
-O3
Lo mismo que -O2, excepto que permite optimizaciones que alargan el proceso de compilación y pueden llegar a generar un binario de tamaño más grande, con la idea de hacer que se ejecute más rápido.
-Ofast
Activa todas las optimizaciones de -O3, junto con otras optimizaciones agresivas que pueden violar el estricto cumplimiento de los estándares del lenguaje de programación.
-Os
Lo mismo que -O2, con optimizaciones adicionales para reducir el tamaño del binario resultante.
-Oz Lo mismo que -Os, pero con una reducción de tamaño del binario resultante, más agresiva.
-O
Equivalente a -O2.
-O4 y superiores
Actualmente equivale a -O3.
-Og Equivalente a -O1.

Optimizaciones adicionales para Clang  

Optimizaciones adicionales para Clang
Polly
-mllvm -polly Activa el uso de Polly, un optimizador de características similares a la optimización Graphite de GCC. Este optimizador solo funciona en el nivel '-O3' de optimización. Puede provocar errores de compilación y de ejecución en determinados paquetes.

La definición de la optimización Graphite también es válida para Polly y consiste en utilizar la representación de la figura geométrica del poliedro para ejecutar en paralelo los bucles de programación que contenga el código fuente de un programa, en el binario resultante del proceso de compilación, acelerando de forma considerable el tiempo de ejecución del mismo.
-mllvm -polly-vectorizer=stripmine Activa la generación automática de código vectorizado, a través de Polly. Este parámetro debe ir precedido del anterior. Desde Clang 3.5 es el parámetro predefinido de Polly utilizado en los manuales de la web. A partir de la versión 3.7, se ha sustituido el valor polly por stripmine. El primer valor se ha eliminado a partir de Clang 17.
-mllvm -polly-parallel Activa la generación automática de código OpenMP, a través de Polly. Este parámetro debe ir precedido del inicial de esta sección. Este parámetro lo pongo como experimental, y sólo lo podremos aplicar en aquellos paquetes que contengan código OpenMP, en combinación con el utilizado por el compilador para dicho proceso. 
-mllvm-polly-omp-backend=LLVM Activa que Polly utilice libomp en lugar de libgomp (GNU) que es la predefinida, para generar el código OpenMP.
-mllvm -polly-position=before-vectorizer Activa que Polly se ejecute antes que LLVM en el proceso de optimización y no después, que es el modo de trabajar predefinido. Este parámetro debe ir precedido del inicial de esta sección. En determinados procesos de compilación puede mejorar la optimización del binario resultante. En otros la puede empeorar. Más información en este enlace.
-mllvm -polly-2nd-level-tiling Activa un segundo nivel de optimización de mosaico de bucle. Por defecto, Polly tiene activada la optimización de mosaico (tiling). Con esta opción hacemos que le dé otra pasada más profunda de este tipo de optimización específico, al código a compilar.
-mllvm -polly-pattern-matching-based-opts Activa optimizaciones basadas en patrones idénticos.
-mllvm -polly-loop-fusion-greedy Intenta fusionar de forma agresiva todos los bucles de programación, independientemente del rendimiento que pueda producir. Esta opción está a partir de la versión 14 de Polly.
LTO
-flto Activa la optimización LTO, equivalente a la optimización LTO de GCC. Puede provocar errores de compilación y de ejecución en determinados paquetes. Tenemos que tener instalado y configurado el paquete Binutils, con el enlazador dinámico ld.gold como el predefinido del sistema, para que este parámetro sea funcional. La otra alternativa es utilizar LLD como enlazador dinámico.

Además de haber compilado el plugin LLVM Gold como se explica en este manual. La carga de este plugin se realiza de forma automática, sin tener que añadir ningún parámetro adicional.

En procesos de compilación que se generen librerías estáticas, o que intervenga el programa nm de Binutils, tendremos que establecer la siguiente variable de entorno, antes de ejecutar el script de configuración del paquete.

$ export AR=llvm-ar RANLIB=llvm-ranlib NM=llvm-nm

En el manual de GCC explico de forma más extendida el porqué del uso de esta variable de entorno, en la sección de preguntas y respuestas relacionada con la optimización LTO.
ThinLTO
-flto=thin ThinLTO es una variante más agresiva de LTO, introducida a partir de la versión 3.9 de LLVM. Lo que se busca con esta optimización es reducir a la mínima expresión el tiempo necesario para generar el binario ejecutable correspondiente a partir de los archivos objeto que intervengan en el proceso de enlace, aprovechando la capacidad multinúcleo de los procesadores actuales, que es el principal inconveniente que se nos presenta cuando hacemos uso de la tradicional optimización LTO. Podemos encontrar más información y la correspondiente comparativa en esta entrada del blog de la web de LLVM.

A diferencia de LTO que utiliza sólo un archivo objeto para volcar el proceso de enlazado, ThinLTO divide el mismo en múltiples archivos objeto, lo que puede llegar a rebasar el límite de procesos por usuario establecidos en nuestro sistema, impidiendo por ejemplo, si tenemos un limite de 1024, que en paquetes como Inkscape o GTK3, no podamos utilizar esta optimización.

Para solucionar este inconveniente, primero comprobaremos el límite de procesos por usuario que tenemos en nuestro sistema con el siguiente comando:

[jose@localhost ~]$ ulimit -n
1024

En mi caso personal son 1024, que elevo a 4096, con el mismo comando al que le añado el número dado.

$ ulimit -n 4096

Como este valor es volátil y desaparece cuando reiniciamos el sistema, creamos el script correspondiente para que dicho valor quede establecido por defecto al inicio del sistema. Abrimos un editor de texto y añadimos lo siguiente:

#!/bin/sh

ulimit -n 4096


Lo guardamos con el nombre ulimit.sh y lo instalamos en /etc/profile.d.

$ su -c "install -m755 ulimit.sh /etc/profile.d"

En el siguiente reinicio del sistema, comprobamos que el valor establecido por el script es el que está en uso.

[jose@localhost ~]$ ulimit -n
4096

Unified LTO
-funified-lto
-Wl,--lto=full o thin
A partir de Clang 17, se introduce esta opción de compilación, que permite combinar una optimación LTO o ThinLTO en el proceso previo al enlazado, con una optimización LTO o ThinLTO, en el posterior enlazado con LLD. Podemos encontrar más información en este enlace. La aplicación real de la misma sirve para reducir el tiempo de compilación de una optimización LTO, al introducir la posibilidad de aplicar una optimización ThinLTO en el proceso de enlazado con LLD, al unificar el código LTO en una sóla estructura compatible con las dos optimizaciones. Si lo hacemos a la inversa, compilar con ThinLTO y enlazar con LTO, los tiempos de compilación se disparan de forma exponencial.

Un ejemplo con LTO+ThinLTO  de variable de entorno a  establecer sería este:

$ export {C,CXX}FLAGS+=" -flto -funified-lto"
$ export LDFLAGS+=" -Wl,--lto=thin"

Y viceversa,

$ export {C,CXX}FLAGS+=" -flto=thin -funified-lto"
$ export LDFLAGS+=" -Wl,--lto=full"

Las optimizaciones adicionales LTO y ThinLTO para LLD, siguen siendo igual de aplicables, en función del tipo de optimización que elijamos a utilizar por el enlazador.
OpenMP
-fopenmp=libomp OpenMP es una API de programación creada única y exclusivamente para aprovechar la potencia de los procesadores multinúcleo, si el nuestro no lo es, no es necesario seguir leyendo esto. Esta optimización sólo es operativa en aquellos paquetes de código fuente que están previamente preparados para utilizarla: ImageMagick, Blender, Inkscape, etc.

La versión actual de Clang es compatible con la versión 5.0 de OpenMP e inferiores. El parámetro a añadir varía del utilizado por GCC, y permite elegir qué librería utilizar para el enlazado del binario ejecutable. Si no especificamos ninguna y dejamos que el script de configuración del paquete detecte el parámetro -fopenmp, el mismo se enlazará contra la librería libomp de LLVM. En versiones antiguas de Clang, se enlaza por defecto contra la librería libgomp de GCC.

Desde la versión 3.9 de Clang, también se puede seleccionar la versión de OpenMP a utilizar por el compilador, en el caso de que el script de configuración del paquete, no detecte de forma correcta la versión más reciente. En este caso el parámetro a añadir al inicial, sería uno de los siguientes:

-fopenmp-version=31 >> OpenMP 3.1

-fopenmp-version=40 >> OpenMP 4.0

-fopenmp-version=45 >> OpenMP 4.5 (versión predefinida de Clang 10 si sólo se utiliza el parámetro -fopenmp)

-fopenmp-version=50 >> OpenMP 5.0 (a partir de Clang 10. Versión predefinida de Clang 11 si sólo se utiliza el parámetro -fopenmp)

-fopenmp-version=51 >> OpenMP 5.1 (a partir de Clang 11. Soporte en fase previa de implementación)

Que quedaría así en una variable de entorno, un ejemplo con OpenMP 4.5:

$ export {C,CXX}FLAGS+=" -fopenmp=libomp -fopenmp-version=45"

Es posible que en algunos procesos tengamos que añadir una variable de entorno LDFLAGS, tipo export LDFLAGS+=' -lomp' para que la librería de OpenMP proporcionada por LLVM intervenga en el proceso de enlazado. En paquetes como ImageMagick es necesario hacer una sustitución masiva después de ejecutar el script de configuración para sustituir el parámetro -lgomp, añadido de forma automática por el script de configuración, por el parámetro -lomp. Un ejemplo:

$ find . -name 'Makefile' -type f | xargs sed -i 's:-lgomp:-lomp:g'

Como experiencia personal con algunos paquetes, mi recomendación es que se utilicen los siguientes parámetros para el uso de esta optimización.

$ export {C,CXX}FLAGS+=" -fopenmp=libomp -fopenmp-version=50"
$ export LDFLAGS+=" -lomp"

En paquetes como LibRaw, si dejamos que el script de configuración establezca el parámetro -fopenmp por sí mismo, nos encontraremos que, la librería resultante del proceso de compilación, no quedará enlazada contra libomp y, por lo tanto, no hará uso de las características de esta optimización. En cambio, en otros paquetes como MAME, sí que funciona este parámetro sin que tengamos que añadir nada por nuestra cuenta.

NOTA IMPORTANTE= Con Clang 14, existe un error (inicialmente subsanado, pero que, en las siguientes versiones de corrección de errores, se ha vuelto a reproducir) que no añade de forma correcta, el directorio /opt/llvm18/lib64, a la ruta predefinida de búsqueda de librerías de Clang, lo que nos obliga a tener que completar la variable de entorno anterior con lo que está en rojo, siendo obligatorio el uso de esta variable siempre:

$ export {C,CXX}FLAGS+=" -fopenmp=libomp -fopenmp-version=50"
$ export LDFLAGS+=" -L$(llvm-config --libdir) -lomp"

Si no queremos hacer uso de la misma, la otra opción más simple es crear un enlace simbólico al directorio /usr/lib64 de la librería libomp:

# ln -s /opt/llvm18/lib64/libomp.so /usr/lib64

PGO
-fprofile-generate=ruta a directorio o archivo

-fprofile-use=ruta a directorio o archivo
PGO son las siglas con las que se conoce a la optimización Profile Guided Optimization (Optimización Guiada por Perfiles). El compilador recopila información del total de funciones y bloques de datos que contiene el binario ejecutable, tanto en el proceso de compilación como en una posterior ejecución del mismo. Dichos datos serán utilizados en un segundo proceso de compilación, pudiendo utilizarlos en posteriores procesos de compilación, siempre y cuando, el código fuente no sea alterado de forma significativa.

La optimización PGO se divide en dos modos: instrumentación (la que explico aquí) y muestreo, que requiere del uso de aplicaciones externas como Perf y AutoFDO, realizando todo el proceso de forma manual por parte del usuario: ejecución del binario compilado con perf para obtener las muestras y su posterior conversión con AutoFDO a un formato legible por Clang.

A diferencia de las otras optimizaciones explicadas en este manual, ésta requiere de un proceso más elaborado para su aplicación, que explico a continuación:

1) Creamos un directorio de ubicación de los perfiles de optimización guiada con permisos de escritura para todos los usuarios.

$ su
# mkdir -p /var/pgo
# chmod 777 /var/pgo

2) Establecemos la variable de entorno de generación del perfil de optimización con la ruta del directorio de perfiles de optimización que hemos creado, más el nombre del paquete que vamos a compilar, por ejemplo, mc. Añadimos un nivel de optimización -O2 y un parámetro de generación de símbolos de depuración, g1 o -gline-tables-only que es lo mismo.

$ export {C,CXX}FLAGS+=' -O2 -g1 -fprofile-generate=/var/pgo/mc

3) Ejecutamos el script de configuración pertinente y compilamos el paquete. En la ruta indicada se habrán creado una serie de archivos de datos con el nombre default_*.profraw. Instalamos el paquete y lo ejecutamos. Si el paquete de código fuente contiene tests, lo mejor para recopilar información es ejecutar en el mismo make check después de haber compilado el mismo (nos ahorramos tener que instalarlo). En unos paquetes esto resulta efectivo (por ejemplo, ImageMagick o POV-Ray), en otros, al ser los tests muy básicos, lo mejor es ejecutar la aplicación.

4) Convertimos el formato en crudo de los archivos default_*.profraw a un formato binario legible por Clang, con el comando llvm-profdata:

$ llvm-profdata merge /var/pgo/mc/default_*.profraw \
--output=/var/pgo/mc/default.profdata

Con la variable de entorno LLVM_PROFILE_FILE, podemos cambiar la ruta predefinida de ubicación y el nombre del perfil de optimización. Un ejemplo de ejecución de un programa, en el que se sustituye el nombre predefinido, por la ID del proceso de ejecución. Esto nos permitirá crear varios archivos de perfil por ejecución del programa, que podremos combinar con el comando llvm-profdata, para su posterior uso en el segundo proceso de compilación del paquete:

$ LLVM_PROFILE_FILE=/var/pgo/mc/mc-%p.profraw mc

$ llvm-profdata merge /var/pgo/mc/mc-*.profraw \
--output=/var/pgo/mc/default.profdata

5) Volvemos a recompilar el paquete. Tenemos dos opciones, una es empezar de cero con las variables de entorno y añadir la siguiente, volviendo a ejecutar el script de configuración, después de haber limpiado el directorio de compilación con el comando make distclean o make clean según esté configurado:

$ export {C,CXX}FLAGS+=' -fprofile-use=/var/pgo/mc

La otra más rápida consiste en modificar los archivos Makefile de forma masiva, modificando el parámetro aplicado, con el siguiente comando:

$ find . -name 'Makefile' | xargs sed -i 's:fprofile-generate:fprofile-use:g'

Una vez los hayamos modificado, ejecutamos make clean; make y volvemos a instalar el paquete. El perfil de optimización creado lo podemos cargar de forma directa en la siguiente compilación, siempre y cuando el código fuente no haya sido alterado de forma significativa. Ya se encargará el compilador de avisarnos de esto. Si tenemos desactivados los avisos con el parámetro -w, no nos avisará en absoluto.

Tener en cuenta que si creamos el perfil con el parámetro -O2 -g1, sin ningún tipo de  optimización adicional, obtendremos más información que nos servirá para aplicar una optimización PGO más efectiva. Lo cual no quiere decir que no se pueda realizar el proceso con las mismas optimizaciones en el comienzo y en el final del mismo.

$ export {C,CXX}FLAGS+=' -fprofile-use=/var/pgo/mc

Para acelerar todo esto, siempre es bueno crearse unas funciones de Bash. Abrimos con un editor de texto, el archivo ~/.bashrc, si no existe lo creamos y añadimos lo siguiente al final del mismo:

optclang-gpgo () { dir="$1"; export {C,CXX}FLAGS+=" -O2 -g1 -fprofile-generate=/var/pgo/$dir"; }

optclang-upgo () { dir="$1"; export {C,CXX}FLAGS+=" -fprofile-use=/var/pgo/$dir"; }

optclang-profdata () { llvm-profdata merge /var/pgo/$1/default_*.profraw --output=/var/pgo/$1/default.profdata; rm -f /var/pgo/$1/default_*.profraw; }


Guardamos el archivo, abrimos una ventana de terminal, y ahora simplemente basta ejecutar el alias correspondiente para añadir la variable de entorno de esta optimización más el nombre del directorio que definamos para ubicar los datos del perfil.

Primer proceso de compilación:

$ optclang-gpgo mc

Conversión de los datos del perfil (incluye borrado de los archivos generados por el compilador para reducir el espacio en disco de los directorios que incluyen los perfiles de optimización)

$ optclang-profdata mc

Segundo proceso de compilación:

$ optclang-upgo mc

Y, por último, siempre que se modifiquen los parámetros de optimización, se debe de crear un perfil nuevo de optimización porque los resultados pueden variar significativamente en función de las optimizaciones aplicadas en el proceso de compilación.
CSPGO
-fprofile-use=ruta a directorio o archivo -fcs-profile-generate=ruta a directorio o archivo

-fprofile-use=ruta a archivo
Introducida a partir de Clang 9, CSPGO es una optimización PGO de dos pases, primero se realiza una PGO normal y, a partir del archivo de perfil creado se vuelve a recompilar el paquete tomando como referencia dicho archivo, para finalmente, volver a recompilar el paquete con el archivo de perfil PGO resultante de la fusión de los archivos de perfil en crudo generados por el ejecutable (default_*.profraw) y el perfil PGO inicial (default.profdata). Un ejemplo:

1) Primer proceso de compilación tomando como referencia el archivo de perfil PGO creado en la sección anterior:

$ export {C,CXX}FLAGS+=' -O2 -g1 -fprofile-use=/var/pgo/mc -fcs-profile-generate=/var/pgo/mc

2) Ejecución y testeo del programa.

3) Convertimos el formato en crudo de los archivos default_*.profraw a un formato binario legible por Clang, con el comando llvm-profdata, mezclándolos con el perfil PGO creado inicialmente, creando un archivo de perfil de salida con el nombre de la optimización:

$ llvm-profdata merge /var/pgo/mc/default_*.profraw \
/var/pgo/mc/default.profdata --output=/var/pgo/mc/cspgo.profdata

4) Finalmente, utilizamos el perfil resultante de la fusión, en el proceso de compilación:

$ export {C,CXX}FLAGS+=' -fprofile-use=/var/pgo/mc/cspgo.profdata

Para acelerar todo esto, siempre es bueno crearse unas funciones de Bash. Abrimos con un editor de texto, el archivo ~/.bashrc, si no existe lo creamos y añadimos lo siguiente al final del mismo:

optclang-csgpgo () { dir="$1"; export {C,CXX}FLAGS+=" -O2 -g1 -fprofile-use=/var/pgo/$dir -fcs-profile-generate=/var/pgo/$dir"; }

optclang-csupgo () { dir="$1"; export {C,CXX}FLAGS+=" -fprofile-use=/var/pgo/$dir/cspgo.profdata"; }

optclang-csprofdata () { llvm-profdata merge /var/pgo/$1/default.profdata
/var/pgo/$1/default_*.profraw --output=/var/pgo/$1/cspgo.profdata; rm -f /var/pgo/$1/default_*.profraw; }

Guardamos el archivo, abrimos una ventana de terminal, y ahora simplemente basta ejecutar el alias correspondiente para añadir la variable de entorno de esta optimización más el nombre del directorio que definamos para ubicar los datos del perfil.

Primer proceso de compilación:

$ optclang-csgpgo mc

Conversión de los datos del perfil (incluye borrado de los archivos generados por el compilador para reducir el espacio en disco de los directorios que incluyen los perfiles de optimización) y mezcla con el archivo de perfil PGO. Como opción también le podemos añadir que borre el archivo de perfil PGO (default.profdata), para dejar solo el archivo de perfil CSPGO (cspgo.profdata).

$ optclang-csprofdata mc

Segundo proceso de compilación:

$ optclang-csupgo mc

Por último, si se producieran mensajes de error en la ejecución y testeo del programa, del tipo error: undefined reference to '__llvm_profile_runtime', añadimos la siguiente variable de entorno LDFLAGS en el primer proceso de compilación:

$ export LDFLAGS+=" $(clang -print-resource-dir)/lib/linux/libclang_rt.profile-x86_64.a"

Esto es válido también para la optimización PGO.


Para añadir estas optimizaciones adicionales a las variables de entorno de optimización de CPU que hayamos establecido, basta ejecutar el comando siguiente, incluyendo el nivel de optimización nuevamente, para asegurarnos de que se utilice el mismo en el proceso de compilación:

Polly
$ export {C,CXX}FLAGS+=' -O3 -mllvm -polly'

Polly (generación automática de código vectorizado)
$ export {C,CXX}FLAGS+=' -O3 -mllvm -polly -mllvm -polly-vectorizer=stripmine'

Polly (sólo en aquellos paquetes en los que se active por defecto el parámetro -fopenmp)
$ export {C,CXX}FLAGS+=' -O3 -mllvm -polly -mllvm -polly-vectorizer=stripmine -mllvm -polly-parallel'

LTO
$ export {C,CXX}FLAGS+=' -flto'

ThinLTO
$ export {C,CXX}FLAGS+=' -flto=thin'

En algunos procesos hay que añadir también el parámetro a la variable de entorno LDFLAGS.

$ export {C,CXX,LD}FLAGS+=' -flto=thin'

En procesos que se generen librerías estáticas, o intervenga el comando nm de Binutils, la variable de entorno sería la siguiente, acompañada de la modificación que sea necesario realizar en paquetes que no acepten estas variables de entorno.

$ export AR=llvm-ar RANLIB=llvm-ranlib NM=llvm-nm
$ export {C,CXX}FLAGS+=' -flto'

Tener en cuenta que el nivel de optimización aplicado en un proceso de compilación es siempre el último que aparece en la salida del compilador. Si el script de configuración del paquete, incluye su propio nivel de optimización, es posible, y en esta web hay ejemplos, de que éste se muestre detrás del nivel de optimización que hayamos establecido mediante la correspondiente variable de entorno en la terminal. Para evitar esto, tendríamos que recurrir a una edición masiva de los archivos 'Makefile', con el siguiente comando:

$ find . -name 'Makefile' | xargs sed -i 's:-O2::'

Para ver las variables de entorno que hemos aplicado antes de ejecutar el script de configuración, ejecutamos el siguiente comando:

$ echo ${CFLAGS,CXXFLAGS}

Y si nos hemos equivocado en algo, pues las borramos y volvemos a empezar. O las sobreescribimos volviendo a aplicar la primera variable de entorno que hemos establecido, la que no lleva el símbolo '+'.

$ unset {C,CXX}FLAGS

Clang suele mostrar más mensajes de aviso que GCC, Para eliminarlos todos (algunos producen errores de compilación), basta establecer la siguiente variable de entorno, antes de ejecutar el proceso de configuración del paquete correspondiente.

$ export {C,CXX}FLAGS+=' -w'

Crear alias de bash para facilitar las tareas de compilación de programas con Clang

Lo mejor que podemos hacer para no tener que estar copiando y pegando variables de entorno relacionadas con Clang, es automatizar un poco el proceso, creando los correspondientes alias de bash. A continuación pongo los míos, que cada usuarios los adapte a sus necesidades. Abrimos con un editor de texto, el archivo de configuración personal, ~/.bashrc, si no existe lo creamos, y añadimos lo siguiente al final del contenido del mismo:

alias optclang="export CC=clang CXX=clang++ {C,CXX}FLAGS='-O3 -march=znver3 -mtune=znver3 -mllvm -polly -mllvm -polly-vectorizer=stripmine'"

alias optclang-cpu="export CC=clang CXX=clang++ {C,CXX}FLAGS='-O3 -march=znver3 -mtune=znver3'"


alias optclang-lto="export {C,CXX}FLAGS+=' -flto'"

alias optclang--thinlto="export {C,CXX}FLAGS+=' -flto=thin'"


alias optclang-ar="export AR=llvm-ar RANLIB=llvm-ranlib NM=llvm-nm"

El principal es optclang, que establece el uso de compilador, optimización de CPU y optimización con Polly, en un sólo comando. Si nos falla la compilación con Polly, sobreescribimos este alias con optclang-cpu, que omite la vectorización de código con Polly. Luego como añadido utilizamos el alias optclang-lto para utilizar la optimización LTO o la optimización ThinLTO con el alias optclang--thinlto, que combinamos con el alias optclang-ar en procesos en los que se compilan librerías estáticas. Tener en cuenta que todo esto debe de adaptarse al proceso de compilación en cuestión. Muchas veces tenemos que añadir el parámetro -flto a la propia variable de entorno de uso de compilador (export {CC,CXX}+=" -flto") para que esta optimización se pueda llevar a cabo con algunos paquetes.

Con el nuevo soporte de OpenMP añadimos un nuevos alias, aunque su uso queda muy reducido por la escases de paquetes que soportan OpenMP.

alias optclang-omp="export {C,CXX}FLAGS+=' -fopenmp=libomp' LDFLAGS+=' -lomp'"




Foro Galería Blog


Página - 1Página - 2

Actualizado el 20-03-2024

Instalar Clang desde cero

Instalar Binutils desde ceroInstalar CMake desde cero