Back

生成表达式代码的 SDD

产生式语义规则Sid=E;S.code=E.codegen(top.get(id.lexeme)=E.addr)EE1+E2E.addr=new Temp()E.code=E1.codeE2.codegen(E.addr=E1.addr+E2.addr)E E1E.addr=new Temp()E.code=E1.codegen(E.addr=“minus”E1.addr)E (E1)E.addr=E1.addrE.code=E1.codeE idE.addr=top.get(id.lexeme)E.code=“”\begin{array}{|ll|} \hline \text{产生式} & \text{语义规则} \\ \hline S \to \text{id} = E ; & S.\text{code} = E.\text{code} \parallel \\ & \quad \text{gen}(\text{top.get(id.lexeme)} = E.\text{addr}) \\ \hline E \to E_1 + E_2 & E.\text{addr} = \text{new Temp()} \\ & E.\text{code} = E_1.\text{code} \parallel E_2.\text{code} \parallel \\ & \quad \text{gen}(E.\text{addr} = E_1.\text{addr} + E_2.\text{addr}) \\ \hline \phantom{E \to \ }| - E_1 & E.\text{addr} = \text{new Temp()} \\ & E.\text{code} = E_1.\text{code} \parallel \\ & \quad \text{gen}(E.\text{addr} = \text{“minus”} E_1.\text{addr}) \\ \hline \phantom{E \to \ }| ( E_1 ) & E.\text{addr} = E_1.\text{addr} \\ & E.\text{code} = E_1.\text{code} \\ \hline \phantom{E \to \ }| \text{id} & E.\text{addr} = \text{top.get(id.lexeme)} \\ & E.\text{code} = \text{“”} \\ \hline \end{array}

其中:

  • 综合属性 code\text{code} 表示代码
  • addr\text{addr} 表示存放表达式结果的地址(临时变量)
  • top.get()\text{top.get}(\cdots) 从栈顶符号表(符号是嵌套的,可以实现为栈)开始,逐个向下寻找 id\text{id} 的信息
  • newTemp()\text{new} \, \text{Temp}() 可以生成一个临时变量
  • gen()\text{gen}(\cdots) 生成相应代码

这里实际上是一个增量式翻译,即要运算得到 c = a + b,先翻译出生成 ab 的代码,再翻译生成 c 的代码。

数组元素

数组元素的寻址

一维数组的寻址

假设数组元素被存放在连续的存储空间中,元素从 0 到 n1n-1 编号,第 ii 个元素的地址为:

base+iw\text{base} + i \cdot w

其中:

  • base\text{base} 为数据 AA 的内存块的起始地址,即 A[0]A[0] 的相对地址
  • ww 为每个数组元素的宽度
  • base\text{base}, ww, nn 的值都可以从符号表中找到

k 维数组的寻址

假设数组按行存放,即首先存放 A[0][i2]...[ik]A[0][i_2]...[i_k],然后存放 A[1][i2]...[ik]A[1][i_2]...[i_k], …

njn_j 为第 jj 维的维数,wjw_j 为第 jj 维的每个子数组元素的宽度,wk=ww_k = w 为单个元素的宽度:

wk1=nkwk=nkwwk2=nk1wk1=nk1nkw\begin{aligned} w_{k-1} &= n_k \cdot w_k = n_k \cdot w \\ w_{k-2} &= n_{k-1} \cdot w_{k-1} = n_{k-1} \cdot n_k \cdot w \end{aligned}

多维数组 A[i1][i2]...[ik]A[i_1][i_2]...[i_k] 的地址为:

base+i1w1+i2w2+...+ikwk\text{base} + i_1 \cdot w_1 + i_2 \cdot w_2 + ... + i_k \cdot w_k

或者:

base+(((...((i1n2+i2)n3+i3)...)nk)+ik)w\text{base} + (((...((i_1 \cdot n_2 + i_2) \cdot n_3 + i_3)...) \cdot n_k) + i_k) \cdot w

多维数组的存放方法

  • 行优先(一般选择)
  • 列优先

a[i]a[i] 的地址:

base+(ilow)w=baseloww+iw\text{base} + (i - \text{low}) \cdot w = \text{base} - \text{low} \cdot w + i \cdot w

注意,这里 low\text{low} 是下界,其不一定为 0。

包含数组元素的表达式文法

