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





Instalar Clang desde cero




Copyright

Copyright © José Luis Lara Carrascal  2012-2017   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
Compilar programas con Clang
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.



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 - (7.2.0)
* CMake - (3.9.1)
* Make - (4.2.1)
* Pkg-config - (0.29.2)

Librerías de Desarrollo

* Binutils - (2.29)
* Libxml2 - (2.9.4)
* Ncurses - (6.0)
* Zlib - (1.2.11)

Intérpretes de Lenguaje de Programación

* OCaml - (4.04.2) - opcional [1]
   Findlib - (1.7.3)
   Ctypes - (0.12.1)
* Perl - (5.26.0)
* Python - (2.7.13)

Aplicaciones

* Sphinx - (1.6.3)

[1] A partir de la versión 4.0.0, el paquete de LLVM para OCaml se instala en el directorio de instalación predefinido de los paquetes de este lenguaje de programación, /usr/lib/ocaml o /usr/local/lib/ocaml, según donde hayamos instalado OCaml. La ruta se puede cambiar en la configuración de instalación de LLVM, pero se deduce que sí se utiliza ese directorio en concreto a partir de ahora, es por cuestiones de funcionalidad en el uso de LLVM con OCaml.



Descarga  Información sobre los paquetes

llvm-5.0.0.tar.xz

Firma Digital  Clave pública PGP

llvm-5.0.0.tar.xz.asc

Verificar la firma digital del paquete

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

Optimizaciones

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

