¿Qué es exactamente el sonido metálico? ¿Cuál es exactamente la diferencia entre gcc y clang?

Recientemente, encontré que no tengo muy claro la diferencia entre GNU GCC y Clang, lo que afecta un poco la implementación y el aprendizaje, así que aproveché estos dos días para estudiarlo detenidamente.

Durante este proceso de investigación, descubrí que muchos problemas en realidad surgen del lenguaje (no se refiere al lenguaje de programación, sino a la distorsión de las traducciones al chino y al inglés) y la comprensión conceptual.

Si revisa en línea clang, algunas personas le dirán que se trata de una interfaz, y luego extraerán algunas introducciones del compilador del libro, y luego enumerarán un montón de tablas para comparar, sin dar una explicación detallada de los principios y mecanismos y presentación. Así que habrá más problemas apareciendo en este momento:

  • ¿Por qué clanguna parte delantera? ¿No es un compilador completo? Si clanges un compilador completo, ¿por qué se llama front-end? Si no está completo, ¿cuál es el backend?
  • ¿Cuál es exactamente la definición de un compilador? Siento que la definición del compilador en el libro gcces diferente de la real.

Déjame explicarte aquí: aquí gccse refiere a los comandos que puedes usar directamente en Ubuntu y otras distribuciones de Linux (del grupo de software GNU), si se refiere al proyecto, se escribirá como "GNU GCC". Si se refiere a llvm-gcc, no se abreviará como gcc.

Este artículo responderá gradualmente a esta serie de preguntas y, en el proceso, no solo le permitirá descubrir qué es , sino que también le permitirá saber más clangsobre el proceso de compilación, el compilador y LLVM.gcc

Un programa como gcceste nombre moderno "compilador" es una colección de herramientas

Primero se debe entender una cosa, que puede considerarse como la respuesta a la mayoría de las preguntas anteriores o la fuente de malentendidos: un "compilador" moderno como este es una colección de herramientas, incluido un preprocesador, un compilador, y llamará el ensamblador, vincule gccvarias herramientas, como el compilador o el cargador, en lugar de un solo compilador (este tipo de conflicto entre términos y sustantivos también es una de las razones importantes para el engaño).

La respuesta se establece primero para permitir que los lectores vayan a la explicación con la respuesta, para que puedan entender mejor.

Qué es exactamente un compilador (o cuál es el proceso de compilación)

Como se mencionó anteriormente, "compilador" es un término que a menudo expresa conflictos: en muchos discursos, blogs, libros de texto y libros profesionales, un compilador se describe como "un programa que convierte el código fuente en un programa ejecutable" (como gcceste Un "compilador "puede programar directamente el código fuente en un programa ejecutable) . Esta declaración describe de manera sucinta y precisa gcclo que sucede cuando usa el comando, pero no es una definición del compilador.

Echemos un vistazo a la introducción a los compiladores en el libro más clásico relacionado con la compilación "Principios de compilación" (es decir, el Libro del dragón), que también es el significado más clásico de compiladores:

En términos simples, un compilador es un programa que lee un programa en un idioma determinado (este idioma es el idioma de origen ) y lo traduce a un programa equivalente en otro idioma (este idioma se denomina idioma de destino ). Una de las disposiciones más importantes del compilador es informar de los errores encontrados en el programa "fuente" durante la traducción.
Si el programa objeto está en lenguaje de máquina, entonces el programa objeto es un programa ejecutable.

Luego regrese a la definición en "Principios de compilación", que en realidad es: el compilador convierte el código fuente que escribimos para obtener código en otro idioma, y ​​si el código convertido está en lenguaje de máquina, entonces el código objeto es ejecutable. programa. Pero si hay varios archivos de código fuente o bibliotecas con enlaces externos, entonces puede ser un objeto compartido.

"Código" se refiere a un conjunto de números, letras y símbolos en inglés, y se traduce como "código" o "código" en chino. La esencia
de una aplicación es un conjunto de archivos binarios explicados en lenguaje de máquina.

Es decir, un compilador es en realidad un programa que convierte un idioma en otro .

