编译器的前端中端和后端

前面说的词法分析语法分析,确实是编译器前端 (Front End) 最核心的两个部分。但前端的工作还没有结束。


编译器各阶段划分

一个完整的编译器通常可以分为三个部分:前端、中端 (Middle End)、后端 (Back End)

前端 (Front End)
  • 核心职责: 理解源代码。它负责处理与源语言 (Source Language) 相关的所有事情。
  • 输入: 源代码文件 (e.g., program.c)。
  • 输出: 中间表示 (Intermediate Representation, IR)。这是一种独立于具体硬件平台的、类似于“通用汇编语言”的代码表示。
  • 主要工作流程:
    1. 词法分析 (Lexical Analysis): 源代码 -> Token 序列。
    2. 语法分析 (Syntax Analysis): Token 序列 -> 语法树 (Syntax Tree)。
    3. 语义分析 (Semantic Analysis): 这是前端的第三个,也是非常重要的一个步骤

“语义分析”属于前端。

  • 语义分析做什么?
    • 语法分析只管“结构对不对”,不管“意思对不对”。比如 int a = "hello"; 这句话,从语法结构上看(类型 标识符 = 字面量;),是完全正确的。
    • 但从**语义(意思)**上看,它是错误的,因为你不能把一个字符串赋值给一个整型变量。
    • 语义分析就是负责检查这些“意思”层面的错误,主要包括:
      • 类型检查: 运算符两边的类型是否匹配?函数调用的参数类型和数量是否正确?
      • 作用域分析: 变量在使用前是否已经声明?是否存在重复定义的变量?
      • 等等…
    • 语义分析通常会向语法树中添加额外的信息(比如每个节点的类型),形成一个“带注解的语法树”或直接生成中间表示。
中端 (Middle End) / 优化器 (Optimizer)
  • 核心职责: 优化代码。它在一种独立于具体机器的层面上,对代码进行等价变换,让它运行得更快、占用空间更小。
  • 输入: 前端生成的中间表示 (IR)
  • 输出: 优化后的中间表示 (IR)
  • 主要工作:
    • 生成中间代码: 将语法树(或带注解的语法树)转换成一种更线性的、类似汇编的中间表示(如三地址码)。
    • 代码优化: 这是编译技术中最复杂、最精华的部分之一。包括但不限于:
      • 删除无用代码 (Dead Code Elimination)
      • 常量折叠 (Constant Folding): 比如把 2 + 3 在编译时直接算成 5
      • 循环优化 (Loop Optimizations)
      • 函数内联 (Function Inlining)

所以,“生成中间代码”和“代码优化”属于中端。

后端 (Back End)
  • 核心职责: 生成目标代码。它负责处理与目标机器 (Target Machine) 相关的所有事情。
  • 输入: (优化后的)中间表示 (IR)
  • 输出: 目标机器的汇编代码或机器码 (e.g., program.s or program.o)。
  • 主要工作:
    1. 指令选择 (Instruction Selection): 将通用的中间代码指令,翻译成特定CPU的指令(比如 x86 的 mov, add 指令)。
    2. 寄存器分配 (Register Allocation): 决定哪些变量应该放在CPU的高速寄存器里,哪些放在内存里。这是一个对性能至关重要的步骤。
    3. 指令调度 (Instruction Scheduling): 调整指令的顺序以适应CPU的流水线特性,避免等待。
    4. 最终代码生成: 输出汇编代码或二进制文件。

所以,“生成目标程序”属于后端。


前端和后端的主要区别 (The “Why”)

这种“前-中-后”三段式的设计是现代编译器的基石,其最大的好处是解耦 (Decoupling)复用 (Reuse)

  • 前端 (Source-Dependent, Target-Independent)

    • 只关心源语言: C++ 的前端和 Swift 的前端完全不同。
    • 不关心目标机器: C++ 的前端不在乎最终代码是跑在 Intel CPU 上还是 ARM CPU 上。它只生成一份通用的 IR。
  • 后端 (Source-Independent, Target-Dependent)

    • 不关心源语言: 后端拿到的是通用的 IR,它根本不知道这份 IR 最初是由 C++ 还是 Swift 写成的。
    • 只关心目标机器: 针对 Intel x86 的后端和针对 ARM 的后端是完全不同的。