添加新的文法产生式

  1. 数组元素 L:LL[E]  id[E]L: L \rightarrow L[E] \ | \ id[E]
  2. 以数组元素为左部的赋值 SL=ES \rightarrow L = E
  3. 数组元素作为表达式中的因子 ELE \rightarrow L

翻译方案

  1. 计算偏移量:对 LL 的代码计算偏移量,将结果存于 L.addrL.\text{addr} 所指的临时变量中。

  2. 综合属性 array\text{array}:记录相应数组的信息:元素类型,基地址等。

  3. 数组元素作为因子

    • LL 的代码只计算了偏移量

    • 数组元素的存放地址应该根据偏移量进一步计算,即 LL 的数组基地址加上偏移量

    • 使用三地址指令 x=a[i]x = a[i]

  4. 数组元素作为赋值左部

    • 使用三地址指令 a[i]=xa[i] = x
产生式语义规则Lid[E]L.array=top.get(id.lexeme)L.type=L.array.type.elemL.addr=new Temp()gen(L.addr=E.addrL.type.width)L L1[E]L.array=L1.arrayL.type=L1.type.elemt=new Temp()L.addr=new Temp()gen(t=E.addrL.type.width)gen(L.addr=L1.addr+t)EE1+E2E.addr=new Temp()gen(E.addr=E1.addr+E2.addr)E idE.addr=top.get(id.lexeme)E LE.addr=new Temp()gen(E.addr=L.array.base[L.addr])Sid=E;gen(top.get(id.lexeme)=E.addr)S L=E;gen(L.array.base[L.addr]=E.addr)\begin{array}{|ll|} \hline \text{产生式} & \text{语义规则} \\ \hline L \to \text{id} [ E ] & L.\text{array} = \text{top.get(id.lexeme)} \\ & L.\text{type} = L.\text{array.type.elem} \\ & L.\text{addr} = \text{new Temp()} \\ & \text{gen}(L.\text{addr} = E.\text{addr} * L.\text{type.width}) \\ \hline \phantom{L \to \ }| L_1 [ E ] & L.\text{array} = L_1.\text{array} \\ & L.\text{type} = L_1.\text{type.elem} \\ & t = \text{new Temp()} \\ & L.\text{addr} = \text{new Temp()} \\ & \text{gen}(t = E.\text{addr} * L.\text{type.width}) \\ & \text{gen}(L.\text{addr} = L_1.\text{addr} + t) \\ \hline E \to E_1 + E_2 & E.\text{addr} = \text{new Temp()} \\ & \text{gen}(E.\text{addr} = E_1.\text{addr} + E_2.\text{addr}) \\ \hline \phantom{E \to \ }| \text{id} & E.\text{addr} = \text{top.get(id.lexeme)} \\ \hline \phantom{E \to \ }| L & E.\text{addr} = \text{new Temp()} \\ & \text{gen}(E.\text{addr} = L.\text{array.base} [ L.\text{addr} ]) \\ \hline S \to \text{id} = E ; & \text{gen}(\text{top.get(id.lexeme)} = E.\text{addr}) \\ \hline \phantom{S \to \ }| L = E ; & \text{gen}(L.\text{array.base} [ L.\text{addr} ] = E.\text{addr}) \\ \hline \end{array}

注意:

  • 这里不是在算数组的类型大小,而是在算一个数组表达式的相对于基地址的偏移量。
  • addr\text{addr} 是偏移量这一计算结果的存放地址,而不是数组的基地址,基地址应当在 array\text{array} 属性里面。
  • 建议结合例子观察一下 type\text{type} 的解码顺序
  • 这里省略了一些 gen\text{gen} 的引号,请勿认为它完成了计算,这只是生成了计算的代码。

例子

56189

类型检查和转换

类型系统 (type system)

  • 给每一个组成部分赋予一个类型表达式
  • 通过一组逻辑规则来表示这些类型表达式必须满足的条件

设计类型系统的根本目的是用静态检查的方式来保证合法程序运行时的良行为。

类型检查规则

类型综合:根据子表达式的类型构造出表达式的类型

例如:

  • 如果 ff 的类型为 sts \rightarrow txx 的类型为 ss
  • 那么 f(x)f(x) 的类型为 tt

类型推导:根据语言结构的使用方式来确定该结构的类型