Sin embargo, de acuerdo con el proceso de compilación estándar de las últimas décadas, un compilador se refiere a un programa que .cconvierte archivos como archivos en archivos. .sPara facilitar la explicación, a menos que se especifique lo contrario, el siguiente "compilador" se define de acuerdo con esto.

Según esta definición, el flujo de trabajo interno del compilador es más o menos el siguiente:

C 语言代码
C 前端
优化器
C++ 语言代码
C++ 前端
Objective-C 语言代码
Objective-C 前端
X86后端
X86汇编代码
ARM 后端
ARM 汇编代码

El compilador puede generar código ensamblador para la plataforma especificada. Luego, el ensamblador convierte el código ensamblador en lenguaje de máquina y, finalmente, el enlazador lo conecta en un programa ejecutable.

Además, hay algunos puntos para agregar:

  1. Los diversos códigos de idioma aquí están preprocesados;
  2. El front-end de un lenguaje generalmente se refiere al analizador léxico (Lexer) y al analizador (Parser).El front-end convertirá el código fuente paso a paso (de alto nivel a bajo nivel) en la expresión intermedia ( IR) requerido por el optimizador.Este es un multi-análisis realizado por el dispositivo.
  3. Generalmente, un "AST (Árboles de sintaxis abstracta)" se enumera por separado delante del optimizador. Esta es una expresión intermedia de alto nivel, que es básicamente la reorganización del código fuente.
  4. El optimizador a veces se denomina el extremo medio. El optimizador no solo mejora el rendimiento, sino que también como extremo medio puede hacer que los extremos delantero y trasero estén mejor separados, lo que aumenta la posibilidad de compilación cruzada.

El proceso desde el código fuente hasta la expresión intermedia de alto nivel, y luego desde el nivel intermedio al bajo, es más o menos el siguiente:

Flujo desde el código fuente a las representaciones intermedias de alto nivel, y de las intermedias a las de bajo nivel

Aquí hay un artículo que lo presenta con más detalle: "Representación intermedia"

El proceso de convertir el código fuente en un programa ejecutable

El proceso completo de convertir el código fuente en un programa ejecutable es lo que solemos llamar el "proceso de compilación". En las últimas décadas, este proceso estándar ha sido más o menos el siguiente (los rectángulos redondeados representan el código, los rectángulos representan varios procesadores):

源代码
预处理器
调整之后的代码
编译器
汇编语言代码
汇编器
可调整的机器语言代码
连接器或加载器
可执行程序

Se puede ver que desde el código fuente hasta el programa ejecutable, tiene que pasar por el preprocesador (preprocesador), compilador (compilador), ensamblador (ensamblador) y enlazador (enlazador) o cargador (cargador), y el compilador es solo responsable de la fuente El código se convierte en la función del código ensamblador correspondiente.

Pantalla de proceso: gcc y soporte cpp, as, programa de conversión de procesamiento ld

Además del compilador y ensamblador de los varios programas de conversión de procesamiento mencionados anteriormente, se estima que los otros tres se escuchan muy raramente. A continuación se utiliza el lenguaje C más clásico gccpara introducir este proceso, gccel preprocesador incluido es cpp, y el ensamblador asy el enlazador también se denominan ld.

Para la introducción de los tres, así como el proceso detallado de cada paso en el proceso de compilación, puede leer mi otro artículo "Usar gcc para mostrar el proceso de compilación completo" . Este artículo también presenta algunas instrucciones gccsobre cómo hacerlo. Se recomienda encarecidamente que le eche un vistazo a este artículo después de leerlo, de lo contrario, es posible que solo entienda el contenido literal. El contenido del artículo originalmente estaba destinado a colocarse aquí, pero aumentará la cantidad de palabras a 20,000 palabras, lo que tomará demasiado largo para leer .

flujo de trabajo interno de gcc

El flujo de trabajo interno de gcc es el siguiente, y aquí se ignora el proceso de preprocesamiento:

