《Engineering a Complier》学习笔记(六)
过程抽象(Procedure Abstract)
- Algol语言:ALGOL是算法语言(ALGOrithmic Language)的简称,是在计算机发展史上首批清晰定义的高级语言,由欧美计算机学家合力所组成的联席大会于仍是晶体管计算机流行的1950年代所开发。国际计算机学会(ACM)将ALGOL模式列为算法描述的标准,启发ALGOL类现代语言Pascal、Ada、C语言等出现。
- 类Algol语言:又称ALL
一.概述
过程抽象建立了一个受控的执行环境,每个过程都具有私有的命名化存储空间。
过程帮助定义了系统组件的借口,跨组件间的交互以过程调用的形式建立。
调用者(Caller)和被调用者(Callee)
链接约定(Linkage Convention):编译器和操作系统的约定,定义了调用过程和函数需要采取的操作
二.过程调用
在(ALL)中,过程调用将控制从调用者中的调用位置被转移到被调用者的开始处。在从被调用者退出时,控制返回到调用者中紧接着调用位置后的下一个位置。
调用图:给出各个过程间可能发生的潜在调用的集合。
示例:Pascal语言中的调用图和执行历史。
发散(Diverge):程序不能正常终止
闭包:一个过程定义了自由变量运行时的上下文。
类ALGOL语言的调用和返回可以用栈模拟。
1. 面向对象语言中的控制流
2. 更复杂的控制流
三.命名空间
1.作用域
类ALGOL语言中,命名空间=作用域
词法作用域:按程序中出现的顺序嵌套的作用域
Pascal的词法作用域(lexical scoping):在一个给定的作用域,每个名字引用在词法上与之最接近的声明。
静态坐标:对于作用域s中声明的名字x,静态坐标是一个对<l,o>,l是s的词法嵌套层次,o是x在作用域数据区中的偏移量。
Pascal代码以及嵌套、调用关系。
不同语言的作用域规则,书中介绍了FORTRAN,C,Scheme。
2.类ALGOL语言运行时的结构
活动记录(Activation Record, AR):转换过程调用和作用域化的命名空间的两个“孪生”抽象,是一个分配的内存区,用于保存于单个过程的单次激活实例相关联的控制信息和数据存储。
活动记录指针:为定位当前AR,编译器需要设法在一个指定的寄存器中维持一个指向AR的指针。
一个典型的AR:
1. 局部储存
- 为局部数据分配空间
- 初始化变量
- 用于保存寄存器值的空间
2.分配活动记录
分配方式:
- AR的栈分配方式
- AR的堆分配方式
- 活动记录的静态分配
叶过程(Leaf Procedure):不包含过程调用的过程
3.OOP语言的命名空间
OOL: Objected-Oriented Language 从编译器的角度来看,ools重新组织了程序的名称空间。他们用第二套命名约定来扩充这个经典的命名方案,一套命名约定是围绕数据的布局组织的——特别是对象的定义。这种以数据为中心的命名规则导致了第二种作用域层次结构和第二种解析名称的机制——即将源语言名称映射到运行时地址,以便编译后的代码可以访问与该名称关联的数据。
即时编译器(Just-In-Time Complier)指在运行时编译的编译器
面向对象语言的几个特性:
1. 对象 对象是具有一个或多个成员的抽象。这些成员可以是数据项、操作这些数据项的代码或其他对象。具有代码成员的对象是类。每个对象都有内部状态数据,其生存期与对象的生存期匹配。
2. 类 是具有相同抽象结构和特征的对象的集合。类在类的每个实例中定义一组数据成员,并定义该类本地的代码成员(方法)。一些方法是公共的,或者在外部可见的,其他方法是私有的,或者在类外部不可见的。
3.继承 指的是类之间的关系,它在类的名称范围上定义了部分顺序。每个类都可以有一个超类,它可以从超类继承代码和数据成员。如果a是b的超类,那么b就是a的子类。有些语言允许一个类有多个超类。
4. 接收器 方法是相对于某个对象调用的,该对象称为方法的接收器。接收方在方法内部有一个指定的名称,例如this或self。
对OOL命名空间的结构分析,下图比较直观,图中的几个类包含了继承的关系:
上图中,ColorPoint继承了Point,而SimplePoint是Point的实例化对象,LeftCorner, RightCorner则是ColorPoint的实例化对象
关于静态与动态类型,多继承的内容书中有详细介绍,不在此描述
四.过程间值的传递
1.参数传递
1.1.按值传递
考虑C语言示例:
在c语言中,通过按值调用的参数传递,调用者将实际参数的值复制到对应形式参数的适当位置——被调用者ar中的寄存器或参数槽。只有一个名称引用该值——形式参数的名称。它的值是一个初始条件,由调用时对实际参数求值确定。如果被调用方更改了其值,则该更改在被调用方内部可见,而在调用方中则不可见。
2.按引用传递
按引用调用与按值调用在两个关键方面有所不同。首先,引用形式参数的任何重新定义都反映在相应的实际参数中。第二,任何引用形式参数都可以绑定到一个变量,该变量可以通过被调用方内部的另一个名称访问。当发生这种情况时,我们称名称为别名,因为它们引用相同的存储位置。
上例的程序假设以引用传递:
2.返回值
对于按值调用参数,链接约定通常将为第一个参数保留的寄存器指定为保存返回值的寄存器。要从函数返回值,编译器必须为返回值留出空间。因为根据定义,返回值是在被调用方终止后使用的,所以它需要存储在被调用方的ar之外。如果编译器的编写者能够确保返回值的大小是固定的,那么它可以存储在调用方的ar或指定的寄存器中。
3.构建可寻址性
Data Area: 内存中保存特定作用域数据的区域称为其数据区域。
Base Address: 数据区开始的地址通常称为基址。
编译器通常安排全局数据区域和静态数据区域具有静态基地址。为这样的变量生成地址的策略很简单:将数据区域的基址计算到寄存器中,并将其偏移量添加到基址中。编译器的ir通常会包括地址模式来表示这个计算;例如,在iloc中,loadAI表示“寄存器+立即偏移”模式,而loadAO表示“寄存器+寄存器”模式。
若要生成静态基地址的运行时地址,编译器将向数据区域附加一个符号、程序集级标签。根据目标机器的指令集,该标签可能用于立即加载操作,也可能用于初始化已知位置,在这种情况下,可以通过标准的加载操作将其移动到寄存器中。
4.标准链接
过程链接:是编译器、操作系统和目标机器间的协议。其详细地划分了命名、资源分配、地址分配以及保护之间的责任界限。过程链接确保了由编译器翻译的用户代码与来自其他来源的代码(包括系统库、应用程序库和用其他编程语言编写的代码)之间过程的互操作性。通常,目标机器和操作系统的给定组合的所有编译器都尽可能使用相同的链接。
过程链接在机器中是独立的,调用者和被调用者的链接过程包含了4个序列:
- Precall Sequence:开始构建被召唤者环境的过程。它计算实际参数,确定返回地址,并在必要时确定保留用于保存返回值的空间的地址。
- Postreturn Sequence: 撤销预调用序列的操作。它必须恢复所有需要返回到寄存器的引用调用和值结果调用参数。它从寄存器保存区恢复任何调用者保存的寄存器。它可能需要解除被调用者的全部或部分ar。
- Prologue Sequence: 完成创建被调用方运行时环境的任务。它可以在被调用者的ar中创建空间来存储由调用者在寄存器中传递的一些值。它必须为局部变量创建空间,并根据需要对它们进行初始化。
- Epilogue Sequence: 分解被调用者的环境和重构调用者的环境的过程。它可能参与释放被调用者的ar。如果过程返回一个值,epilogue可能负责将该值存储到调用者指定的地址中。(另外,为return语句生成的代码也可以执行此任务。)最后,它恢复调用者的arp并跳转到返回地址。
在一个典型的系统中,在系统开发的早期阶段,编译器实现者和操作系统实现者之间就连接约定进行协商。因此,诸如caller-保存寄存器和callee-保存寄存器之间的区别是在设计时决定的。当编译器运行时,它必须为每个过程发出Precedure Sequence和epilogue Sequence,以及每个调用站点的precall和postreturn sequence。此代码在运行时执行。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具