例如:

  • 如果 f(x)f(x) 是一个表达式,ff 的类型为 αβ\alpha \rightarrow \beta,且 xx 的类型为 α\alpha
  • 那么 f(x)f(x) 的类型为 β\beta
  • α,β\alpha, \beta 可以是未知类型

类型转换

假设在表达式 xix * i 中,xx 为浮点数、ii 为整数,则结果应该是浮点数

  • xxii 使用不同的二进制表示方式
  • 浮点数 * 和整数 * 使用不同的指令
  • 例如:
    • t1=(float)it_1 = (\text{float}) i
    • t2=x  fmul  t1t_2 = x \; \text{fmul} \; t_1

处理简单的类型转换的 SDD:

产生式语义规则EE1+E2if (E1.type=integer and E2.type=integer)E.type=integerelse if (E1.type=float and E2.type=integer)E.type=float\begin{array}{|ll|} \hline \text{产生式} & \text{语义规则} \\ \hline E \to E_1 + E_2 & \text{if } (E_1.\text{type} = \text{integer} \text{ and } E_2.\text{type} = \text{integer}) E.\text{type} = \text{integer} \\ & \text{else if } (E_1.\text{type} = \text{float} \text{ and } E_2.\text{type} = \text{integer}) E.\text{type} = \text{float} \\ \hline \end{array}

类型拓宽和类型收缩

  • 编译器自动完成的转换为 隐式转换(coercion)
  • 程序员用代码指定的强制转换为 显式转换(cast)

处理类型转换的 SDT

产生式语义规则EE1+E2E.type=max(E1.type,E2.type);a1=widen(E1.addr,E1.type,E.type);a2=widen(E2.addr,E2.type,E.type);E.addr=new Temp();gen(E.addr “=” a1 “+” a2);\begin{array}{|ll|} \hline \text{产生式} & \text{语义规则} \\ \hline E \to E_1 + E_2 & E.\text{type} = \max(E_1.\text{type}, E_2.\text{type}); \\ & a_1 = \text{widen}(E_1.\text{addr}, E_1.\text{type}, E.\text{type}); \\ & a_2 = \text{widen}(E_2.\text{addr}, E_2.\text{type}, E.\text{type}); \\ & E.\text{addr} = \text{new Temp}(); \\ & \text{gen}(E.\text{addr} \text{ “=” } a_1 \text{ “+” } a_2); \\ \hline \end{array}

widen 函数用于将一个地址的值转换为指定的类型。其定义如下:

Addr widen(Addr a, Type t, Type w) {
    if (t == w) return a;
    else if (t == integer && w == float) {
        Addr temp = new Temp();
        gen(temp '=' '(float)' a); // 就是这里发生了隐式类型转换
        return temp;
    }
    else error;
}
cpp
  • max 函数用于查找两个类型的最小公共祖先。具体实现依赖于类型系统的定义。
  • widen 函数生成必要的类型转换代码,并返回转换后的地址。

函数/运算符的重载

通过查看参数来解决函数重载问题:

产生式语义规则Ef(E1)if f.typeset={siti1ik} and E1.type=sk then E.type=tk\begin{array}{|ll|} \hline \text{产生式} & \text{语义规则} \\ \hline E \to f(E_1) & \text{if } f.\text{typeset} = \{s_i \to t_i \mid 1 \leq i \leq k\} \text{ and } E_1.\text{type} = s_k \text{ then } E.\text{type} = t_k \\ \hline \end{array}

控制流的翻译

布尔表达式可以用于改变控制流/计算逻辑值:

文法:

BBBB&&B!B(B)ErelEtruefalseB \to B \, || \, B \mid B \, \&\& \, B \mid !B \mid (B) \mid E \, \text{rel} \, E \mid \text{true} \mid \text{false}

短路求值:

  • B1B2B_1 \, || \, B_2B1B_1 为真时,不用计算 B2B_2,整个表达式为真。
  • B1&&B2B_1 \, \&\& \, B_2B1B_1 为假时,不用计算 B2B_2,整个表达式为假。

短路代码通过跳转指令实现控制流的处理,逻辑运算符本身不在代码中出现。

98647

控制流语句的 SDD

