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-2021   http://manualinux.eu



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
Compilar programas con Clang
Comparativa de resultados de optimización entre Clang y GCC
El estándar de lenguaje de programación predefinido de C++, a partir de Clang 6
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 - (11.1.0) o Clang - (12.0.0)
* CMake - (3.20.5)
* Ninja - (1.10.2)
* Pkg-config - (0.29.2)

Librerías de Desarrollo

* Binutils - (2.36.1)
* Elfutils - (0.185)
* Libffi - (3.3)
* Libxml2 - (2.9.12)
* Ncurses - (6.2)
* Zlib - (1.2.11)

Intérpretes de Lenguaje de Programación

* Perl - (5.34.0)
* Python - (3.9.6)
   Pygments - (2.9.0)
   PyYAML - (5.4.1.1)

Aplicaciones

* Sphinx - (4.0.3)



Descarga  Información sobre los paquetes

llvm-12.0.1.tar.xz

Firma Digital  Clave pública PGP

llvm-12.0.1.tar.xz.asc

Verificar la firma digital del paquete

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

Optimizaciones

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

Donde pone znver2 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
New Pass Manager
$ export {C,CXX}FLAGS+=' -fexperimental-new-pass-manager'
Este parámetro no es combinable con Polly en la compilación de la versión de 64 bits de LLVM, con Clang 11 y superiores.
Polly
$ export {C,CXX}FLAGS+=' -O3 -mllvm -polly -mllvm -polly-vectorizer=stripmine'

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

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/gcc11/lib'

Establecer el RPATH correspondiente si utilizamos una versión de GCC que no es la principal del sistema
$ export LDFLAGS+=" -Wl,-rpath,/opt/gcc11/lib -lstdc++"
Sustituir /opt/gcc11/lib por la ruta de instalación alternativa que cada usuario tenga en su sistema.

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 el RPATH correspondiente si utilizamos una versión de GCC que no es la principal del sistema
$ export LDFLAGS+=" -Wl,-rpath,/opt/gcc11/lib64 -lstdc++"
Sustituir /opt/gcc11/lib64 por la ruta de instalación alternativa que cada usuario tenga en su sistema.

Parámetros adicionales globales

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

Parámetros adicionales complementarios de la optimización LTO/ThinLTO 
GCC
$ export AR=gcc-ar RANLIB=gcc-ranlib NM=gcc-nm
Clang
$ export AR=llvm-ar RANLIB=llvm-ranlib NM=llvm-nm

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=$(getconf _NPROCESSORS_ONLN)"
Optimizaciones complementarias ThinLTO de LLD
$ export LDFLAGS+=" -Wl,--thinlto-jobs=$(getconf _NPROCESSORS_ONLN)"

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

$ tar Jxvf llvm-12.0.1.tar.xz
$ cd llvm-12.0.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. 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. En este modo de compilación sólo instalaremos las librerías de 32 bits de LLVM, omitiendo el resto de componentes del paquete, que anularemos con el correspondiente comando de cambio de permisos de directorio. Es necesario y recomendable realizar cada compilación en diferentes ventanas de terminal, para no mezclar variables de entorno, y en diferentes directorios de compilación.

$ for i in compiler-rt openmp clang lld polly libcxx{,abi} libunwind ; do \
chmod 000 {projects,tools}/$i &> /dev/null ; \
done
$ mkdir build32; cd build32
$ cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/llvm12 \
-DLLVM_TARGETS_TO_BUILD=X86 -DBUILD_SHARED_LIBS=ON -DLLVM_ENABLE_RTTI=ON \
-DLLVM_ENABLE_FFI=ON -DOCAMLFIND=null -DLLVM_CCACHE_BUILD=ON -G Ninja ..

Explicación de los comandos

for i in compiler-rt openmp clang lld polly libcxx{,abi} libunwind ; do \
chmod 000 {projects,tools}/$i &> /dev/null ; \
done : Cambia los permisos de los directorios de los componentes adicionales de LLVM para que el proceso de configuración no pueda acceder a los mismos, y se omita su compilación en el modo de 32 bits. 

mkdir build32; cd build32 : Crea un directorio de compilación para no compilar directamente en el directorio de las fuentes, y entramos en el mismo.

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

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