C 语言代码
C 前端
AST 代码
优化器
LLVM IR 码
C++ 语言代码
C++ 前端
Objective-C 语言代码
Objective-C 前端
X86 后端
X86 汇编代码
ARM 后端
ARM 汇编代码

Flujo de trabajo dentro de Clang

Con el desarrollo y el progreso de los tiempos, el proceso de compilación a la antigua no es suficiente:

  1. La optimización del rendimiento requiere demasiada mano de obra y recursos materiales (el lenguaje ensamblador actual es mucho más complicado que antes, el manual clásico de PDP-11 tiene menos de 30 páginas de instrucciones, pero ahora el manual de instrucciones de Intel X86 tiene solo 2500 páginas);
  2. El consumo de desarrollo para cada máquina es alto (por ejemplo, compilar el mismo programa en ARM y X86);
  3. Los "complementos" del compilador no son suficientes (a veces se necesitan nuevas optimizaciones o procesamiento).

Cuando vea esto, comprenderá que el front-end aquí se refiere al front-end del proceso de compilación de toda la familia del lenguaje C, no al front-end de un compilador. So clang es un compilador completo que se convierte .cen .sun archivo, pero llama al ensamblador y al compilador para producir el ejecutable final.

Como compilador, clang puede convertir el lenguaje de la familia C que escribió en LLVM IR (un lenguaje de bajo nivel), luego convertir y generar un .sarchivo, y luego llamar al ensamblador (u otro ensamblador) en el proyecto LLVM para ensamblarlo en un .oarchivo de objeto (es decir, la "etapa de ensamblaje" mencionada anteriormente), y finalmente llame al enlazador para conectarse y generar un programa ejecutable.

Es decir, el proceso interno del compilador descrito anteriormente se convierte en el siguiente proceso, y aquí también se omite el proceso de preprocesamiento:

C 语言代码
C 前端
AST
优化器
LLVM IR 码
C++ 语言代码
C++ 前端
Objective-C 语言代码
Objective-C 前端
后端llc
X86 汇编代码
ARM 汇编代码

clang后面使用的的汇编器和连接器,既可以使用 LLVM 集成,也可以使用 GNU 的,比如连接器可以使用 LLVM 集成的的lld,也可以使用 GNU 的ldgold,以及 MSVC的link.exe。不过默认情况下是使用 LLVM 集成的。

如果你好奇更详细 Clang 工作流程,和每一步的操作,比如说什么选项对应的是编译过程的某一步,可以看看这篇文档《An Overview of Clang》,我就不单独写博客了。

这种编译方式对于适配不同平台来说非常方便。当出现一个新的平台,只要将指令与 LLVM IR 对应即可,完全不用开发者去写一个全新的优化器和代码生成器去将源代码转换成汇编代码,省时省力。

为什么clang是一个前端?难道它不是完整的编译器吗?如果clang是完整的编译器的话,那么为什么叫前端呢?如果它不是完整的,那么后端是什么呢?

Clang 是一个完整的编译器,也是一个前端。不过是将源代码转换成可执行程序流程的前端,而不是编译器的前端。如果说是编译器的前端,那是预处理器、词义分析器(Lexer)和语法分析器(Parser)等部分构成的。

clang对应的后端指的是 LLVM 内含的,或者 GNU 等软件组的连接器、编译器等工具,这些工具负责将汇编代码汇编、连接成最后的可执行文件。

编译器的定义到底是什么?感觉书上编译器的定义和实际的gcc有所不同

关于编译器的定义前文有详细的解释,现在一般情况下“编译器”指的是从将.c等文件转换成.s文件的程序。

实际上编译器,比如gcc包含了一些工具(比如预处理器),也会去调用其他的工具(汇编器和连接器),所以与定义有所不同。

LLVM 项目是干什么项目?

前文提到,很多编译器是需要多个中间表达(IR)的,这些中间表达可能是词汇分析器生成的,也可能是语义分析器生成的,就很不统一,这就导致更新指令和优化性能随着数量的大幅提升成为了一件很困难的事情。