产生式语义规则PSS.next=newlabel()P.code=S.codelabel(S.next)SassignS.code=assign.codeSif(B)S1B.true=newlabel()B.false=S1.next=S.nextS.code=B.codelabel(B.true)S1.codeSif(B)S1 else S2B.true=newlabel()B.false=newlabel()S1.next=S2.next=S.nextS.code=B.codelabel(B.true)S1.codegen(“goto”S.next)label(B.false)S2.codeSwhile(B)S1begin=newlabel()B.true=newlabel()B.false=S.nextS1.next=beginS.code=label(begin)B.codelabel(B.true)S1.codegen(“goto”begin)SS1S2S1.next=newlabel()S2.next=S.nextS.code=S1.codelabel(S1.next)S2.code\begin{array}{|ll|} \hline \text{产生式} & \text{语义规则} \\ \hline P \to S & S.\text{next} = \text{newlabel()} \\ & P.\text{code} = S.\text{code} \parallel \text{label}(S.\text{next}) \\ \hline S \to \text{assign} & S.\text{code} = \text{assign.code} \\ \hline S \to \text{if} (B) S_1 & B.\text{true} = \text{newlabel()} \\ & B.\text{false} = S_1.\text{next} = S.\text{next} \\ & S.\text{code} = B.\text{code} \parallel \text{label}(B.\text{true}) \parallel S_1.\text{code} \\ \hline S \to \text{if} (B) S_1 \text{ else } S_2 & B.\text{true} = \text{newlabel()} \\ & B.\text{false} = \text{newlabel()} \\ & S_1.\text{next} = S_2.\text{next} = S.\text{next} \\ & S.\text{code} = B.\text{code} \parallel \text{label}(B.\text{true}) \parallel S_1.\text{code} \\ & \quad \parallel \text{gen}(\text{“goto”} S.\text{next}) \parallel \text{label}(B.\text{false}) \parallel S_2.\text{code} \\ \hline S \to \text{while} (B) S_1 & \text{begin} = \text{newlabel()} \\ & B.\text{true} = \text{newlabel()} \\ & B.\text{false} = S.\text{next} \\ & S_1.\text{next} = \text{begin} \\ & S.\text{code} = \text{label}(\text{begin}) \parallel B.\text{code} \parallel \text{label}(B.\text{true}) \parallel S_1.\text{code} \parallel \text{gen}(\text{“goto”} \text{begin}) \\ \hline S \to S_1 S_2 & S_1.\text{next} = \text{newlabel()} \\ & S_2.\text{next} = S.\text{next} \\ & S.\text{code} = S_1.\text{code} \parallel \text{label}(S_1.\text{next}) \parallel S_2.\text{code} \\ \hline \end{array}

重点在于理解标号的顺序,明白基本块之间是怎么跳转的,其实如果自己做完 Lab Lv6 基本上就很简单了。

布尔表达式控制流翻译

生成的代码执行时跳转到两个标号之一:

  • 表达式的值为真时,跳转到 B.trueB.\text{true}
  • 表达式的值为假时,跳转到 B.falseB.\text{false}

B.trueB.\text{true}B.falseB.\text{false} 是两个继承属性,根据 BB 所在的上下文指向不同的位置:

  • 如果 BB 是 if 语句的条件表达式,分别指向 then 分支和 else 分支
  • 如果没有 else 分支,则 B.falseB.\text{false} 指向 if 语句的下一条指令
  • 如果 BB 是 while 语句的条件表达式,分别指向循环体的开头和循环的出口

下图的代码中同时考虑了短路求值