这种设计的巨大优势:
想象一下,我们要支持 M 种编程语言(C++, Swift, Rust, …)和 N 种目标CPU架构(x86, ARM, RISC-V, …)。

  • 如果没有前后端分离: 我们需要为每一种语言和每一种CPU的组合都写一个完整的编译器。总共需要 M * N 个编译器。
  • 有了前后端分离: 我们只需要为每种语言写一个前端(共 M 个),为每种CPU写一个后端(共 N 个)。然后像搭积木一样,把它们通过统一的中间表示 (IR) 连接起来。总共只需要 M + N 个组件。

这就是像 LLVM 这样的现代编译器架构如此成功的原因。Clang 是 C/C++/Objective-C 的前端swiftc 是 Swift 的前端,它们都会生成 LLVM IR。然后,LLVM 提供了强大的中端优化器和针对各种CPU的后端,来完成剩下的工作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/96593.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/96593.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

黑马Java进阶教程,全面剖析Java多线程编程,并发和并行,笔记02

黑马Java进阶教程,全面剖析Java多线程编程,并发和并行,笔记02 一、并发和并行 并发:在同一时刻,有多个指令在单个CPU上交替执行 并行:在同一时刻,有多个指令在多个CPU上同时执行 二、为什么有…

20250908 背包DP总结

引子 ~ 我们都有一个家,名字叫背包 ~ 背包DP 顾名思义,背包DP是用来解决背包最值问题的。题目会给出背包的容量,以及几个物品的属性,比如重量,价值,限额等等,具体是什么看题目。 01背包 01…

Redis持久化之RDB:快照机制原理、配置与最佳实践

Redis持久化之RDB:快照机制原理、配置与最佳实践 1. RDB持久化概述 1.1 什么是RDB RDB(Redis Database)是Redis的默认持久化方式,它在指定的时间间隔内生成数据集的快照(snapshot),并将快照保…

daily notes[44]

文章目录基础references基础 hello,world是几乎所有编程语言的第一例子,rust也不例外。但和其它语言不一样,Rust的源码最好拥有自己的项目目录。 $ mkdir ~/pro $ cd ~/pro $ mkdir helloWorld $ cd helloWorld源代码文件名为main.rs,内容如…

JavaScript对象创建方式完全指南:从原始到现代的演进之路

前言 作为一名前端开发者,JavaScript中对象创建是很重要。在JavaScript这门基于原型的语言中,对象几乎无处不在。今天,我将带领大家回顾JavaScript对象创建的7种方式,从最原始的字面量到现代的ES6 class,每一步演进都解…

基于单片机的无线水塔监控系统设计(论文+源码)

本设计为基于单片机的无线水塔监控系统设计,主要由以下几部分组成:均采用STC89C52RC单片机为主控;主机:NRF24L01无线通讯模块,1602LCD液晶显示屏。从机:NRF24L01无线通讯模块,水位传感器&#x…

凌晨0-3点不睡,你熬的不是夜,是人生!

“熬夜”这个词,早已成为现代生活的常态。有人为了工作加班到深夜,有人为了娱乐刷剧到天明,但你知道吗?熬夜最“要命”的时间段,其实是凌晨0点到凌晨3点。别以为只是少睡几个小时而已,这个时间段不睡&#…

大语言模型基石:Transformer

一、引言 如今火爆的 GPT、LLaMA、通义千问、ChatGLM 等大语言模型,背后都离不开一个核心架构——Transformer。 2017 年,Google 在论文《Attention Is All You Need》中首次提出 Transformer 模型,彻底改变了自然语言处理的发展方向。它摒…

【算法】【链表】160.相交链表--通俗讲解

