C编译器剖析_5.4.1 中间代码生成与优化_删除无用的临时变量和优化跳转目标

C编译器剖析_5.4.2 中间代码生成及优化_基本块的合并 5.4.2  基本块的合并     我们在第5.4.1节时给出了由基本块构成的双向链表和控制流图,为阅读方便,我们这里再次给出“图5.1.4 基本块的静态结构和动态结构”。在这一小节中,我们试图把双向链表中相邻的基本块进行合并,当然这种合并需要满足一定条件,同时要保持程序的原有语义。在合并后,控制流图中的前驱与后继关系也要进行调整。我们需要改动的数据结构有图5.4.1中的双向链表和控制流图。需要 阅读详情

5.4.1  删除无用的临时变量和优化跳转目标

    UCC编译器在优化方面做的工作不多,其中与优化有关的函数主要有以下几个:

(1)    Symbol  Simplify(Type ty, int opcode, Symbol src1,Symbol src2);

用于进行“代数恒等式”的简化,例如表达式“a<<0”可简化为a。

(2)    Symbol  TryAddValue(Type ty, int op, Symbol src1,Symbol src2);

用于处理“公共子表达式”。

(3)    void Optimize(FunctionSymbolfsym);

用于对“C源代码中的某一函数”进行优化。

     在前面的章节中,我们已经对函数Simplify和TryAddValue做过讨论,在这一小节中,我们主要讨论函数Optimize。我们在中间代码生成阶段,产生了许多临时变量,用来存放表达式的值,在优化时,如果发现有些临时变量的值并没有在其他地方被使用(即该临时变量的引用计数为1),则可删除该临时变量。如图5.4.1所示,对于第14至18行的C程序而言,优化前的中间代码在第24至28行,其中的临时变量t0至t4并没有在其他地方被使用,优化时,我们可删除t1、t2、t3和t4,第31和32行是优化后的中间代码。


图5.4.1 删除无用的临时变量

     我们注意到,图5.4.1第24行的t0在优化后仍然得到了保留,其原因在于函数GetData()的返回值为结构体对象。为第24行的中间代码“t0:GetData();”生成汇编代码时,UCC编译器将其视为“GetData(&t0);”,我们会在主调函数main的栈中为t0分配内存,然后把t0的首地址传递给被调函数GetData()。对UCC编译器而言,第5行的函数GetData的接口相当于是:

         void  GetData(Data * ptr);

     按照C的语义,函数的返回值可以是整数、浮点数和结构体对象,但不可以是数组。如果返回值是整数,这些值可以暂存于X86的通用寄存器eax和edx中;而如果返回值是浮点数,则可暂存于浮点协处理器X87的寄存器中;但对于图5.4.1第4行的结构体来说,其结构体对象要占16字节的空间,如果CPU中的寄存器无法存放结构体对象,UCC编译器在生成汇编代码时,就会隐式地按照GetData(&t0)来处理第31行的函数调用。C标准要为“在x86平台上,函数调用的返回值应如何传递等细节”进行统一约定,即“调用约定 Calling Convention”。在后续的“目标代码生成”章节中,我们会遇到用于为“函数调用生成汇编代码”的EmitCall(),到时我们再进一步展开讨论。图5.4.1第34至38行是与函数调用“(GetData(&t0));”对应的汇编代码。

     接下来,我们来看一下“删除无用临时变量”的函数EliminateCode,如图5.3.2第17至50行所示,第25至30行用于删除形如“t4:f();”中的无用临时变量t4,而第31至41行用于删除形如“t2:~a;”的无用临时变量t2,由于表达式“~a”没有副作用,不仅可删去临时变量t2,同时还可在第38至39行删去整条中间代码。而函数调用f()有副作用,我们就只能在第29行删去临时变量t4。