产生式语义规则BB1B2B1.true=B.true;B1.false=newlabel();B2.true=B.true;B2.false=B.false;B.code=B1.codelabel(B1.false)B2.codeBB1&&B2B1.true=newlabel();B1.false=B.false;B2.true=B.true;B2.false=B.false;B.code=B1.codelabel(B1.true)B2.codeB!B1B1.true=B.false;B1.false=B.true;B.code=B1.codeB(B1)B1.true=B.true;B1.false=B.false;B.code=B1.codeBE1 rel E2B.code=gen(“if”E1.addr rel.op E2.addr “goto”B.true)gen(“goto”B.false)BtrueB.code=gen(“goto”B.true)BfalseB.code=gen(“goto”B.false)\begin{array}{|ll|} \hline \text{产生式} & \text{语义规则} \\ \hline B \to B_1 || B_2 & B_1.\text{true} = B.\text{true}; B_1.\text{false} = \text{newlabel()}; \\ & B_2.\text{true} = B.\text{true}; B_2.\text{false} = B.\text{false}; \\ & B.\text{code} = B_1.\text{code} \parallel \text{label}(B_1.\text{false}) \parallel B_2.\text{code} \\ \hline B \to B_1 \&\& B_2 & B_1.\text{true} = \text{newlabel()}; B_1.\text{false} = B.\text{false}; \\ & B_2.\text{true} = B.\text{true}; B_2.\text{false} = B.\text{false}; \\ & B.\text{code} = B_1.\text{code} \parallel \text{label}(B_1.\text{true}) \parallel B_2.\text{code} \\ \hline B \to ! B_1 & B_1.\text{true} = B.\text{false}; B_1.\text{false} = B.\text{true}; B.\text{code} = B_1.\text{code} \\ \hline B \to (B_1) & B_1.\text{true} = B.\text{true}; B_1.\text{false} = B.\text{false}; B.\text{code} = B_1.\text{code} \\ \hline B \to E_1 \text{ rel } E_2 & B.\text{code} = \text{gen}(\text{“if”} E_1.\text{addr } \text{rel.op } E_2.\text{addr } \text{“goto”} B.\text{true}) \parallel \text{gen}(\text{“goto”} B.\text{false}) \\ \hline B \to \text{true} & B.\text{code} = \text{gen}(\text{“goto”} B.\text{true}) \\ \hline B \to \text{false} & B.\text{code} = \text{gen}(\text{“goto”} B.\text{false}) \\ \hline \end{array}

布尔值和跳转代码

程序中出现布尔表达式的目的也有可能就是求出它的值,例如 x=a<bx = a < b

处理方法:首先建立表达式的语法树,然后根据表达式的不同角色来处理。

文法:

  • Sid=E;  if (E) S  while (E) S  S SS \rightarrow \text{id} = E; \ | \ \text{if (E) S} \ | \ \text{while (E) S} \ | \ S \ S
  • EEE  E && E  E rel E  E \rightarrow E \| E \ | \ E \ \&\& \ E \ | \ E \ \text{rel} \ E \ | \ \ldots

根据 EE 的语法树结点所在的位置:

  • Swhile (E) S1S \rightarrow \text{while (E) S1} 中的 EE,生成跳转代码
  • Sid=ES \rightarrow \text{id} = E,生成计算右值的代码

在写 Lab 的时候实际上是反正值肯定返回,但是怎么用(赋值还是条件跳转)就是上一级考虑的问题了。

回填

为布尔表达式和控制流语句生成目标代码的关键问题:某些跳转指令应该跳转到哪里?

例如: if (B) S\text{if (B) S}

  • 按照短路代码的翻译方法,BB 的代码中有一些跳转指令在 BB 为假时执行,
  • 这些跳转指令的目标应该跳过 SS 对应的代码。生成这些指令时,SS 的代码尚未生成,因此目标不确定
  • 如果通过语句的继承属性 next\text{next} 来传递,当中间代码不允许符号标号时,则需要第二趟处理。

回填的基本思想

  1. 记录 BB 的代码中跳转指令 goto S.next\text{goto S.next}if ... goto S.next\text{if ... goto S.next} 的位置,但是不生成跳转目标
  2. 这些位置被记录到 BB 的综合属性 B.falseListB.\text{falseList}
  3. S.nextS.\text{next} 的值已知时(即 SS 的代码生成完毕时),把 B.falseListB.\text{falseList} 中的所有指令的目标都填上这个值

回填技术

  • 生成跳转指令时暂时不指定跳转目标标号,而是使用列表记录这些不完整的指令
  • 等知道正确的目标时再填写目标标号
  • 每个列表中的指令都指向同一个目标,列表包括:truelist\text{truelist}, falselist\text{falselist}, nextlist\text{nextlist}

布尔表达式的回填翻译