LLVM 全名“Low-Level Virtual Machine”,是一架构和中间表达的实现。而 LLVM 项目最初是一套围绕着 LLVM 代码的工具,C 语言和对应的 LLVM 代码如下(源自Chris Lattner 的《Architecture for a Next-Generation GCC》):
Por favor agregue una descripción de la imagen

LLVM 代码有三种用途:

  1. 编译器的中间表达;
  2. 存放在硬盘里的位码(bitcode);
  3. 人类可读的汇编语言表达

这三种用途实际上都是等价的,要么能共用,要么有工具可以很轻松的转换,这点就让 LLVM 兼容新的机器、优化性能、开发新的语言,甚至是反汇编都是很容易的。

整个项目最核心内容其实就是 LLVM IR。LLVM IR 旨在成为某种“通用IR”,希望足够低级,可以将高级代码干净地映射到 LLVM IR(类似于处理器使用的指令是“通用IR”,允许将许多种不同的语言映射到这些汇编语言)。这给使用 LLVM IR 的编译器带来了性能很不错提升。

关于 LLVM 设计更详细的介绍还是请看文档:《LLVM Language Reference Manual》

关于 LLVM 带来的性能提升可以看 Intel 的这篇文章:《Intel® C/C++ Compilers Complete Adoption of LLVM》

reinders-2021-LLVM-puntos de referencia-01

gcc和clang有什么区别?

LLVM 早期有一个名为llvm-gcc的项目,它和 GNU GCC 的最大区别就在于:llvm-gcc在编译器最后使用的是 LLVM 作为最低一级的中间表达,而不是 GNU GCC 使用的的 RTL 作为最低一级的中间表达,所以llvm-gcc编译器的最后一部分是处理 LLVM IR,而不是处理 RTL(Register Transfer Language)。

其他方面,llvm-gccgcc一样将会输出一个汇编文件,工作原理也一样。不过可以通过使用-emit-llvm选项来让llvm-gcc输出 LLVM 字节码。

后来 LLVM 创始人 Chris Lattner 在苹果的时候就开创了一个中间表达全部使用 LLVM 作为中间表达的 C 语言家族的编译器,也就是 Clang。

虽然clang淘汰了llvm-gcc,虽然现在还是有llvm-gcc,但是使用率和性能都不如clang。也正是因为 LLVM IR,Clang进行反汇编也很方便。

下面是 Chris Lattner 简历中提到 Clang 诞生的部分(https://www.nondot.org/sabre/Resume.html#Apple):

Captura de pantalla del currículum de Chris Lattner

这里字太小了,机翻一下:

voltear la máquina

总结一下,gccclang的区别在于:clang的各个中间层均为 LLVM IR,而gcc的各个中间层为 TRL 或其他一些事物。

Cabe señalar aquí que no es lo mismo que el compilador LLVM descrito por el llvm-gccfundador de LLVM, Chris Lattner, en "Arquitectura para un GCC de próxima generación" . Este compilador LLVM no es lo mismo que el Clang posterior . El diagrama esquemático del compilador LLVM en el documento es el siguiente:

Por favor agregue una descripción de la imagen

La diferencia es que se agrega una capa de conexión en el medio y se realizan dos conexiones en todo el compilador. Pero obviamente, según los datos de Intel, el rendimiento y el efecto del compilador LLVM son similares a los de GNU GCC. Pero ahora aún puede subir y bajar en GitHub, la última versión es 16: https://github.com/llvm/llvm-project/releases/tag/llvmorg-16.0.0

Puede elegir clangdescargar con:
Por favor agregue una descripción de la imagen

También se puede descargar por separado:
Por favor agregue una descripción de la imagen

En el proceso de escribir este blog, tengo una comprensión más profunda del uso y la comprensión de los compiladores gcc. clangSin embargo, dado que este artículo es demasiado largo, es inevitable que haya revelaciones. Si encuentra errores (errores, errores tipográficos, algunas cosas que olvidó eliminar, etc.) durante el proceso de lectura, comente y hágamelo saber ~

Espero ayudar a los necesitados ~

Supongo que te gusta

Origin blog.csdn.net/qq_33919450/article/details/130911617
Recomendado
Clasificación