图5.4.2 Optimize()

     图5.4.2第1至16行的函数Optimize用于对C源代码中的某一函数进行优化,第4行调用的PeepHole会对基本块进行窥孔优化,我们已在前面的章节中介绍过这个函数,第8行调用的EliminateCode函数用于删除无用的临时变量,第9行调用的ExamineJump函数用于优化跳转语句的跳转目标,而第14行调用的TryMergeBBlock则尝试进行相邻基本块之间的合并。

     图5.4.3给出了函数ExamineJump的代码,当我们面对形如第2至10行的中间代码时,第4行的跳转语句是基本块BB的最末一条指令,其跳转目标为BB1,而基本块BB1又只有一条跳往BB2的无条件跳转指令,此时我们可把基本块BB最末一条指令的跳转目标优化为BB2,如第14行所示。图5.4.3第26行用于获取基本块BB的最后一条指令,当该指令不是跳转指令时,我们从第29行直接返回。对基本块BB而言,当我们将第4行的跳转目标BB1改为第14行的BB2后,基本块BB、BB1和BB2之间的“前驱与后继的关系”发生了变化,我们要从BB基本块的后继链表中找到BB1,将该链表元素改为BB2,第31至37行的do语句用于在后继链表bb->succs中查找BB1,之后在第42行将其改为BB2,这样BB2就成了BB基本块的后继结点。而第43行的代码用于从BB1基本块的前驱链表中删去BB基本块,第44行用于在基本块BB2的前驱链表中添加基本块BB。第47行实现了将BB基本块最末一条指令的跳转目标从BB1改为BB2的优化操作。


图5.4.3 ExamineJump()

     在下一小节中,我们会对图5.4.2第14行调用的TryMergeBBlock函数进行分析,该函数用于基本块的合并。

C++编译器机器代码生成原理与性能优化实战指南 编译器作为连接高级语言与底层硬件的桥梁,其核心任务是将抽象的程序逻辑转换为机器可执行的指令序列。这一过程涉及指令选择、寄存器分配、指令调度等关键技术,直接决定了生成代码的执行效率与资源占用。理解代码生成原理对于性能调优至关重要,它使开发者能够从硬件视角审视程序,编写出更利于编译器优化的代码。在C++高性能计算、游戏引擎及嵌入式开发等场景中,掌握寄存器分配策略与指令选择机制,能有效避免性能瓶颈,实现从软件逻辑到硬件执行的高效映射。本文聚焦机器代码生成这一编译后端核心环节,深入解析其工作流程与优化实践。 阅读详情

相关推荐

从零解析C语言编译器:源码结构与核心模块实现详解

编译器是连接高级编程语言与计算机硬件的关键系统软件,其核心原理是将源代码转换为机器可执行的目标代码。这一过程通常遵循经典的编译流程,包括词法分析、语法分析、语义分析、中间代码生成、优化和目标代码生成等阶段。理解编译器的实现,对于深入掌握程序运行机制、提升系统编程能力以及参与大型编译器项目开发具有重要价值。在工程实践中,通过剖析一个中等规模、功能完整的教学型编译器源码,可以将抽象的编译原理概念(如有限自动机、递归下降解析、符号表管理)与具体的C语言代码实现一一对应。本文聚焦于一个用C语言实现的简单C编译器项目

weixin_33811539的博客 403

编译原理(十六)——中间代码优化(1)

一、代码优化的阶段 欲提高源程序的运行速度,需要经过几个阶段的优化: 用户对源程序进行优化(和编译器无关,与coder设计的算法有关) 编译器前端对中间代码进行优化 编译器后端对目标代码进行优化 两个编译器必须等价,编译的结果必须是正确的,即使有99.99%的可能性是不正确的,但是效率很好也不行,正确性是根本。 二、中间代码优化的分类 从优化的种类来看,中间代码的优化可有如下分类: 局部优化 循环上的优化(所有优化方法效果最好的) 1. 循环不变体外提 循环中不因循环而改变的部分可以提到循环的

很注重数学和408 7727

深入解析ANSI-C编译器:嵌入式开发中的类型系统、优化策略与混合编程实践

编译器作为软件开发的基石,其核心作用是将高级语言代码转换为机器可执行的指令。其工作原理涉及词法分析、语法分析、语义分析、优化和代码生成等多个阶段。理解编译器的内部机制对于编写高效、可靠的代码至关重要,尤其在资源受限的嵌入式系统开发中,它能帮助开发者充分利用硬件性能,避免潜在陷阱。在嵌入式应用场景下,开发者经常需要与底层硬件直接交互,并应对严格的内存和实时性约束。此时,深入掌握ANSI-C编译器的类型系统实现(如整型提升和算术转换)、代码优化策略(如窥孔优化和强度削减)以及C与汇编的混合编程技巧,成为提升代码

weixin_30279671的博客 430

c语言如何消除变量,如何删除C中的变量

有3种变量.根据您管理内存的种类不同.全局变量它们位于程序的特殊部分.它们在程序启动时出现,在程序结束时消失.你无法做任何事情来回收全局变量占用的内存.一些更复杂的常量也可能属于该类别.您的字符串文字“我不希望这个被清除,但完全消失”很可能会驻留在那里,无论您是否将其复制到i_want_to_delete_this变量.堆栈变量局部变量和函数参数.它们出现在您的代码中.输入该变量的范围时会分配内存...