产生式语义规则BB1MB2backpatch(B1.falselist,M.instr);B.truelist=merge(B1.truelist,B2.truelist);B.falselist=B2.falselist;BB1&&MB2backpatch(B1.truelist,M.instr);B.truelist=B2.truelist;B.falselist=merge(B1.falselist,B2.falselist);B!B1B.truelist=B1.falselist;B.falselist=B1.truelist;B(B1)B.truelist=B1.truelist;B.falselist=B1.falselist;BE1 rel E2B.truelist=makelist(nextinstr);B.falselist=makelist(nextinstr + 1);emit(“if”E1.addr rel.op E2.addr “goto” B.true);emit(“goto”B.false);MεM.instr=nextinstr;BtrueB.truelist=makelist(nextinstr);emit(“goto”B.true);B.falselist=null;BfalseB.falselist=makelist(nextinstr);emit(“goto”B.false);B.truelist=null;\begin{array}{|ll|} \hline \text{产生式} & \text{语义规则} \\ \hline B \to B_1 || M B_2 & \text{backpatch}(B_1.\text{falselist}, M.\text{instr}); \\ & B.\text{truelist} = \text{merge}(B_1.\text{truelist}, B_2.\text{truelist}); \\ & B.\text{falselist} = B_2.\text{falselist}; \\ \hline B \to B_1 \&\& M B_2 & \text{backpatch}(B_1.\text{truelist}, M.\text{instr}); \\ & B.\text{truelist} = B_2.\text{truelist}; \\ & B.\text{falselist} = \text{merge}(B_1.\text{falselist}, B_2.\text{falselist}); \\ \hline B \to ! B_1 & B.\text{truelist} = B_1.\text{falselist}; \\ & B.\text{falselist} = B_1.\text{truelist}; \\ \hline B \to (B_1) & B.\text{truelist} = B_1.\text{truelist}; \\ & B.\text{falselist} = B_1.\text{falselist}; \\ \hline B \to E_1 \text{ rel } E_2 & B.\text{truelist} = \text{makelist(nextinstr)}; \\ & B.\text{falselist} = \text{makelist(nextinstr + 1)}; \\ & \text{emit}(\text{“if”} E_1.\text{addr } \text{rel.op } E_2.\text{addr } \text{“goto” } B.\text{true}); \\ & \text{emit}(\text{“goto”} B.\text{false}); \\ \hline M \to \varepsilon & M.\text{instr} = \text{nextinstr}; \\ \hline B \to \text{true} & B.\text{truelist} = \text{makelist(nextinstr)}; \\ & \text{emit}(\text{“goto”} B.\text{true}); \\ & B.\text{falselist} = \text{null}; \\ \hline B \to \text{false} & B.\text{falselist} = \text{makelist(nextinstr)}; \\ & \text{emit}(\text{“goto”} B.\text{false}); \\ & B.\text{truelist} = \text{null}; \\ \hline \end{array}

首先注意:所有的语义规则都是在产生式末尾,这个表省略了大括号(后同),此时,你即将规约回产生式头,而且拥有了所有产生式体的属性(此时他们是综合属性),所以可以随便用了。

这里,引入两个综合属性:

  • truelist:包含跳转指令(位置)的列表,这些指令在取值 true 时执行
  • falselist:包含跳转指令(位置)的列表,这些指令在取值 false 时执行

辅助函数包括:

  • makelist(i):构造一个列表
  • merge(p1, p2):合并两个列表
  • backpatch(p, i):用 i 回填 p 指向的语句列表中的跳转语句的跳转地址

大概讲一下,以第一个产生式 BB1MB2B \to B_1 || M B_2 为例:

  1. 当展开此步的时候,我们已经知道了当 B1B_1 为假时,会继续判断 B2B_2,所以可以用 M.instrM.\text{instr} 回填 B1.falselistB_1.\text{falselist}
  2. 但是此时还没有生成 B2B_2 的代码,我们不知道 B1B_1 为真的时候应该跳多远才能跳过 B2B_2,所以先把 B1.truelistB_1.\text{truelist}B2.truelistB_2.\text{truelist} 合并,得到 B.truelistB.\text{truelist},这意味着如果二者任一为真,就代表 BB 为真,在回填 B.truelistB.\text{truelist} 的时候,也就可以回填到 B1.truelistB_1.\text{truelist}B2.truelistB_2.\text{truelist}
  3. 当然,如果 B2B_2 为假(这隐含我们已经判断到了 B2B_2,也即 B1B_1 为假),那么 BB 也为假,所以 B.falselistB.\text{falselist} 就是 B2.falselistB_2.\text{falselist}

控制流语句的回填翻译