Donde pone amdfam10 se indica el procesador respectivo de cada sistema seleccionándolo de la siguiente tabla:
* 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='.
Valores CPU
Genéricos
generic Produce un código binario optimizado para la mayor parte de procesadores existentes. Utilizar este valor si no sabemos el nombre del procesador que tenemos en nuestro equipo. Este valor sólo es aplicable en la opción '-mtune=', si utilizamos GCC. Esta opción está disponible a partir de GCC 4.2.
native Produce un código binario optimizado para el procesador que tengamos en nuestro sistema, siendo éste detectado utilizando la instrucción cpuid. Procesadores antiguos pueden no ser detectados utilizando este valor. Esta opción está disponible a partir de GCC 4.2.
Intel
atom Intel Atom con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3 y extensiones 64-bit. Esta opción está disponible desde GCC 4.6, hasta GCC 4.8. A partir de GCC 4.9 se utiliza la definición bonnell.
bonnell Intel Bonnell con soporte de instrucciones MOVBE, MMX, SSE, SSE2, SSE3, SSSE3 y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.9.
broadwell Intel Broadwell con soporte de instrucciones MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AVX2, AES, PCLMUL, FSGSBASE, RDRND, FMA, BMI, BMI2, F16C, RDSEED, ADCX, PREFETCHW y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.9.
core2 Intel Core2 con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3 y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.3.
core-avx2 Intel Core (Haswell). Esta opción está disponible desde GCC 4.6, hasta GCC 4.8. A partir de GCC 4.9 se utiliza la definición haswell.
core-avx-i Intel Core (ivyBridge) con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AES, PCLMUL, FSGSBASE, RDRND, F16C y extensiones 64-bit. Esta opción está disponible desde GCC 4.6, hasta GCC 4.8. A partir de GCC 4.9 se utiliza la definición ivybridge.
corei7 Intel Core i7 con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1 y SSE4.2 y extensiones 64-bit. Soporta también los procesadores Intel Core i3 e i5. Esta opción está disponible desde GCC 4.6, hasta GCC 4.8. A partir de GCC 4.9 se utiliza la definición nehalem.
corei7-avx Intel Core i7 con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AES y PCLMUL y extensiones 64-bit. Soporta también los procesadores Intel Core i3 e i5. Esta opción está disponible desde GCC 4.6, hasta GCC 4.8. A partir de GCC 4.9 se utiliza la definición sandybridge.
haswell Intel Haswell con soporte de instrucciones MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AVX2, AES, PCLMUL, FSGSBASE, RDRND, FMA, BMI, BMI2, F16C y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.9.
i386 Intel i386.
i486 Intel i486.
i586, pentium Intel Pentium sin soporte de instrucciones MMX.
i686 Produce un código binario optimizado para la mayor parte de procesadores compatibles con la serie 80686 de Intel. Todos los actuales lo son.
intel Intel Haswell y Silvermont. Este valor sólo es aplicable en la opción '-mtune='. Esta opción está disponible a partir de GCC 4.9.
ivybridge Intel Ivy Bridge con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AES, PCLMUL, FSGSBASE, RDRND, F16C y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.9.
knl Intel Knights Landing con soporte de instrucciones MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AVX2, AES, PCLMUL, FSGSBASE, RDRND, FMA, BMI, BMI2, F16C, RDSEED, ADCX, PREFETCHW, AVX512F, AVX512PF, AVX512ER y extensiones 64-bit. Esta opción está disponible a partir de GCC 5.
lakemont Intel Quark Lakemont MCU, basado en el procesador Intel Pentium. Esta opción está disponible a partir de GCC 6.
nehalem Intel Nehalem con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.9.
nocona Versión mejorada de Intel Pentium4 con soporte de instrucciones MMX, SSE, SSE2, SSE3 y extensiones 64-bit.
pentiumpro Intel PentiumPro.
pentium2 Intel Pentium2 basado en PentiumPro con soporte de instrucciones MMX.
pentium3, pentium3m Intel Pentium3 basado en PentiumPro con soporte de instrucciones MMX y SSE.
pentium4, pentium4m Intel Pentium4 con soporte de instrucciones MMX, SSE y SSE2.
pentium-m Versión de bajo consumo de Intel Pentium3 con soporte de instrucciones MMX, SSE y SSE2. Utilizado por los portátiles Centrino.
pentium-mmx Intel PentiumMMX basado en Pentium con soporte de instrucciones MMX.
prescott Versión mejorada de Intel Pentium4 con soporte de instrucciones MMX, SSE, SSE2 y SSE3.
sandybridge Intel Sandy Bridge con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AES, PCLMUL y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.9.
silvermont Intel Silvermont con soporte de instrucciones MOVBE, MMX, SSE, MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AES, PCLMU, RDRND y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.9.
skylake Intel Skylake con soporte de instrucciones MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AVX2, AES, PCLMUL, FSGSBASE, RDRND, FMA, BMI, BMI2, F16C, RDSEED, ADCX, PREFETCHW, CLFLUSHOPT, XSAVEC, XSAVES y extensiones 64-bit. Esta opción está disponible a partir de GCC 6.
skylake-avx512 Intel Skylake Server con soporte de instrucciones MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AVX2, AES, PCLMUL, FSGSBASE, RDRND, FMA, BMI, BMI2, F16C, RDSEED, ADCX, PREFETCHW, CLFLUSHOPT, XSAVEC, XSAVES, AVX512F, AVX512VL, AVX512BW, AVX512DQ, AVX512CD y extensiones 64-bit. Esta opción está disponible a partir de GCC 6.
westmere Intel Westmere con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AES, PCLMUL y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.9.
AMD
amdfam10, barcelona Procesadores basados en AMD Family 10h core con soporte de instrucciones x86-64 (MMX, SSE, SSE2, SSE3, SSE4A, 3DNow!, enhanced 3DNow!, ABM y extensiones 64-bit). Esta opción está disponible a partir de GCC 4.3.
athlon, athlon-tbird AMD Athlon con soporte de instrucciones MMX, 3DNow!, enhanced 3DNow! y SSE prefetch.
athlon4, athlon-xp, athlon-mp Versiones mejoradas de AMD Athlon con soporte de instrucciones MMX, 3DNow!, enhanced 3DNow! y full SSE.
bdver1 Procesadores basados en AMD Family 15h core con soporte de instrucciones x86-64 (FMA4, AVX, XOP, LWP, AES, PCL_MUL, CX16, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM y extensiones 64-bit). Esta opción está disponible a partir de GCC 4.7.
bdver2 Procesadores basados en AMD Family 15h core con soporte de instrucciones x86-64 (BMI, TBM, F16C, FMA, LWP, AVX, XOP, LWP, AES, PCL_MUL, CX16, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM y extensiones 64-bit). Esta opción está disponible a partir de GCC 4.7.
bdver3 Procesadores basados en AMD Family 15h core con soporte de instrucciones x86-64 (FMA4, AVX, XOP, LWP, AES, PCL_MUL, CX16, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM y extensiones 64-bit). Esta opción está disponible a partir de GCC 4.8.
bdver4 Procesadores basados en AMD Family 15h core con soporte de instrucciones x86-64 (BMI, BMI2, TBM, F16C, FMA, FMA4, FSGSBASE, AVX, AVX2, XOP, LWP, AES, PCL_MUL, CX16, MOVBE, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM y extensiones 64-bit). Esta opción está disponible a partir de GCC 4.9.
btver1 Procesadores basados en AMD Family 14h core con soporte de instrucciones x86-64 (MMX, SSE, SSE2, SSE3, SSE4A, CX16, ABM y extensiones 64-bit). Esta opción está disponible a partir de GCC 4.6.
btver2 Procesadores basados en AMD Family 16h core con soporte de instrucciones x86-64 (MOVBE, F16C, BMI, AVX, PCL_MUL, AES, SSE4.2, SSE4.1, CX16, ABM, SSE4A, SSSE3, SSE3, SSE2, SSE, MMX y extensiones 64-bit). Esta opción está disponible a partir de GCC 4.8.
geode AMD integrado con soporte de instrucciones MMX y 3DNow!. Esta opción está disponible a partir de GCC 4.3.
k6 AMD K6 con soporte de instrucciones MMX.
k6-2, k6-3 Versiones mejoradas de AMD K6 con soporte de instrucciones MMX y 3DNow!.
k8, opteron, athlon64, athlon-fx Procesadores basados en AMD K8 core con soporte de instrucciones x86-64 (MMX, SSE, SSE2, 3DNow!, enhanced 3DNow! y extensiones 64-bit).
k8-sse3, opteron-sse3, athlon64-sse3 Versiones mejoradas de AMD K8 core con soporte de instrucciones SSE3. Esta opción está disponible a partir de GCC 4.3.
znver1 Procesadores basados en AMD Family 17h core con soporte de instrucciones x86-64 (BMI, BMI2, F16C, FMA, FSGSBASE, AVX, AVX2, ADCX, RDSEED, MWAITX, SHA, CLZERO, AES, PCL_MUL, CX16, MOVBE, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM, XSAVEC, XSAVES, CLFLUSHOPT, POPCNT y extensiones 64-bit). Esta opción está disponible a partir de GCC 6.
VIA
c3 VIA C3 con soporte de instrucciones MMX y 3DNow! (no se implementa planificación para este chip).
c3-2 VIA C3-2 (Nehemiah/C5XL) con soporte de instrucciones MMX y SSE (no se implementa planificación para este chip).
c7 VIA C7 (Esther) con soporte de instrucciones MMX, SSE, SSE2 y SSE (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
eden-x2 VIA Eden X2 con soporte de instrucciones x86-64, MMX, SSE, SSE2 y SSE3 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
eden-x4 VIA Eden X4 con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX y AVX2 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
esther VIA Eden Esther con soporte de instrucciones MMX, SSE, SSE2 y SSE3 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
nano VIA Nano genérico con soporte de instrucciones x86-64, MMX, SSE, SSE2, SSE3 y SSSE3 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
nano-1000 VIA Nano 1xxx con soporte de instrucciones x86-64, MMX, SSE, SSE2, SSE3 y SSSE3 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
nano-2000 VIA Nano 2xxx con soporte de instrucciones x86-64, MMX, SSE, SSE2, SSE3 y SSSE3 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
nano-3000 VIA Nano 3xxx con soporte de instrucciones x86-64, MMX, SSE, SSE2, SSE3, SSSE3 y SSE4.1 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
nano-x2 VIA Nano Dual Core con soporte de instrucciones x86-64, MMX, SSE, SSE2, SSE3 y SSSE3 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
nano-x4 VIA Nano Quad Core con soporte de instrucciones x86-64, MMX, SSE, SSE2, SSE3 y SSSE3 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
IDT
winchip2 IDT Winchip2, que equivale a un i486 con soporte de instrucciones MMX y 3DNow!.
winchip-c6 IDT Winchip C6, que equivale a un i486 con soporte de instrucciones MMX.

Optimizaciones adicionales

Optimizaciones adicionales
Graphite
$ export {C,CXX}FLAGS+=' -floop-interchange -ftree-loop-distribution -floop-strip-mine -floop-block'

Parámetros adicionales

Establecer el RPATH correspondiente si utilizamos una versión de GCC que no es la principal del sistema
$ export LDFLAGS="-Wl,-rpath,/opt/gcc7/lib -lstdc++"
Sustituir /opt/gcc7/lib por la ruta de instalación de la versión de GCC alternativa que se vaya a utilizar en el proceso de compilación de este paquete.

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

$ tar Jxvf llvm-5.0.0.tar.xz
$ mkdir llvm-5.0.0_build
$ cd llvm-5.0.0_build
$ cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/llvm5 \
-DLLVM_TARGETS_TO_BUILD=X86 -DGCC_INSTALL_PREFIX=/opt/gcc7 \
-DLLVM_BINUTILS_INCDIR=/usr/include -DLLVM_BUILD_DOCS=ON \
-DLLVM_ENABLE_SPHINX=ON -DSPHINX_WARNINGS_AS_ERRORS=OFF \
-DBUILD_SHARED_LIBS=ON -DLLVM_ENABLE_OCAMLDOC=OFF ../llvm-5.0.0

Explicación de los comandos

mkdir llvm-5.0.0_build : Creamos un directorio de compilación, para no compilar directamente en el directorio de las fuentes.

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

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

-DGCC_INSTALL_PREFIX=/opt/gcc7 : 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 : 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.

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

-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 make install/strip, reduce la ocupación del espacio en disco del paquete en un 70 %.

-DLLVM_ENABLE_OCAMLDOC=OFF : Desactiva la creación e instalación de la documentación relativa a Ocaml, sólo de interés para programadores informáticos.

Compilación

$ make

Parámetros de compilación opcionales

VERBOSE=1 : Muestra más información en el proceso de compilación.

-j2 : Si tenemos un procesador de doble núcleo (dual-core), y el kernel está optimizado para el mismo y es SMP, con este parámetro aumentaremos el número de procesos de compilación simultáneos a un nivel de 2 y aceleraremos el tiempo de compilación del programa de forma considerable.
-j4 : Lo mismo que arriba pero con procesadores de 4 núcleos (quad-core).

Instalación como root

$ su
# make install/strip
# rename i386 i686 /opt/llvm5/lib/clang/5.0.0/lib/linux/*

Explicación de los comandos

rename i386 i686 /opt/llvm5/lib/clang/5.0.0/lib/linux/* : Renombramos la arquitectura de procesador del nombre de las librerías ubicadas en el directorio /opt/llvm5/lib/clang/5.0.0/lib/linux al correcto en función de nuestro sistema. Esto evita errores de compilación en paquetes en los que hay que establecer la siguiente variable de entorno, cuya necesidad se explica en la sección dedicada al optimizador Polly:

$ export LDFLAGS+=' -rtlib=compiler-rt -lgcc_s'

El error mostrado en el proceso de configuración del paquete en el que se establece la variable de entorno anterior es el siguiente:

configure:4111: gcc -O3 -march=amdfam10 -mtune=amdfam10 -O3 -mllvm -polly -mllvm -polly-vectorizer=stripmine -Qunused-arguments -flto=thin -Wno-unknown-warning-option   -rtlib=compiler-rt -lgcc_s -Wl,-rpath,/opt/gcc7/lib -lstdc++ conftest.c  >&5
/usr/bin/ld: error: cannot open /opt/llvm5/bin/../lib/clang/5.0.0/lib/linux/libclang_rt.builtins-i686.a: No such file or directory
/usr/bin/ld: error: cannot open /opt/llvm5/bin/../lib/clang/5.0.0/lib/linux/libclang_rt.builtins-i686.a: No such file or directory
clang-5.0: error: linker command failed with exit code 1 (use -v to see invocation)

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

Estadísticas de Compilación e Instalación de LLVM
CPU AMD Athlon(tm) II X2 260 Processor
MHz 3214.610
RAM 4096 MB
Sistema de archivos XFS
Versión de Glibc 2.25
Enlazador dinámico GNU gold (Binutils 2.29) 1.14
Compilador GCC 7.2.0 + Ccache 3.3.4
Parámetros de optimización -03 -march=amdfam10 -mtune=amdfam10 -floop-interchange -ftree-loop-distribution -floop-strip-mine -floop-block
Parámetros de compilación VERBOSE=1 -j2
Ocupación de espacio en disco del proceso de compilación 1,2 GB
Tiempo de compilación 25' 11"
Archivos instalados 2.832
Mostrar/Ocultar la lista de archivos instalados
Enlaces simbólicos creados 194
Mostrar/Ocultar la lista de enlaces simbólicos creados
Ocupación de espacio en disco 190,8 MB

Desinstalación como root

1) MODO TRADICIONAL

Este programa no tiene soporte para desinstalación con el comando 'make 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-5.0.0-scripts.tar.gz

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

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

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



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/llvm5/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/llvm5/bin:$PATH
export MANPATH=/opt/llvm5/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/llvm5/bin:$PATH
export MANPATH=/opt/llvm5/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 5.0.0 (tags/RELEASE_500/final)
Target: i386-pc-linux-gnu
Thread model: posix
InstalledDir: /opt/llvm5/bin

2c) Lectura de las páginas de manual

$ man clang

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

/opt/llvm5/share/doc/llvm/html/index.html  |  /opt/llvm5/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=amdfam10 -mtune=amdfam10'

Optimizaciones con versiones inferiores a Clang 3.4

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

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 amdfam10 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
generic Produce un código binario optimizado para la mayor parte de procesadores existentes. Utilizar este valor si no sabemos el nombre del procesador que tenemos en nuestro equipo. Este valor sólo es aplicable en la opción '-mtune=', si utilizamos GCC. Esta opción está disponible a partir de GCC 4.2.
native Produce un código binario optimizado para el procesador que tengamos en nuestro sistema, siendo éste detectado utilizando la instrucción cpuid. Procesadores antiguos pueden no ser detectados utilizando este valor. Esta opción está disponible a partir de GCC 4.2.
Intel
atom Intel Atom con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3 y extensiones 64-bit. Esta opción está disponible desde GCC 4.6, hasta GCC 4.8. A partir de GCC 4.9 se utiliza la definición bonnell.
bonnell Intel Bonnell con soporte de instrucciones MOVBE, MMX, SSE, SSE2, SSE3, SSSE3 y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.9.
broadwell Intel Broadwell con soporte de instrucciones MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AVX2, AES, PCLMUL, FSGSBASE, RDRND, FMA, BMI, BMI2, F16C, RDSEED, ADCX, PREFETCHW y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.9 y Clang 3.6.
cannonlake Intel Cannonlake con soporte de instrucciones X87, MMX, AVX, FXSR, CMPXCHG16B, POPCNT, AES, PCLMUL, XSAVE, XSAVEOPT, LAHFSAHF, RDRAND, F16C, FSGSBase, AVX2, BMI, BMI2, FMA, LZCNT, MOVBE, INVPCID, VMFUNC, RTM, HLE, SlowIncDec, ADX, RDSEED, SMAP, MPX, XSAVEC, XSAVES, SGX, CLFLUSHOPT, AVX512, CDI, DQI, BWI, VLX, PKU, PCOMMIT, CLWB, VBMI, IFMA y SHA. Esta opción está disponible a partir de Clang 3.9.
core2 Intel Core2 con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3 y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.3.
core-avx2 Intel Core (Haswell). Esta opción está disponible desde GCC 4.6, hasta GCC 4.8. A partir de GCC 4.9 se utiliza la definición haswell.
core-avx-i Intel Core (ivyBridge) con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AES, PCLMUL, FSGSBASE, RDRND, F16C y extensiones 64-bit. Esta opción está disponible desde GCC 4.6, hasta GCC 4.8. A partir de GCC 4.9 se utiliza la definición ivybridge.
corei7 Intel Core i7 con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1 y SSE4.2 y extensiones 64-bit. Soporta también los procesadores Intel Core i3 e i5. Esta opción está disponible desde GCC 4.6, hasta GCC 4.8. A partir de GCC 4.9 se utiliza la definición nehalem.
corei7-avx Intel Core i7 con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AES y PCLMUL y extensiones 64-bit. Soporta también los procesadores Intel Core i3 e i5. Esta opción está disponible desde GCC 4.6, hasta GCC 4.8. A partir de GCC 4.9 se utiliza la definición sandybridge.
goldmont Intel Goldmont con soporte de instrucciones X87, MMX, SSE42, FXSR, CMPXCHG16B, MOVBE, POPCNT, PCLMUL, AES, PRFCHW, CallRegIndirect, SlowLEA, SlowIncDec, SlowBTMem, LAHFSAHF, MPX, SHA, RDSEED, XSAVE, XSAVEOPT, XSAVEC, XSAVES y CLFLUSHOPT. Esta opción está disponible a partir de Clang 5.
haswell Intel Haswell con soporte de instrucciones MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AVX2, AES, PCLMUL, FSGSBASE, RDRND, FMA, BMI, BMI2, F16C y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.9.
i386 Intel i386.
i486 Intel i486.
i586, pentium Intel Pentium sin soporte de instrucciones MMX.
i686 Produce un código binario optimizado para la mayor parte de procesadores compatibles con la serie 80686 de Intel. Todos los actuales lo son.
intel Intel Haswell y Silvermont. Este valor sólo es aplicable en la opción '-mtune='. Esta opción está disponible a partir de GCC 4.9.
ivybridge Intel Ivy Bridge con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AES, PCLMUL, FSGSBASE, RDRND, F16C y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.9.
knl Intel Knights Landing con soporte de instrucciones MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AVX2, AES, PCLMUL, FSGSBASE, RDRND, FMA, BMI, BMI2, F16C, RDSEED, ADCX, PREFETCHW, AVX512F, AVX512PF, AVX512ER y extensiones 64-bit. Esta opción está disponible a partir de GCC 5 y Clang 3.4.
lakemont Intel Quark Lakemont MCU, basado en el procesador Intel Pentium. Esta opción está disponible a partir de GCC 6 y Clang 3.9.
nehalem Intel Nehalem con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.9.
nocona Versión mejorada de Intel Pentium4 con soporte de instrucciones MMX, SSE, SSE2, SSE3 y extensiones 64-bit.
penryn Intel Penryn con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3 y SSE4.1.
pentiumpro Intel PentiumPro.
pentium2 Intel Pentium2 basado en PentiumPro con soporte de instrucciones MMX.
pentium3, pentium3m Intel Pentium3 basado en PentiumPro con soporte de instrucciones MMX y SSE.
pentium4, pentium4m Intel Pentium4 con soporte de instrucciones MMX, SSE y SSE2.
pentium-m Versión de bajo consumo de Intel Pentium3 con soporte de instrucciones MMX, SSE y SSE2. Utilizado por los portátiles Centrino.
pentium-mmx Intel PentiumMMX basado en Pentium con soporte de instrucciones MMX.
prescott Versión mejorada de Intel Pentium4 con soporte de instrucciones MMX, SSE, SSE2 y SSE3.
sandybridge Intel Sandy Bridge con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AES, PCLMUL y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.9 y Clang 3.6.
silvermont Intel Silvermont con soporte de instrucciones MOVBE, MMX, SSE, MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AES, PCLMU, RDRND y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.9 y Clang 3.6.
skx Intel Skylake Server con soporte de instrucciones X87, MMX, AVX, FXSR, CMPXCHG16B, POPCNT, AES, PCLMUL, XSAVE, XSAVEOPT, LAHFSAHF, RDRAND, F16C, FSGSBase, AVX2, BMI, BMI2, FMA, LZCNT, MOVBE, INVPCID, VMFUNC, RTM, HLE, SlowIncDec, ADX, RDSEED, SMAP, MPX, XSAVEC, XSAVES, SGX, CLFLUSHOPT, AVX512, CDI, DQI, BWI, VLX, PKU, PCOMMIT y CLWB. Esta opción está disponible a partir de Clang 3.5. A partir de Clang 3.9 se utiliza también la definición skylake-avx512.
skylake Intel Skylake con soporte de instrucciones MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AVX2, AES, PCLMUL, FSGSBASE, RDRND, FMA, BMI, BMI2, F16C, RDSEED, ADCX, PREFETCHW, CLFLUSHOPT, XSAVEC, XSAVES y extensiones 64-bit. Esta opción está disponible a partir de GCC 6 y Clang 3.6.
skylake-avx512 Intel Skylake Server con soporte de instrucciones MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AVX, AVX2, AES, PCLMUL, FSGSBASE, RDRND, FMA, BMI, BMI2, F16C, RDSEED, ADCX, PREFETCHW, CLFLUSHOPT, XSAVEC, XSAVES, AVX512F, AVX512VL, AVX512BW, AVX512DQ, AVX512CD y extensiones 64-bit. Esta opción está disponible a partir de GCC 6 y Clang 3.9
slm Intel Silvermont con soporte de instrucciones X87, MMX, SSE42, FXSR, CMPXCHG16B, MOVBE, POPCNT, PCLMUL, AES, SlowDivide64, CallRegIndirect, PRFCHW, SlowLEA, SlowIncDec, SlowBTMem y LAHFSAHF. Esta opción está disponible a partir de Clang 3.4. A partir de Clang 3.9 se utiliza también la definición silvermont.
westmere Intel Westmere con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AES, PCLMUL y extensiones 64-bit. Esta opción está disponible a partir de GCC 4.9.
yonah Procesadores basados en la microarquitectura de Pentium M, con soporte de instrucciones MMX, SSE, SSE2 y SSE3.
AMD
amdfam10, barcelona Procesadores basados en AMD Family 10h core con soporte de instrucciones x86-64 (MMX, SSE, SSE2, SSE3, SSE4A, 3DNow!, enhanced 3DNow!, ABM y extensiones 64-bit). Esta opción está disponible a partir de GCC 4.3. La definición barcelona está disponible a partir de Clang 3.6.
athlon, athlon-tbird AMD Athlon con soporte de instrucciones MMX, 3DNow!, enhanced 3DNow! y SSE prefetch.
athlon4, athlon-xp, athlon-mp Versiones mejoradas de AMD Athlon con soporte de instrucciones MMX, 3DNow!, enhanced 3DNow! y full SSE.
bdver1 Procesadores basados en AMD Family 15h core con soporte de instrucciones x86-64 (FMA4, AVX, XOP, LWP, AES, PCL_MUL, CX16, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM y extensiones 64-bit). Esta opción está disponible a partir de GCC 4.7.
bdver2 Procesadores basados en AMD Family 15h core con soporte de instrucciones x86-64 (BMI, TBM, F16C, FMA, LWP, AVX, XOP, LWP, AES, PCL_MUL, CX16, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM y extensiones 64-bit). Esta opción está disponible a partir de GCC 4.7.
bdver3 Procesadores basados en AMD Family 15h core con soporte de instrucciones x86-64 (FMA4, AVX, XOP, LWP, AES, PCL_MUL, CX16, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM y extensiones 64-bit). Esta opción está disponible a partir de GCC 4.8 y Clang 3.4.
bdver4 Procesadores basados en AMD Family 15h core con soporte de instrucciones x86-64 (BMI, BMI2, TBM, F16C, FMA, FMA4, FSGSBASE, AVX, AVX2, XOP, LWP, AES, PCL_MUL, CX16, MOVBE, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM y extensiones 64-bit). Esta opción está disponible a partir de GCC 4.9 y Clang 3.5.
btver1 Procesadores basados en AMD Family 14h core con soporte de instrucciones x86-64 (MMX, SSE, SSE2, SSE3, SSE4A, CX16, ABM y extensiones 64-bit). Esta opción está disponible a partir de GCC 4.6.
btver2 Procesadores basados en AMD Family 16h core con soporte de instrucciones x86-64 (MOVBE, F16C, BMI, AVX, PCL_MUL, AES, SSE4.2, SSE4.1, CX16, ABM, SSE4A, SSSE3, SSE3, SSE2, SSE, MMX y extensiones 64-bit). Esta opción está disponible a partir de GCC 4.8.
geode AMD integrado con soporte de instrucciones MMX y 3DNow!. Esta opción está disponible a partir de GCC 4.3.
k6 AMD K6 con soporte de instrucciones MMX.
k6-2, k6-3 Versiones mejoradas de AMD K6 con soporte de instrucciones MMX y 3DNow!.
k8, opteron, athlon64, athlon-fx Procesadores basados en AMD K8 core con soporte de instrucciones x86-64 (MMX, SSE, SSE2, 3DNow!, enhanced 3DNow! y extensiones 64-bit).
k8-sse3, opteron-sse3, athlon64-sse3 Versiones mejoradas de AMD K8 core con soporte de instrucciones SSE3. Esta opción está disponible a partir de GCC 4.3.
x86-64 Procesadores AMD y compatibles con soporte de instrucciones x86-64, SSE2 y extensiones 64-bit.
znver1 Procesadores basados en AMD Family 17h core con soporte de instrucciones x86-64 (BMI, BMI2, F16C, FMA, FSGSBASE, AVX, AVX2, ADCX, RDSEED, MWAITX, SHA, CLZERO, AES, PCL_MUL, CX16, MOVBE, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM, XSAVEC, XSAVES, CLFLUSHOPT, POPCNT y extensiones 64-bit). Esta opción está disponible a partir de GCC 6 y Clang 4.
VIA
c3 VIA C3 con soporte de instrucciones MMX y 3DNow! (no se implementa planificación para este chip).
c3-2 VIA C3-2 (Nehemiah/C5XL) con soporte de instrucciones MMX y SSE (no se implementa planificación para este chip).
c7 VIA C7 (Esther) con soporte de instrucciones MMX, SSE, SSE2 y SSE (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
eden-x2 VIA Eden X2 con soporte de instrucciones x86-64, MMX, SSE, SSE2 y SSE3 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
eden-x4 VIA Eden X4 con soporte de instrucciones MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX y AVX2 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
esther VIA Eden Esther con soporte de instrucciones MMX, SSE, SSE2 y SSE3 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
nano VIA Nano genérico con soporte de instrucciones x86-64, MMX, SSE, SSE2, SSE3 y SSSE3 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
nano-1000 VIA Nano 1xxx con soporte de instrucciones x86-64, MMX, SSE, SSE2, SSE3 y SSSE3 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
nano-2000 VIA Nano 2xxx con soporte de instrucciones x86-64, MMX, SSE, SSE2, SSE3 y SSSE3 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
nano-3000 VIA Nano 3xxx con soporte de instrucciones x86-64, MMX, SSE, SSE2, SSE3, SSSE3 y SSE4.1 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
nano-x2 VIA Nano Dual Core con soporte de instrucciones x86-64, MMX, SSE, SSE2, SSE3 y SSSE3 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
nano-x4 VIA Nano Quad Core con soporte de instrucciones x86-64, MMX, SSE, SSE2, SSE3 y SSSE3 (no se implementa planificación para este chip). Esta opción está disponible a partir de GCC 7.
IDT
winchip2 IDT Winchip2, que equivale a un i486 con soporte de instrucciones MMX y 3DNow!.
winchip-c6 IDT Winchip C6, que equivale a un i486 con soporte de instrucciones MMX.

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 puede seguir utilizando pero se recomienda utilizar el segundo.

A partir de Clang 3.9, se produce un error de compilación en algunos paquetes, cuando aplicamos este optimizador. El error mostrado es el siguiente:

obrender/.libs/libobrender.so: error: undefined reference to '__mulodi4'
clang-3.9: error: linker command failed with exit code 1 (use -v to see invocation)

Parece ser que el error se produce sólo en sistemas de 32 bits, o utilizando el parámetro '-m32' en sistemas de 64 bits. Que se soluciona (no en todos los paquetes) forzando a Polly a ejecutarse antes que LLVM en el proceso de optimización, con el parámetro siguiente:

$ export {C,CXX}FLAGS+=' -mllvm -polly-position=before-vectorizer'

Con el que sustituimos al anterior explicado en este manual, que tendremos que seguir utilizando allí donde falle el primero.

$ export LDFLAGS+=' -rtlib=compiler-rt -lgcc_s'

-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. El uso de este parámetro requiere también del uso del parámetro siguiente a este, para evitar errores en el enlazado final de los binarios.
-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.

Este parámetro es requerido en todos los procesos de compilación en los que se use el anterior a este y en todos los procesos de compilación en los que se produzca el error undefined reference to '__mulodi4', que no soluciona siempre.
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; export RANLIB=llvm-ranlib; export 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

Por poner un ejemplo práctico con la compilación de FileZilla, he aquí las diferencias en cuanto a tiempo de compilación del paquete con un dual-core a 3,2 Ghz.

1) Clang+CPU+Polly+ThinLTO+GOLD= 8' 50"

2) Clang+CPU+Polly+ThinLTO+LLD= 9' 20"

3) Clang+CPU+Polly+LTO+GOLD= 9' 21"

4) Clang+CPU+Polly+LTO+LLD= 9' 54"

Es evidente que son irrelevantes, y que la optimización requiere de muchos más núcleos para ser eficaz. Tener en cuenta también que el soporte de LTO en el enlazador dinámico LLD para los binarios en formato ELF es inicial, y que por lo tanto, no está tan optimizado como el de GNU gold.
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.

Desde la versión 3.7 de Clang, se incluye soporte completo para la versión 3.1 de OpenMP y parcial para la versión 4.0. 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 -fopemp, el mismo se enlazará contra la librería libgomp de GCC.

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'

PGO
-fprofile-generate=ruta a directorio

-fprofile-use=ruta a directorio
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.

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.

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

3) Ejecutamos el script de configuración pertinente y compilamos el paquete. En la ruta indicada se habrá creado un archivo de datos con el nombre default.profraw. Instalamos el paquete y lo ejecutamos. En este caso, si es una librería tendremos que ejecutar un programa que esté enlazado contra la misma. Cuando el programa esté en ejecución, realizar las acciones más habituales con el mismo (abrir ventanas, desplegar menús, etc), para que esta información sea recogida en el perfil creado.

4) Convertimos el formato en crudo del archivo 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.

$ 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+=" -fprofile-generate=/var/pgo/$dir"; }

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

optclang-profdata () { llvm-profdata merge --output=/var/pgo/$1/default.profdata /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
$ optclang-profdata mc

Segundo proceso de compilación
$ optclang-upgo mc


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 (generación automática de código vectorizado) + Solución al error "undefined reference to '__mulodi4'"
$ export {C,CXX}FLAGS+=' -O3 -mllvm -polly -mllvm -polly-vectorizer=stripmine -mllvm -polly-position=before-vectorizer'

El error no se soluciona en todos los paquetes, en algunos tendremos que seguir utilizando la variable de entorno LDFLAGS indicada en la sección de Polly, y en otros como Wine, ni siquiera ésta soluciona el problema.

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 -mllvm -polly-position=before-vectorizer'

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

ThinLTO
$ export {C,CXX}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; export RANLIB=llvm-ranlib; export NM=llvm-nm
$ export {C,CXX}FLAGS+=' -flto'

OpenMP (sólo en aquellos paquetes en los que se active por defecto el parámetro -fopenmp)
$ export {C,CXX}FLAGS+=' -fopenmp=libomp'

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

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,CXX}=clang; export {C,CXX}FLAGS='-O3 -march=amdfam10 -mtune=amdfam10'; export {C,CXX}FLAGS+=' -O3 -mllvm -polly -mllvm -polly-vectorizer=stripmine'; export {C,CXX}FLAGS+=' -Qunused-arguments'"

alias optclang-cpu="export {CC,CXX}=clang; export {C,CXX}FLAGS='-O3 -march=amdfam10 -mtune=amdfam10 -Qunused-arguments'"


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

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


alias optclang-ar="export AR=llvm-ar; export RANLIB=llvm-ranlib; export 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'; export LDFLAGS+=' -lomp'"



LLD - El enlazador dinámico de LLVM  

Inicialmente creado para versiones de 64 bits de la arquitectura X86 en sus respectivos formatos: ELF (Unix) y COFF (Windows), el enlazador dinámico alternativo a GNU ld fue reescrito desde cero en mayo de 2015, añadiendo soporte también, de ahí que lo incluya en este manual, para sistemas de 32 bits. El uso de este enlazador dinámico sigue siendo muy experimental, y en los procesos de compilación en los que se generan librerías compartidas o modulos dinámicos, puede provocar violación de segmento en los binarios ejecutables generados. A continuación explico cómo hacer uso del mismo con los sistemas de compilación más utilizados en nuestro sistema.

1) Establecer el uso de enlazador dinámico para LLD

1a) GNU Autotools, CMake y sistemas de compilación que acepten la variable de entorno LDFLAGS

Establecemos la correspondiente variable de entorno antes de ejecutar el script de configuración del paquete. 

$ export LDFLAGS+=" -fuse-ld=lld"

En aquellos procesos de compilación donde intervenga Libtool, tendremos que modificar el script incluido en el paquete de código fuente, ltmain.sh, a partir del cual se crea el script libtool, con el siguiente comando, para que Libtool soporte el parámetro fuse-ld=, antes de ejecutar el script de configuración pertinente. Como el script puede estar en el directorio raíz del paquete o en un subdirectorio, con el siguiente comando, solucionamos este inconveniente con la ejecución del mismo en el directorio raíz del paquete a configurar.

$ find . -name 'ltmain.sh' | xargs sed -i 's:|-fuse-linker-plugin:&|-fuse-ld=*:' 

Que automatizamos y simplificamos creando una función de Bash que nos facilitará su ejecución en un proceso de compilación, incluyendo confirmación del proceso realizado. 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:

function lld-libtool
{
if find . -name 'ltmain.sh' | xargs grep -q '\-fuse-ld=*' ; then
 echo "el parche ya está aplicado"
else
  find . -name 'ltmain.sh' | xargs sed -i 's:|-fuse-linker-plugin:&|-fuse-ld=*:'
 echo "parche aplicado"
fi
}

Cuando ejecutemos el comando lld-libtool dentro del directorio raíz del paquete a compilar que utilice Libtool para el proceso de compilación, éste modificará el archivo correspondiente para que Libtool soporte el parámetro fuse-ld=, mostrando información de la acción realizada.

La versión de desarrollo de Libtool ya soporta este parámetro, pero esto sólo es válido para paquetes que no contienen el script de configuración y hay que generarlo, ya sea con el script autogen.sh o con el comando autoreconf -vif. Los paquetes de código fuente nunca suelen tener la versión de Libtool actualizada a la última versión, de ahí que, la modificación antes explicada siga siendo necesaria.

En aquellos paquetes en los que hay que generar el script de configuración, lo que haremos será, modificar el script del sistema, ltmain.sh, ubicado en /usr/share/libtool/build-aux, para no tener que volver a hacerlo después de haber generado el correspondiente script de configuración.

$ su
# sed -i 's:linker-plugin|:&-fuse-ld=*|:' /usr/share/libtool/build-aux/ltmain.sh

2) Optimizar el uso de LLD cuando se hace uso de la optimización LTO o ThinLTO en los procesos de compilación con Clang

Con los siguientes parámetros, aplicamos las correspondiente optimizaciones complementarias de la optimización LTO utilizada con Clang.

$ export LDFLAGS+=" -Wl,--lto-partitions=2 -Wl,--lto-O3"

Donde pone 2 se indica el número de núcleos de nuestro procesador, si sólo tiene uno, no es necesario añadir el primer parámetro. 

Con ThinLTO, cambiamos el primero de los dos parámetros:

$ export LDFLAGS+=" -Wl,--thinlto-jobs=2 -Wl,--lto-O3"

Donde pone 2 se indica el número de núcleos de nuestro procesador, si sólo tiene uno, no es necesario añadir el primer parámetro. 

3) Incluir pases de análisis y transformación de LLVM en el proceso de optimización LTO de LLD

Para elevar aún más el nivel de optimización LTO de LLD, también podemos incluir en dicho proceso, los pases de análisis y transformación en tubería utilizados por LLVM, con los siguientes parámetros, en los que incluyo un ejemplo de pase de análisis y otro de transformación, el uno seguido del otro porque deben de ir siempre en combinación.

$ export LDFLAGS+=" -Wl,--lto-aa-pipeline=aa-basic -Wl,--lto-newpm-passes=loweratomic"

Todos los valores posibles de pases de análisis y transformación los podemos encontrar en este archivo entrecomillados en color rojo. Unos son los de análisis (*_ANALYSIS) y otros son los de transformación (*_PASS). El uso que le podamos dar a todo esto, ya entra dentro del terreno de la ciencia ficción, porque exige un conocimiento de programación informática que la mayoría de usuarios no tenemos, y los beneficios nunca son visibles.

Para saber todas las opciones posibles de LLD, ejecutamos el siguiente comando:

$ ld.lld --help

Y como no podía ser de otra forma, los correspondientes alias de bash para facilitar su uso:

alias clang-lld="export LDFLAGS+=' -fuse-ld=lld'"

alias lld-lto="export LDFLAGS+=' -Wl,--lto-partitions=2 -Wl,--lto-O3'"

alias lld-thinlto="export LDFLAGS+=' -Wl,--thinlto-jobs=2 -Wl,--lto-O3'"

Con clang-lld, activamos el uso de LLD como enlazador dinámico, con lld-lto, activamos las correspondientes optimizaciones complementarias de LTO y con lld-thinlto las correspondientes a ThinLTO.



Compilar programas con Clang  Bloc de Notas

A diferencia de GCC, Clang utiliza el mismo binario ejecutable para compilar todos los lenguajes soportados por éste, con lo que la variable de entorno a aplicar antes del proceso de configuración de los paquetes es la siguiente:

$ export {CC,CXX}=clang

Además, en aquellas compilaciones que se requiera de un enlazado contra Libstdc++.so, utilizaremos siempre la librería de la versión de GCC con la que haya sido compilado Clang. Los usuarios que utilicen Ccache como compilador caché de GCC, también pueden utilizar Clang, estableciendo previamente al proceso de configuración del paquete, la correspondiente variable de entorno.

$ export CCACHE_CC=clang

A continuación pongo el ejemplo de compilación de NomNom, utilizando GCC, Clang, Clang con Polly y LTO, y Clang con Ccache. Las optimizaciones aplicadas son las del procesador que tengo en mi ordenador.

Extracción y Configuración con GCC

$ tar Jxvf nomnom-0.3.1.tar.xz
$ cd nomnom-0.3.1
$ export {C,CXX}FLAGS='-O3 -march=amdfam10 -mtune=amdfam10'
$ export PATH=/usr/local/lib/qt4/bin:$PATH
$ export LDFLAGS="-Wl,-rpath,/opt/gcc7/lib -lstdc++"
$ ./configure --disable-dependency-tracking --prefix=/usr

Extracción y Configuración con Clang

$ tar Jxvf nomnom-0.3.1.tar.xz
$ cd nomnom-0.3.1
$ export {C,CXX}FLAGS='-O3 -march=amdfam10 -mtune=amdfam10'
$ export {CC,CXX}=clang
$ export PATH=/usr/local/lib/qt4/bin:$PATH
$ export LDFLAGS="-Wl,-rpath,/opt/gcc7/lib -lstdc++"
$ ./configure --disable-dependency-tracking --prefix=/usr

Extracción y Configuración con Clang, Polly y LTO

$ tar Jxvf nomnom-0.3.1.tar.xz
$ cd nomnom-0.3.1
$ export {C,CXX}FLAGS='-O3 -march=amdfam10 -mtune=amdfam10'
$ export {C,CXX}FLAGS+=" -O3 -mllvm -polly -mllvm -polly=vectorizer=stripmine -flto"
$ export {CC,CXX}=clang
$ export PATH=/usr/local/lib/qt4/bin:$PATH
$ export LDFLAGS="-Wl,-rpath,/opt/gcc7/lib -lstdc++"
$ ./configure --disable-dependency-tracking --prefix=/usr

Extracción y Configuración con Clang y Ccache

$ tar Jxvf nomnom-0.3.1.tar.xz
$ cd nomnom-0.3.1
$ export {C,CXX}FLAGS='-O3 -march=amdfam10 -mtune=amdfam10'

$ export CCACHE_CC=clang
$ export PATH=/usr/local/lib/qt4/bin:$PATH
$ export LDFLAGS="-Wl,-rpath,/opt/gcc7/lib -lstdc++"
$ ./configure --disable-dependency-tracking --prefix=/usr

Compilación con GCC

[jose@localhost nomnom-0.3.1]$ make
make  all-recursive
make[1]: se ingresa al directorio `/home/jose/descargas/nomnom-0.3.1'
Making all in src
make[2]: se ingresa al directorio `/home/jose/descargas/nomnom-0.3.1/src'
/usr/local/lib/qt4/bin/uic -o ui_MainWindow.h ../src/rc/MainWindow.ui
make  all-am
make[3]: se ingresa al directorio `/home/jose/descargas/nomnom-0.3.1/src'
  CXX    naboutdialog.o
g++ -DHAVE_CONFIG_H -I. -I..  -I../src -I../src/i -DDATADIR='"/usr/share"' -DQT_SHARED -I/usr/local/lib/qt4/include -I/usr/local/lib/qt4/include/QtCore   -DQT_SHARED -I/usr/local/lib/qt4/include -I/usr/local/lib/qt4/include/QtGui -I/usr/local/lib/qt4/include/QtCore   -DQT_SHARED -I/usr/local/lib/qt4/include -I/usr/local/lib/qt4/include/QtCore     -O3 -march=amdfam10 -mtune=amdfam10 -c -o naboutdialog.o `test -f 'about/naboutdialog.cpp' || echo './'`about/naboutdialog.cpp
  CXX    ndetectdialog.o

Compilación con Clang

[jose@localhost nomnom-0.3.1]$ make
make  all-recursive
make[1]: se ingresa al directorio `/home/jose/descargas/nomnom-0.3.1'
Making all in src
make[2]: se ingresa al directorio `/home/jose/descargas/nomnom-0.3.1/src'
/usr/local/lib/qt4/bin/uic -o ui_MainWindow.h ../src/rc/MainWindow.ui
make  all-am
make[3]: se ingresa al directorio `/home/jose/descargas/nomnom-0.3.1/src'
  CXX    naboutdialog.o
clang -DHAVE_CONFIG_H -I. -I..  -I../src -I../src/i -DDATADIR='"/usr/share"' -DQT_SHARED -I/usr/local/lib/qt4/include -I/usr/local/lib/qt4/include/QtCore   -DQT_SHARED -I/usr/local/lib/qt4/include -I/usr/local/lib/qt4/include/QtGui -I/usr/local/lib/qt4/include/QtCore   -DQT_SHARED -I/usr/local/lib/qt4/include -I/usr/local/lib/qt4/include/QtCore     -O3 -march=amdfam10 -mtune=amdfam10 -c -o naboutdialog.o `test -f 'about/naboutdialog.cpp' || echo './'`about/naboutdialog.cpp

Compilación con Clang, Polly y LTO

[jose@localhost nomnom-0.3.1]$ make
make  all-recursive
make[1]: Entering directory '/home/jose/descargas/nomnom-0.3.1'
Making all in src
make[2]: Entering directory '/home/jose/descargas/nomnom-0.3.1/src'
/usr/local/lib/qt4/bin/uic -o ui_MainWindow.h ../src/rc/MainWindow.ui
make  all-am
make[3]: Entering directory '/home/jose/descargas/nomnom-0.3.1/src'
  CXX    naboutdialog.o
clang -DHAVE_CONFIG_H -I. -I..  -I../src -I../src/i -DDATADIR='"/usr/share"' -DQT_SHARED -I/usr/local/lib/qt4/include -I/usr/local/lib/qt4/include/QtCore  -DQT_SHARED -I/usr/local/lib/qt4/include -I/usr/local/lib/qt4/include/QtGui -I/usr/local/lib/qt4/include -I/usr/local/lib/qt4/include/QtCore  -DQT_SHARED -I/usr/local/lib/qt4/include -I/usr/local/lib/qt4/include/QtCore    -O3 -march=amdfam10 -mtune=amdfam10 -O3 -mllvm -polly -mllvm -polly-vectorizer=stripmine -flto -c -o naboutdialog.o `test -f 'about/naboutdialog.cpp' || echo './'`about/naboutdialog.cpp

Compilación con Clang y Ccache (se muestra el enlace simbólico g++)

[jose@localhost nomnom-0.3.1]$ make
make  all-recursive
make[1]: se ingresa al directorio `/home/jose/descargas/nomnom-0.3.1'
Making all in src
make[2]: se ingresa al directorio `/home/jose/descargas/nomnom-0.3.1/src'
/usr/local/lib/qt4/bin/uic -o ui_MainWindow.h ../src/rc/MainWindow.ui
make  all-am
make[3]: se ingresa al directorio `/home/jose/descargas/nomnom-0.3.1/src'
  CXX    naboutdialog.o
g++ -DHAVE_CONFIG_H -I. -I..  -I../src -I../src/i -DDATADIR='"/usr/share"' -DQT_SHARED -I/usr/local/lib/qt4/include -I/usr/local/lib/qt4/include/QtCore   -DQT_SHARED -I/usr/local/lib/qt4/include -I/usr/local/lib/qt4/include/QtGui -I/usr/local/lib/qt4/include/QtCore   -DQT_SHARED -I/usr/local/lib/qt4/include -I/usr/local/lib/qt4/include/QtCore     -O3 -march=amdfam10 -mtune=amdfam10 -c -o naboutdialog.o `test -f 'about/naboutdialog.cpp' || echo './'`about/naboutdialog.cpp
  CXX    ndetectdialog.o



Comparativa de resultados de optimización entre Clang y GCC

Tomando como referencia la compilación del programa Openbox, pongo a continuación una tabla de resultados de optimización entre Clang y GCC, utilizando los mismos parámetros de optimización, que son equivalente entre sí. En esta tabla se mide el tiempo de compilación (sin utilizar compilación en paralelo) y el tamaño resultante del binario openbox, después de haberse compilado en el directorio y también después de haberlo instalado con el comando make install-strip que elimina los símbolos de depuración innecesarios.

Para medir el tiempo de compilación se sustituye el comando de compilación por el siguiente:

$ date &> inicio.log; make; date &> final.log

Y hacemos la correspondiente resta: hora de inicio (inicio.log) - hora final (final.log) = tiempo total de compilación.

En compilaciones cortas, como es el caso de ésta, podemos utilizar este comando (sin volcar nada a ningún archivo) y ver el resultado en la pantalla, eso sí, tendremos que desplazarnos hacia arriba en la ventana de terminal, para buscar la hora de inicio.

$ date; make; date

En paquetes en los que la compilación es en modo silencioso por defecto, añadir el parámetro V=1 o VERBOSE=1 si se han configurado con CMake, al comando make, para poder ver los parámetros de optimización aplicados al paquete.

Como siempre esto se puede automatizar hasta cierto punto, con los correspondiente alias de bash, un ejemplo con los míos, sustituir 2 por el número de núcleos que tenga el procesador de cada usuario. 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 makej1="date +"%H:%M:%S" &> inicio.log; make V=1; date +"%H:%M:%S" &> final.log"
alias makej2="date +"%H:%M:%S" &> inicio.log; make V=1 -j2; date +"%H:%M:%S" &> final.log"
alias cmakej1="date +"%H:%M:%S" &> inicio.log; make VERBOSE=1; date +"%H:%M:%S" &> final.log"
alias cmakej2="date +"%H:%M:%S" &> inicio.log; make VERBOSE=1 -j2; date +"%H:%M:%S" &> final.log"


Y finalmente creamos un script para poder hacer la correspondiente resta. Necesitaremos el programa datediff, que forma parte del paquete Dateutils. Abrimos un editor de texto y añadimos lo siguiente:

#!/bin/sh

date1=`cat inicio.log`
date2=`cat final.log`

ddiff $date1 $date2 -f '%Hh %Mm %Ss'


Lo guardamos con el nombre tiempo y lo instalamos en /usr/bin.

$ su -c "install -m755 tiempo /usr/bin"

Y comprobamos que funciona bien, probando a ejecutarlo cuando terminemos de compilar un paquete.

[jose@localhost dateutils-0.3.3]$ tiempo
0h 0m 10s

La última comparativa de esta tabla es sin optimizaciones adicionales, sólo se utilizan las proporcionadas por el paquete (-O2). Por último, tener en cuenta que un binario más pequeño no tiene por qué equivaler a un binario más rápido.

Comparativa de resultados de optimización entre Clang 5 y GCC 7, con código escrito en C
Parámetros aplicados Tiempo de compilación Tamaño del binario compilado Tamaño del binario instalado
Clang+O3+CPU+Polly+ThinLTO+LLD 1' 19" 432 KB. 352 KB.
GCC+O3+CPU+Graphite+LTO 55"
552 KB. 496 KB.

Clang+O3+CPU+Polly+ThinLTO 1' 16" 416 KB. 348 KB.
GCC+O3+CPU+Graphite+LTO 55"
552 KB. 496 KB.

Clang+O3+CPU+Polly+LTO 1' 19"
396 KB. 344 KB.
GCC+O3+CPU+Graphite+LTO 55"
552 KB. 496 KB.

Clang+O3+CPU+Polly 1' 15" 352 KB. 300 KB.
GCC+O3+CPU+Graphite 55" 524 KB. 472 KB.

Clang+O3+CPU+LTO 1' 19" 400 KB. 348 KB.
GCC+O3+CPU+LTO 52" 552 KB. 496 KB.

Clang+O3+CPU 1' 14" 352 KB. 300 KB.
GCC+O3+CPU 54" 524 KB. 472 KB.

Clang+O3 1' 13" 352 KB. 300 KB.
GCC+O3 53" 516 KB. 464 KB.

Clang 1' 15" 1,8 MB. 296 KB.
GCC 54" 1,8 MB. 384 KB.
De color amarillo las celdas con los mejores resultados.

Esto es con código escrito en C, pero ahora los compararemos con código escrito en C++, tomando como referencia el manual de instalación de Pekwm.

Comparativa de resultados de optimización entre Clang 5 y GCC 7, con código escrito en C++
Parámetros aplicados Tiempo de compilación Tamaño del binario compilado Tamaño del binario instalado
Clang+O3+CPU+Polly+ThinLTO+LLD 1' 15" 868 KB. 768 KB.
GCC+O3+CPU+Graphite+LTO 1' 07"
736 KB. 632 KB.

Clang+O3+CPU+Polly+ThinLTO 1' 11" 872 KB. 772 KB.
GCC+O3+CPU+Graphite+LTO 1' 07"
732 KB. 612 KB.

Clang+O3+CPU+Polly+LTO 1' 21"
812 KB. 728 KB.
GCC+O3+CPU+Graphite+LTO 1' 07"
732 KB. 612 KB.

Clang+O3+CPU+Polly 1' 17" 924 KB. 792 KB.
GCC+O3+CPU+Graphite 1' 18" 1,0 MB. 872 KB.

Clang+O3+CPU+LTO 1' 20" 816 KB. 732 KB.
GCC+O3+CPU+LTO 1' 04" 736 KB. 632 KB.

Clang+O3+CPU 1' 14" 924 KB. 792 KB.
GCC+O3+CPU 1' 20" 1,0 MB. 872 KB.

Clang+O3 1' 15" 928 KB. 796 KB.
GCC+O3 1' 20" 976 KB. 848 KB.

Clang 1' 24" 14,9 MB. 760 KB.
GCC 1' 39" 16,3 MB. 688 KB.
De color amarillo las celdas con los mejores resultados.



Enlaces


http://llvm.org >> La web del proyecto LLVM.

http://clang.llvm.org >> La sección de la web de LLVM dedicada a Clang

http://lld.llvm.org >> La sección de la web de LLVM dedicada a LLD.

http://polly.llvm.org >> La sección de la web de LLVM dedicada a Polly.

http://compiler-rt.llvm.org >> La sección de la web de LLVM dedicada a Compiler-rt.

http://openmp.llvm.org >> La sección de la web de LLVM dedicada a OpenMP.


Foro Galería Blog


Actualizado el 05-09-2017

Instalar Clang desde cero

Instalar Binutils desde ceroInstalar CMake desde cero