weixin_39866774的博客 3890

GCC扩展与编译器优化实战:从分支预测到中间表示

编译器优化是提升程序性能的核心技术,其原理在于将高级语言代码高效转换为底层机器指令。通过分析程序的中间表示(IR),编译器应用多种算法进行优化,如死代码消除和公共子表达式消除,从而减少冗余计算、改善指令流水线效率。这项技术的价值在于,它能在不改变源代码逻辑的前提下,显著提升程序的执行速度和资源利用率,尤其适用于嵌入式系统、游戏引擎和高性能计算等对性能有极致要求的领域。在实际应用中,开发者可以借助编译器的扩展功能,例如GCC的`__builtin_expect`来引导分支预测,或通过预编译头文件加速大型项目的

405

DSP56800E C编译器内存对齐与优化实战指南

在嵌入式系统开发中,数据对齐是编译器将数据放置在内存地址上的基础规则,其核心原理是匹配处理器的内存访问粒度,以实现单周期高效访问。理解并应用对齐规则能避免性能陷阱和硬件异常,对于实时性要求高的数字信号处理(DSP)应用尤为重要。在DSP56800E这类经典处理器上,其独特的哈佛架构和字节/字混合寻址模式,使得内存模型的选择与数据布局成为影响代码效率的关键技术。通过合理配置小内存模型或大内存模型,并利用编译器提供的寄存器着色、窥孔优化等高级优化技术,开发者可以显著提升代码执行速度并减少内存占用。这些优化实践广

309

嵌入式C语言优化小技巧

嵌入式C语言优化技巧1.引言嵌入式系统是指完成一种或几种特定功能的计算机系统,具有自动化程度高、响应速度快等优点, 目前已广泛应用于消费电子、工业控制等领域。嵌入式系统受其使用的硬件以及运行环境的限制, 非常注重代码的时空效率, 因此选择一种合适的开发语言十分重要。目前, 在嵌入式系统开发中可使用的语言很多,其中 C语言应用得最广泛。虽然用 C 语言编程具有许多优点,但基于嵌入式系统的 C语言和标

ce123的技术博客 8300

C++性能优化的双刃剑:从编译器优化到工程权衡的实战指南

在软件工程领域,性能优化是提升系统效率的核心技术手段,其本质是通过算法改进、资源管理和代码重构来降低延迟、提高吞吐量。从原理层面看,优化涉及编译器自动化处理与手动调优的结合,现代编译器如GCC和Clang提供了丰富的优化选项,例如内联展开、循环向量化和链接时优化(LTO),这些技术能显著提升执行效率。其技术价值体现在用户体验改善、硬件成本降低及系统瓶颈突破,尤其在实时计算和高频交易等场景中至关重要。然而,优化也需权衡代码可维护性与性能收益,避免陷入过早优化或引入隐蔽缺陷的陷阱。本文聚焦C++性能优化的实践,

dieqiao8331的博客 347

使用临时变量提高Java代码性能 (转)