产生式语义规则Sif(B) M1 S1 N else M2 S2backpatch(B.truelist,M1.instr);backpatch(B.falselist,M2.instr);temp=merge(S1.nextlist,N.nextlist);S.nextlist=merge(temp,S2.nextlist);Sif(B) then M S1backpatch(B.truelist,M.instr);S.nextlist=merge(B.falselist,S1.nextlist);NεN.nextlist=nextinstr;emit(“goto”____);/稍后回填/MεM.instr=nextinstr;Swhile M1 (B) do M2 S1backpatch(S1.nextlist,M1.instr);backpatch(B.truelist,M2.instr);S.nextlist=B.falselist;emit(“goto”M1.instr);S{L}S.nextlist=L.nextlist;SAS.nextlist=null;LL1 M Sbackpatch(L1.nextlist,M.instr);L.nextlist=S.nextlist;LSL.nextlist=S.nextlist;\begin{array}{|ll|} \hline \text{产生式} & \text{语义规则} \\ \hline S \to \text{if} (B) \ M_1 \ S_1 \ N \ \text{else} \ M_2 \ S_2 & \text{backpatch}(B.\text{truelist}, M_1.\text{instr}); \\ & \text{backpatch}(B.\text{falselist}, M_2.\text{instr}); \\ & \text{temp} = \text{merge}(S_1.\text{nextlist}, N.\text{nextlist}); \\ & S.\text{nextlist} = \text{merge}(\text{temp}, S_2.\text{nextlist}); \\ \hline S \to \text{if} (B) \ \text{then} \ M \ S_1 & \text{backpatch}(B.\text{truelist}, M.\text{instr}); \\ & S.\text{nextlist} = \text{merge}(B.\text{falselist}, S_1.\text{nextlist}); \\ \hline N \to \varepsilon & N.\text{nextlist} = \text{nextinstr}; \\ & \text{emit}(\text{“goto”\_\_\_\_}); /* 稍后回填 */ \\ \hline M \to \varepsilon & M.\text{instr} = \text{nextinstr}; \\ \hline S \to \text{while} \ M_1 \ (B) \ \text{do} \ M_2 \ S_1 & \text{backpatch}(S_1.\text{nextlist}, M_1.\text{instr}); \\ & \text{backpatch}(B.\text{truelist}, M_2.\text{instr}); \\ & S.\text{nextlist} = B.\text{falselist}; \\ & \text{emit}(\text{“goto”} M_1.\text{instr}); \\ \hline S \to \{ L \} & S.\text{nextlist} = L.\text{nextlist}; \\ \hline S \to A & S.\text{nextlist} = \text{null}; \\ \hline L \to L_1 \ M \ S & \text{backpatch}(L_1.\text{nextlist}, M.\text{instr}); \\ & L.\text{nextlist} = S.\text{nextlist}; \\ \hline L \to S & L.\text{nextlist} = S.\text{nextlist}; \\ \hline \end{array}

和之前大差不差。

Break 和 Continue 语句的处理方法

Break 语句:

  • 追踪外围语句 SS
  • 生成一个跳转指令坯
  • 将这个指令坯的位置加入到 S.nextlistS.\text{nextlist}

跟踪的方法:

  • 在符号表中设置 break\text{break} 条目,令其指向外围语句
  • 在符号表中设置指向 S.nextlistS.\text{nextlist} 的指针,然后把这个指令坯的位置直接加入到 nextlist\text{nextlist}

Switch 语句的生成式

为了构造 switch 语句的翻译方案,设置一个队列变量 qq

qq 的元素是记录,包含 cc(condition) 和 dd(destination) 两个成员,分别用于存储 case 后面的常量值 vv 和各语句串中间代码第一个三地址语句地址,以便生成 test 后面的条件转移语句时使用

Switch 语句的翻译方案

