“块参数与 φ 的对应需要连同边一起给出:$B(x)$ 有前驱边 $P i\to B(a i)$,等价于在 $B$ 入口写 $x:=\phi(P i:a i) i$。这个改写是表示层等价,不表…”
“在约定的三地址码指令集上,静态单赋值形式(static single assignment, SSA)要求每个标量名字恰有一个静态定义,并且该定义支配它的每个普通使用。对控制流图中的使用点…”
Three-address code · TAC · 三地址中间码
把复杂表达式拆为至多两个输入和一个结果的简单指令,并以显式跳转组织控制流的中间表示。
三地址码(three-address code, TAC)是一类编译器中间表示。其核心指令通常形如
每条计算指令至多显式写一个结果位置和两个输入位置,“地址”可以是变量、临时量、常量或抽象存储位置,并不要求是真实机器地址。调用、数组寻址、字段访问和返回常以扩展指令存在,但仍把嵌套计算拆成可命名步骤。
标签和跳转把线性指令序列分割成基本块;块内除末尾外顺序执行,块末产生控制流图边。TAC 的语义必须说明操作数求值、整数宽度、异常、内存与调用效果。指令“
实现可把同一 TAC 存成 quadruple
三地址码把一口气完成的表达式拆成流水线上的小工序。源式
中间结果有名字后,公共子表达式消除、常量传播和活跃性分析可以直接谈论哪次定义供哪次使用。复杂控制结构也被还原为标签和跳转,使路径而非语法缩进成为分析对象。
这种拆分没有规定每个源变量只能赋值一次。临时量可以复用,循环计数器也可反复更新;因此 TAC 不等于静态单赋值形式。SSA 常建立在类似 TAC 的指令集之上,再通过版本化名字与合流操作施加额外不变量。把二者混称会掩盖 SSA 构造和消解真正完成的工作。
对 “x:=f(a)+g(b*c)” 且求值顺序固定为从左到右,可得
若
“三地址”也不是严格要求每条指令文本出现三个词。一元负号只有一个输入,复制没有运算,条件跳转可能同时写比较两数。真正约束是把复杂运算分解为有限元的简单原语。相反,一条 “x:=sort(a)” 虽表面只有两个名字,却把任意复杂计算藏进原语;它是否属于所讨论 TAC 取决于指令集规范,而不是计数字符串中的操作数。
内存操作尤其需要分开。
TAC 的细粒度让局部优化能以短窗口描述。若连续出现
后端可把一条 TAC 指令扩成多条机器指令,也可把数条融合成一个寻址模式。这里没有一对一对应:目标机器可能只允许寄存器—寄存器运算,常量范围也有限。TAC 提供的是清楚的中间语义接口;指令选择仍需单独证明扩展或融合保持相同观察。
过程间 TAC 还必须决定调用如何表示。若 call 只写一个返回临时量,却省略可能读取和写入的内存、异常后继与被破坏状态,局部公共子表达式消除可能错误跨过调用复用旧 load。显式 effect token、调用摘要或保守 clobber 都可解决接口问题;单纯增加第四个“地址”字段并不能。