一个Java类中可以存在多种形式的变量,可以是最一般的成员变量;或将其定义为静态变量;也可以在方法中定义临时变量。这三种变量的存取效率如何?1.测试下面作个实验,看下面代码。class CheckVars {    privateintinstVar;// 成员变量    privatestaticintstaticVar; // 静态变量    

WJ S的专栏 1827

keil编写程序,同一个代码跳转的地址不一样,如何解决?

🏆 本文收录于《全栈Bug调优(实战版)》专栏,致力于分享我在项目实战过程中遇到的各类Bug及其原因,并提供切实有效的解决方案。无论你是初学者还是经验丰富的开发者,本文将为你指引出一条更高效的Bug修复之路,助你早日登顶,迈向财富自由的梦想🚀!同时,欢迎大家关注、收藏、订阅本专栏,更多精彩内容正在持续更新中。让我们一起进步,Up!Up!Up!    备注: 部分问题/难题源自互联网,经过精心筛选和整理,结合数位十多年大厂实战经验资深大佬经验总结所得,数条可行方案供所需之人参考。

**My Coding Family** 1404

【信息科学与工程学】计算机科学与自动化——第二十四篇 编译器04 LLVM编译器02

B-WUCL(用户权重均匀对比损失)和D-WUCL(物品权重均匀对比损失)中,动态调整参数λ_new(新用户补偿因子)和α(长尾补偿强度)是优化推荐系统适应数据分布变化的核心。

weixin_49199313的博客 1242

LLVM理论篇之编译器中端

在-O3的基础上添加-fno-signed-zeros、-freciprocal-math、-ffp-contract=fast、-menable-unsafe-fp-math、-menable-no-nans、-menable-no-infs、-mreassociate、-fno-trapping-math、-ffast-math、-ffinite-math-only等。(2)函数(Function)就是编译语言中的函数,包括函数签名和若干个基本块,函数内的第一个本块叫入口基本块(entry)。

panpan_jiang1的博客 2034

一代测序、二代测序以及三代测序的优缺点及应用对比?

一、初现庐山真面目 一代测序:又称Sanger测序(多分子,单克隆) 历史:第一代DNA测序技术(又称Sanger测序)在1975年,由Sanger等人开创,并在1977年完成第一个基因组序列(噬菌体X174),全长5375个碱基。研究人员经过30年的实践并对技术及测序策略的不断改进(如使用了不同策略的作图法、鸟枪法),2001年完成的首个人类基因组图谱就是以改进了的Sanger法为其测序基础。 原理:在4个DNA合成反应体系(含dNTP)中分别加入一定比例带有标记的ddNTP(分为:ddATP...

wangprince2017 3万+

二.编译器和代码优化技术

编译器是将高级编程语言(如C、C++、Java、Python)编写的源代码,转化为计算机可直接执行的机器语言(或字节码、中间代码)的程序,核心价值是“翻译+优化”,让代码能高效在硬件上运行。词法分析:拆分源代码为最小语法单元(如关键字、变量名、运算符,称为“Token”),比如把int a=1+2;拆为inta12;。语法分析:基于Token构建抽象语法树(AST),校验语法合法性(如是否漏写分号、括号不匹配),AST是源代码的结构化表示,便于后续处理。语义分析。

cscs2020ll的博客 855

基于STM32的简易电压、频率、占空比测量仪

任务详解:我们需要实现三个功能: 第一个是测频率的功能,输入信号为频率为1kHz~10kHz的三种波形(方波、三角波、正弦波),这也是我们最常见的三种波形。幅度是不需要测量的,所以先不关心幅度。 第二个是电压报警功能,当输入信号的幅度超过15V时,我们需要报警(这里我用了一个红色LED当报警器,有条件的还可以加个蜂鸣器)并且把此时的信号幅度显示在OLED屏幕上。 第三个是通信方式的要求,需要有串口通信功能。 第四个是计算占空比的任务,这个任务的输入是幅度为1V~10V、频率为20Hz ~1kHz的方波,需要

weixin_43469377的博客 7251

用Docker-Compose三节点部署生产级Doris集群:从配置文件到故障转移

本文详细介绍了如何使用Docker-Compose部署一个生产级的三节点Apache Doris集群。内容涵盖从核心架构理解、配置文件编写到集群初始化验证的全过程,并重点讲解了如何实现高可用、数据持久化、健康检查以及故障转移等生产级运维关键点,为中小团队提供了一种在容器化便利性与系统稳定性间取得平衡的可靠部署方案。

kkk56的博客 856

《编译器设计》第九章——优化简介

代码优化的目标是在编译时发现有关程序运行时行为的信息,并利用该信息来改进编译器生成的代码。改进可能有许多种形式。优化最常见的目标是提高编译后代码的运行速度。但对于某些应用程序来说,编译后代码的长度要比其执行速度更重要。例如,考虑某个将烧录到只读存储器的应用程序,其代码长度会影响整个系统的成本。优化的其他目标包括降低执行的能耗、提高代码对实时事件的响应、降低对内存的总访问量、优化寄存器的使用等。有两个关于优化的比较经典的例子,后面补上。

yelvens的博客 7913

全网最全的AItium Designer 16下载资源与安装步骤

Altium Designer 除了全面继承包括Protel 99SE、Protel DXP在内的先前一系列版本的功能和优点外,还增加了许多改进和很多高端功能。该平台拓宽了板级设计的传统界面,全面集成了FPGA设计功能和SOPC设计实现功能,从而允许工程设计人员能将系统设计中的FPGA与PCB设计及嵌入式设计集成在一起。...

WQ的博客 1万+

基于Rust与多智能体架构的C语言编译器设计与实现

编译器作为将高级语言转换为机器可执行代码的核心系统软件,其设计通常遵循词法分析、语法分析、语义分析、中间代码生成与优化、目标代码生成等经典阶段。传统编译器多为单线程顺序执行,而现代软件架构正朝着模块化、并发化方向发展。多智能体系统通过将复杂任务分解为多个可并行、可通信的专长模块,为系统设计提供了新的范式。结合Rust语言的内存安全、零成本抽象和高并发特性,能够高效构建高可靠性的系统级软件。本文聚焦于将多智能体协同架构应用于编译器构建这一具体场景,详细阐述了如何利用Rust的异步编程模型(如tokio运行时与

weixin_33778544的博客 496

使用GBIF获取生物多样性数据与GIS的结合

随着全球生物多样性的重要性日益凸显,越来越多的研究人员和保护机构开始利用地理信息系统(GIS)来分析和可视化生物多样性数据。GBIF(全球生物多样性信息设施)作为一个全球性的生物多样性数据库,提供了大量的物种分布和分类信息,为研究人员和保护工作者提供了宝贵的数据资源。本文将介绍如何使用GBIF API获取生物多样性数据,并将其与GIS技术相结合,以便更好地理解和研究生物多样性。利用GIS的强大功能,我们可以在地理空间上可视化物种分布、分析物种多样性的空间格局,并为保护生物多样性提供科学依据。

BnxBackend的博客 1762

【C2000系列DSP的Bootloader详解】实现过程、流程图与示例代码

C2000 Bootloader 的核心逻辑可概括为 “初始化→选模式→加载程序→校验→跳转” 五步。对于小白来说,无需纠结底层寄存器配置(驱动库已封装),重点理解 “启动流程” 和 “数据传输 + 校验” 的核心思想即可。 通过本文的流程图和示例代码,相信你已经掌握了 C2000 Bootloader 的核心原理。实际开发中,可基于 TI 提供的 Bootloader 示例(C2000Ware 中自带)进行修改,快速适配自己的硬件和应用场景。

昔时扬尘处 368

编译器优化陷阱频发?现代C++性能剖析的3大隐秘雷区,你踩中了吗?

掌握现代C++性能调优关键方法,避开编译器优化陷阱。结合2025全球C++及系统软件技术大会:现代C++的性能剖析实践,解析高频场景下的三大隐秘雷区,涵盖代码实例与实测数据,提升系统级软件效率,值得收藏。

ProceShoal的博客 632

8.19华为OD机试真题 新系统 - 基站低能耗时段统计 (Java/Py/C/C++/Js/Go)

8.19华为OD机试真题 新系统 - 基站低能耗时段统计 (Java/Py/C/C++/Js/Go)

算法大师! 1141

C++性能优化实战:从编译器配置到内存访问与多线程优化

在计算机编程中,性能优化是提升软件执行效率的核心技术,其原理在于通过减少计算资源消耗和改善硬件利用率来最大化程序运行速度。这项技术的价值不仅体现在提升用户体验和系统响应能力上,更是高并发、实时计算等场景的关键保障。应用场景广泛覆盖游戏开发、高频交易、嵌入式系统和科学计算等领域。本文聚焦C++语言,深入探讨编译器优化等级配置、链接时优化(LTO)与按配置优化(PGO)等关键技术,并结合内存访问模式优化与多线程并发处理,提供一套从系统配置到代码实践的完整性能提升方案。

weixin_30325971的博客 851

【Bug已解决】Claude Code Auto-update failed 自动更新失败的解决方案

方案适用场景推荐指数清理残留重新安装已经损坏,需要快速恢复可用⭐⭐⭐⭐⭐修复目录权限权限不足导致更新反复失败⭐⭐⭐⭐⭐关闭自动更新网络/安全软件环境不稳定⭐⭐⭐切换原生安装方式想从根源规避 npm 更新问题⭐⭐⭐⭐自检修复脚本团队/服务器环境需要自动化恢复⭐⭐⭐报错的本质是自动更新过程中的文件替换操作因权限不足或被中断而处于不完整状态,而不是 Claude Code 本身的功能缺陷。遇到问题先清理残留文件再重新安装,不要在损坏状态上反复尝试修复;

一键难忘的博客 1万+

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

ARM Compiler 5.06代码生成机制:系统学习底层实现原理

剖析ARM Compiler 5.06的底层代码生成原理,揭示编译器如何将高级语言转化为高效机器指令,掌握arm compiler 5.06在嵌入式系统中的优化策略与实现细节。

weixin_33481022的博客 927

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

上一篇: C编译器剖析_5.3.2 中间代码生成及优化_switch语句的翻译
下一篇: C编译器剖析_5.4.2 中间代码生成及优化_基本块的合并
SheIsC
博客等级 码龄12年 224粉丝 64原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值