产生式语义规则Sswitch(E)H{Mdefault:FL}S.nextlist=merge(M.nextlist,L.nextlist,makelist(nextinstr))emit(‘goto-’)backpatch(H.list,nextinstr)for t in q:gen(‘if’E.addr ‘==’ t.c‘goto’ t.d)emit(‘goto’ F.instr)Hεset q as H.list=makelist(nextinstr)emit(‘goto-’)FεF.instr=nextinstrMcaseC:FLt.c=C.valt.d=F.instrinsert tinto qM.nextlist=merge(L.nextlist,makelist(nextinstr))emit(‘goto-’)MM1caseC:FLt.c=C.valt.d=F.instrinsert tinto qM.nextlist=merge(M1.nextlist,L.nextlist,makelist(nextinstr))emit(‘goto-’)LSL.nextlist=S.nextlistLL1FSbackpatch(L1.nextlist,F.instr)L.nextlist=S.nextlist\begin{array}{|ll|} \hline \text{产生式} & \text{语义规则} \\ \hline S \rightarrow \text{switch} (E) H \{ M \, \text{default}:F \, L \} & S.\text{nextlist} = \text{merge}(M.\text{nextlist}, L.\text{nextlist}, \text{makelist}(\text{nextinstr})) \\ & \text{emit}(\text{`goto-'}) \\ & \text{backpatch}(H.\text{list}, \text{nextinstr}) \\ & \text{for t in } q: \\ & \quad \text{gen}(\text{`if'} \, E.\text{addr } \text{`==' } t.c \, \text{`goto' } t.d) \\ & \text{emit}(\text{`goto' } F.\text{instr}) \\ \hline H \rightarrow \varepsilon & \text{set q as } \varnothing \\ & H.\text{list} = \text{makelist}(\text{nextinstr}) \\ & \text{emit}(\text{`goto-'}) \\ \hline F \rightarrow \varepsilon & F.\text{instr} = \text{nextinstr} \\ \hline M \rightarrow \text{case} \, C:F \, L & t.c = C.\text{val} \\ & t.d = F.\text{instr} \\ & \text{insert } t \, \text{into } q \\ & M.\text{nextlist} = \text{merge}(L.\text{nextlist}, \text{makelist}(\text{nextinstr})) \\ & \text{emit}(\text{`goto-'}) \\ \hline M \rightarrow M_1 \, \text{case} \, C:F \, L & t.c = C.\text{val} \\ & t.d = F.\text{instr} \\ & \text{insert } t \, \text{into } q \\ & M.\text{nextlist} = \text{merge}(M_1.\text{nextlist}, L.\text{nextlist}, \text{makelist}(\text{nextinstr})) \\ & \text{emit}(\text{`goto-'}) \\ \hline L \rightarrow S & L.\text{nextlist} = S.\text{nextlist} \\ \hline L \rightarrow L_1 \, F \, S & \text{backpatch}(L_1.\text{nextlist}, F.\text{instr}) \\ & L.\text{nextlist} = S.\text{nextlist} \\ \hline \end{array}

For 循环的翻译方案

(来自 22 年往年题)

产生式语义规则Sfor (S1 M1 ;B; M2 S2) N S3backpatch(B.truelist,N.instr);backpatch(N.nextlist,M1.instr);backpatch(S1.nextlist,M1.instr);backpatch(S2.nextlist,M1.instr);backpatch(S3.nextlist,M2.instr);emit(“goto” M2.instr);S.nextlist=B.falselist;MεM.instr=nextinstr;NεN.nextlist=makelist(nextinstr);emit(“goto” ____);N.instr=nextinstr;\begin{array}{|ll|} \hline \text{产生式} & \text{语义规则} \\ \hline S \to \text{for} \ (S_1 \ M_1 \ ; B ; \ M_2 \ S_2) \ N \ S_3 & \text{backpatch}(B.\text{truelist}, N.\text{instr}); \\ & \text{backpatch}(N.\text{nextlist}, M_1.\text{instr}); \\ & \text{backpatch}(S_1.\text{nextlist}, M_1.\text{instr}); \\ & \text{backpatch}(S_2.\text{nextlist}, M_1.\text{instr}); \\ & \text{backpatch}(S_3.\text{nextlist}, M_2.\text{instr}); \\ & \text{emit}(\text{“goto”} \ M_2.\text{instr}); \\ & S.\text{nextlist} = B.\text{falselist}; \\ \hline M \to \varepsilon & M.\text{instr} = \text{nextinstr}; \\ \hline N \to \varepsilon & N.\text{nextlist} = \text{makelist}(\text{nextinstr}); \\ & \text{emit}(\text{“goto”} \ \_\_\_\_); \\ & N.\text{instr} = \text{nextinstr}; \\ \hline \end{array}

注意这里,BB 默认是非顺序执行,一定跳转的,所以 M2M_2 的位置不是 NN,而 S2S_2 默认接下来是顺序执行的,所以后面要跟个 goto\text{goto}

中间代码生成 II
https://arthals.ink/blog/ir-generation-2
Author Arthals
Published at January 7, 2025
Comment seems to stuck. Try to refresh?✨