算法通俗讲解推荐阅读 【算法–链表】83.删除排序链表中的重复元素–通俗讲解 【算法–链表】删除排序链表中的重复元素 II–通俗讲解 【算法–链表】86.分割链表–通俗讲解 【算法】92.翻转链表Ⅱ–通俗讲解 【算法–链表】109.有序链表转换二叉搜索树–通俗讲解 【算法–链表…

MySQL——库的操作

1、创建数据库语法:CREATE DATABASE [IF NOT EXISTS] db_name [create_specification [, create_specification] ...] create_specification: [DEFAULT] CHARACTER SET charset_name [DEFAULT] COLLATE collation_name这里的CHARACTER SET表示指定数据库采用的字符集…

Python ast模块(Abstract Syntax Trees,抽象语法树)介绍及使用

文章目录 核心概念 基本使用流程 常用节点类型 示例代码 实际应用场景 注意事项 `ast.literal_eval()` 功能说明 适用场景 使用示例 限制与安全特性 与 `eval()` 的对比 总结 Python 的 ast 模块( Abstract Syntax Trees,抽象语法树)允许你解析、分析和修改 Python 代码的…

C++宽度优先搜索算法:队列与优先级队列

本期我们就来深入学习一下C算法中一个很重要的算法思想:宽度优先搜索算法 宽度优先算法是一个应用十分广泛的算法思想,涉及的领域也十分繁多,因此本篇我们先只涉猎它的一部分算法题:队列/优先级队列,后续我们会进一步地…

类的property属性

​​Python 中的 property 特性详解​​property 是 Python 中用于​​将方法转换为属性​​的装饰器,它允许开发者以访问属性的方式调用方法,同时可以添加逻辑控制(如数据校验、计算属性等)。以下是其核心用法和优势:…

【Redis#9】其他数据结构

引言 Redis 除了我们最常用的 String、Hash、List、Set、ZSet(Sorted Set) 这五种基本数据结构外,还提供了很多高级或特殊用途的数据结构/类型 ,它们可以满足更复杂的业务需求。 ✅ Redis 的“五大基本数据结构”回顾类型特点Stri…

AutoGen——自定义Agent

目录引子自定义 AgentCountDownAgentArithmeticAgent在自定义 Agent 中使用自定义模型客户端让自定义 Agent 声明式化Selector Group Chat示例:网页搜索 / 数据分析代理(Agents)Workflow终止条件(Termination Conditions&#xff…

【重定向和转发的核心理解】

重定向和转发 不废话: “转发” 的核心定义: 服务端内部主导跳转、客户端无感知(仅 1 次请求)、浏览器 URL 不改变,与传统 Web 开发中 “转发” 的本质逻辑完全一致,只是实现载体(Nginx 路由层 …

生成对抗网络详解与实现

生成对抗网络详解与实现0. 前言1. GAN 原理2. GAN 架构3. 损失函数3.1 判别器损失3.2 生成器损失3.4 VANILLA GAN4. GAN 训练步骤0. 前言 生成对抗网络 (Generative Adversarial Network, GAN) 是图像和视频生成中的主要方法之一。在本节中,我们将了解 GAN 的架构、…

FPGA硬件开发-XPE工具的使用

目录 XPE 工具概述​ XPE 使用步骤详解​ 1. 工具获取与初始化​ 2. 器件选择与配置​ 3. 电源电压设置​ 4. 资源使用量配置​ 5. 时钟与开关活动配置​ 6. 功耗计算与报告生成​ 报告解读与电源设计优化​ 常见问题与最佳实践​ 与实际功耗的差异处理​ 工具版本…

CentOS 7.9 RAID 10 实验报告

文章目录CentOS 7.9 RAID 10 实验报告一、实验概述1.1 实验目的1.2 实验环境1.3 实验拓扑二、实验准备2.1 磁盘准备2.2 安装必要软件三、RAID 10阵列创建3.1 创建RAID 10阵列3.2 创建文件系统并挂载3.3 保存RAID配置四、性能基准测试4.1 初始性能测试4.2 创建测试数据集五、故障…