-DLLVM_TARGETS_TO_BUILD=X86 : Compila sólo el soporte de la arquitectura de procesador x86, 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.

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

Parámetros de configuración opcionales  

-DCMAKE_AR=$(which $AR) -DCMAKE_RANLIB=$(which $RANLIB) -DCMAKE_NM=$(which $NM) : Sincroniza las variables de entorno establecidas en el manual, relativas a los binarios ejecutables intermedios, gcc-ar, gcc-ranlib y gcc-nm, con los parámetros de configuración utilizados por CMake, relativos a los programas ar, ranlib y nm, para poder aplicar correctamente la optimización LTO. Se utiliza el comando which para buscar los ejecutables porque desde hace ya varias versiones, CMake no establece correctamente la ruta al ejecutable en cuestión.

-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.

-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=$(getconf _NPROCESSORS_ONLN) : 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. Usuarios con 4 GB de memoria RAM, o menos, abstenerse de utilizar todos los núcleos que tenga su procesador, si este tiene 4 o más núcleos. Como ejemplo personal (mi CPU anterior, Phenom II x4 965): 4 instancias de LLD ejecutándose con 4 tareas cada una (-Wl,--thinlto-jobs=4'), llega a consumir máximos de 4 GB de memoria RAM.

Compilación

$ ninja

Parámetros de compilación opcionales  

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

-j$(getconf _NPROCESSORS_ONLN): 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 install/strip
# mv /opt/llvm12/bin/llvm-config{,-32}
# exit
$ cd ..

Explicación de los comandos

mv /opt/llvm12/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.

Configuración de la versión de 64 bits

$ for i in compiler-rt openmp clang lld polly libcxx{,abi} libunwind ; do \
chmod 755 {projects,tools}/$i &> /dev/null ; \
done
$ mkdir build64; cd build64
$ cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/llvm12 \
-DLLVM_TARGETS_TO_BUILD="X86;BPF" -DGCC_INSTALL_PREFIX=/opt/gcc11 \
-DLLVM_BINUTILS_INCDIR=/usr/include -DLLVM_BUILD_DOCS=ON \
-DLLVM_ENABLE_SPHINX=ON -DSPHINX_WARNINGS_AS_ERRORS=OFF \
-DLLVM_BUILD_LLVM_DYLIB=ON -DOCAMLFIND=null -DLLVM_LIBDIR_SUFFIX=64 \
-DLLVM_ENABLE_RTTI=ON -DLLVM_ENABLE_FFI=ON -DLLVM_CCACHE_BUILD=ON \
-DLLVM_INSTALL_UTILS=ON -G Ninja ..

Explicación de los comandos

for i in compiler-rt openmp clang lld polly libcxx{,abi} libunwind ; do \
chmod 755 {projects,tools}/$i &> /dev/null ; \
done: Restablecemos los permisos de los directorios de los componentes adicionales de LLVM a su estado inicial, para que el proceso de configuración del paquete los pueda incluir en el mismo.

mkdir build64; cd build64 : Creamos un directorio de compilación para no compilar directamente en el directorio de las fuentes, y entramos en el mismo.

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

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

-DGCC_INSTALL_PREFIX=/opt/gcc11 : Le indicamos la ruta de instalación de GCC, en el caso de que utilicemos otra versión diferente a la principal del sistema. Esto es necesario para que Clang encuentre las cabeceras C++ de GCC cuando tenga que compilar código escrito en C++.

-DLLVM_TARGETS_TO_BUILD="X86;BPF" : Compila sólo el soporte de la arquitectura de procesador x86, omitiendo otras como ARM, PowerPC, etc. Esto reduce el tiempo de compilación y el tamaño del directorio de instalación del compilador. El soporte de BPF es requerido para la compilación del paquete v4l-utils.

-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.

-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.

-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 sobreescritas en el proceso de instalación, las librerías de la versión de 32 bits, ubicadas en /opt/llvm12/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.

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

Parámetros de configuración opcionales  

-DLLVM_TOOL_LIBCXX{,ABI}_BUILD=OFF : Si no queremos compilar e instalar Libc++ y Libc++abi, añadimos esta opción al comando de configuración del paquete.

-DBUILD_SHARED_LIBS=ON : Compila las librerías compartidas en lugar de las estáticas. Esto, junto a la eliminación de los símbolos innecesarios con el comando ninja install/strip, reduce la ocupación del espacio en disco del paquete en más de un 70 %. Como contrapartida, la pérdida de rendimiento del compilador ronda el 15/20 % según el paquete a compilar (no se ha probado con paquetes de gran tamaño), de ahí que esta opción se haya convertido en opcional en este manual. Sirva de ejemplo, la tabla que pongo a continuación, tomando como referencia la compilación de Pekwm, con las opciones O3+CPU+Polly.

Tabla de diferencias de tamaño entre una instalación estática, una mixta y una compartida de LLVM 10, más el tiempo de compilación que tarda Clang 10, según el modo de instalación del compilador
Modo de instalación Ocupación de espacio en disco Parámetros aplicados Tiempo de compilación
Estática 1,3 GB O3+CPU+Polly 18"
Mixta [1] 731,4 MB O3+CPU+Polly 19"
Compartida 340,9 MB
O3+CPU+Polly 23"
[1] Este es el modo que utiliza el BLFS, enlazando las herramientas (incluido Clang) contra la librería compartida de LLVM, utilizando el parámetro -DLLVM_LINK_LLVM_DYLIB=ON. Al que le tendremos que añadir el parámetro -DCLANG_LINK_CLANG_DYLIB=OFF, para que libclang-cpp se integre de forma estática en el binario ejecutable clang-10, porque de lo contrario, el rendimiento de Clang es el mismo que en una instalación compartida. Aún así, como se puede ver en la tabla, sigue siendo ligeramente más lento que la instalación predefinida (Un segundo, en informática y en Fórmula 1, es un mundo), porque clang-10 queda enlazado contra la librería compartida de LLVM, en lugar de hacerlo de forma estática como en la instalación predefinida.

Si se utiliza esta opción no hay que utilizar -DLLVM_BUILD_LLVM_DYLIB.

-DCMAKE_AR=$(which $AR) -DCMAKE_RANLIB=$(which $RANLIB) -DCMAKE_NM=$(which $NM) : Sincroniza las variables de entorno establecidas en el manual, relativas a los binarios ejecutables intermedios, gcc-ar, gcc-ranlib y gcc-nm, con los parámetros de configuración utilizados por CMake, relativos a los programas ar, ranlib y nm, para poder aplicar correctamente la optimización LTO. Se utiliza el comando which para buscar los ejecutables porque desde hace ya varias versiones, CMake no establece correctamente la ruta al ejecutable en cuestión.

-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, lo que hace que el total de espacio requerido aumente en más de 140 MB, 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.

-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=$(getconf _NPROCESSORS_ONLN) : 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. Usuarios con 4 GB de memoria RAM, o menos, abstenerse de utilizar todos los núcleos que tenga su procesador, si este tiene 4 o más núcleos. Como ejemplo personal (mi CPU anterior, Phenom II x4 965): 4 instancias de LLD ejecutándose con 4 tareas cada una (-Wl,--thinlto-jobs=4'), llega a consumir máximos de 4 GB de memoria RAM.

Compilación

$ ninja

Parámetros de compilación opcionales

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

$ su
# ninja install/strip
# mv /opt/llvm12/bin/llvm-config{,-64}

Explicación de los comandos

mv /opt/llvm12/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.

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/llvm12/bin/multiarch_wrapper
# ln -s multiarch_wrapper /opt/llvm12/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

Borrar los directorios y archivos de instalación que hacen referencia a la versión 12.0.0, sobreescrita 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 los siguientes comandos, borramos los directorios y archivos de instalación de la versión anterior.

# rm -rf /opt/llvm12/lib64/clang/12.0.0
# rm -f /opt/llvm12/lib64/libLLVM-12.0.0.so

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

Estadísticas de Compilación e Instalación de LLVM
CPU AMD Ryzen 3 3100 4-Core Processor
MHz 3593.246
RAM 16 GB
Sistema de archivos XFS
Versión del Kernel 5.12.15-ck1 SMP PREEMPT x86_64
Modo de frecuencia de la CPU performance
Versión de Glibc 2.33
Enlazador dinámico LLD 12.0.0
Compilador Clang 12.0.0 + Ccache 4.3
Parámetros de optimización -03 -march=znver2 -mtune=znver2 -mllvm -polly -mllvm -polly-vectorizer=stripmine -flto=thin
Parámetros de compilación -v -j8
Ocupación de espacio en disco del proceso de compilación 4,1 GB
Tiempo de compilación 58' 24"
Archivos instalados 5.262
Mostrar/Ocultar la lista de archivos instalados
Enlaces simbólicos creados 107
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-12.0.1-scripts.tar.gz

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

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-12.0.1-scripts.tar.gz
# cd llvm-12.0.1-scripts
# ./Respaldar_llvm-12.0.1

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-12.0.1



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/llvm12/lib64
/opt/llvm12/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/llvm12/bin:$PATH
export MANPATH=/opt/llvm12/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/llvm12/bin:$PATH
export MANPATH=/opt/llvm12/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 12.0.1
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /opt/llvm12/bin

2c) Lectura de las páginas de manual

$ man clang

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

/opt/llvm12/share/doc/llvm/html/index.html  |  /opt/llvm12/share/doc/clang/html/index.html



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'.

Optimizaciones con GCC y Clang 3.4 y superiores

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

Optimizaciones con versiones inferiores a Clang 3.4

$ export {C,CXX}FLAGS='-O3 -march=znver2 -mcpu=znver2'

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.

Donde pone znver2 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
New Pass Manager 
-fexperimental-new-pass-manager

-fno-legacy-pass-manager
(a partir de Clang 12)
Activa el uso del nuevo gestor de pases de análisis y optimización de LLVM. Aunque lleva el nombre de experimental, es una optimización de nivel superior bastante estable y puede ser aplicada también con las optimizaciones LTO y ThinLTO, cuando éstas son procesadas por el enlazador dinámico, ya sea GNU gold o LLD.

Si utilizamos esta opción con LLD y LTO o ThinLTO y añadimos también los pases de análisis y optimización de LLVM podemos obtener unos resultados muy óptimos en función del pase de optimización que utilicemos.

Esta optimización se puede establecer como predefinida de Clang, estableciendo la variable de CMake, -DENABLE_EXPERIMENTAL_NEW_PASS_MANAGER=ON en el proceso de configuración de LLVM. Variable que se incluyó por defecto en este manual con la versión 10 de Clang, y que se ha retirado a partir de la versión 11 de Clang, porque desde la versión 10 de Clang, la combinación New Pass Manager+Polly da errores de compilación en varios paquetes, lo que obliga a tener que añadir en cada manual de los ubicados en esta web, donde se produce el error, el parámetro -fno-experimental-new-pass-manager, que desactiva el uso de New Pass Manager, si lo hemos activado por defecto en el proceso de compilación de Clang. En otros manuales, directamente hay que desactivar el uso de Polly, cuyo desarrollo está muy estancado en las dos últimas versiones de LLVM.

Desde que se modificó la integración de Polly en Clang, viene sucediendo esto, algo que, probablemente, se solucione en la versión 13 de Clang, esperemos.

A partir de Clang 12, esta opción se considera lo suficientemente estable para no llamarla experimental, con lo que el nombre del parámetro de optimización ha cambiado a -fno-legacy-pass-manager, pasando a ser -fexperimental-new-pass-manager un alias del primero y fno-experimental-new-pass-manager un alias de -flegacy-pass-manager. Cuando se sustituya de forma definitiva el gestor de pases antiguo por el nuevo, los alias serán eliminados de Clang y también de este manual y de los respectivos manuales de la web. Más información en este enlace.
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 puede seguir utilizando pero se recomienda utilizar el segundo.
-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.
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

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.

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:

New Pass Manager
$ export {C,CXX}FLAGS+=' -fexperimental-new-pass-manager'

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=znver2 -mtune=znver2' -fexperimental-new-pass-manager -mllvm -polly -mllvm -polly-vectorizer=stripmine'"

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


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 09-07-2021

Instalar Clang desde cero

Instalar Binutils desde ceroInstalar CMake desde cero