编译原理核心 3 大考点精讲:LL/LR 文法、中间代码生成与数据流分析实战

编译原理核心 3 大考点精讲:LL/LR 文法、中间代码生成与数据流分析实战
编译原理三大核心考点实战精解从文法分析到代码优化1. LL(1)与LR(1)分析表构造的对比与实践在编译器设计的语法分析阶段LL(1)和LR(1)是两种最常用的自顶向下和自底向上分析方法。理解它们的构造原理和差异对于应对考试和实际开发都至关重要。LL(1)分析表构造五步法消除左递归将形如A→Aα|β的产生式改写为A→βA和A→αA|ε提取左公因子处理产生式如A→αβ|αγ改写为A→αA和A→β|γ计算FIRST集对于非终结符XFIRST(X)是可以从X推导出的所有串的首终结符集合计算FOLLOW集对于非终结符AFOLLOW(A)是可能在某些句型中紧接A后的终结符集合构建分析表对每个产生式A→α将A→α加入表项M[A,b]其中b∈FIRST(α)或若α可推导出ε则b∈FOLLOW(A)# LL(1)分析表构造示例代码片段 def build_ll1_table(grammar): table defaultdict(dict) for nt in grammar.nonterminals: for prod in grammar.productions[nt]: first grammar.first(prod.rhs) for term in first - {ε}: table[nt][term] prod if ε in first: for term in grammar.follow(nt): table[nt][term] prod return tableLR(1)分析表构造关键步骤构造项目集规范族计算LR(1)项目集的闭包和转移构建ACTION表根据移进和规约动作填充构建GOTO表记录状态转移处理冲突解决移进-规约和规约-规约冲突对比维度LL(1)分析LR(1)分析分析方向自顶向下自底向上文法限制无左递归无限制分析表大小较小较大错误检测较快稍慢适用场景简单语法复杂语法提示考试中常出现非LL(1)文法的判断题目可以通过检查分析表中是否存在冲突项来快速判断。对于LR(1)分析要特别注意向前看符号的处理。2. 中间代码生成从C语言到四元式的完整转换中间代码生成是编译器前端和后端的桥梁四元式因其结构清晰成为考试重点。以下是一个包含while循环、if条件和数组访问的C代码示例及其四元式转换模板示例C代码int main() { int a[10], i 0; while(i 10) { if(i % 2 0) { a[i] i * 2; } else { a[i] i 1; } i i 1; } return 0; }对应四元式序列(:, 0, _, i) // i 0(j, i, 10, 5) // 跳转到while条件判断(j, _, _, 16) // 跳出循环(label, _, _, 5) // while条件标签(mod, i, 2, t1) // t1 i % 2(, t1, 0, 8) // if条件判断(j, _, _, 12) // 跳转到else部分(*, i, 2, t2) // t2 i * 2([], t2, i, a) // a[i] t2(j, _, _, 14) // 跳过else部分(label, _, _, 12) // else标签(, i, 1, t3) // t3 i 1([], t3, i, a) // a[i] t3(label, _, _, 15) // 循环结束标签(, i, 1, i) // i i 1(j, _, _, 4) // 跳回while开始(label, _, _, 16) // 循环退出标签(ret, 0, _, _) // return 0关键转换模式while循环模板label L1: 条件判断代码 if false跳转到L2 循环体代码 jmp L1 label L2:if-else模板条件判断代码 if true跳转到L3 else部分代码 jmp L4 label L3: if部分代码 label L4:数组访问需要特别注意地址计算和类型检查多维数组按行优先顺序计算偏移量3. 数据流分析与自然循环优化数据流分析是编译器优化的基础考试中常要求手工推导以下三种经典数据流分析3.1 到达-定值分析Reaching Definitions计算每个程序中各点能够到达的变量定值集合。对于基本块BGEN[B]在B中生成且不被杀死的定值KILL[B]被B杀死的其他定值的集合IN[B]到达B入口的定值集合OUT[B]到达B出口的定值集合方程IN[B] ∪ OUT[P], 其中P是B的所有前驱 OUT[B] GEN[B] ∪ (IN[B] - KILL[B])3.2 活跃变量分析Live Variables分析在程序点之后变量是否会被引用。对于基本块BUSE[B]在B中被引用前未定值的变量DEF[B]在B中被定值的变量IN[B]在B入口处活跃的变量OUT[B]在B出口处活跃的变量方程OUT[B] ∪ IN[S], 其中S是B的所有后继 IN[B] USE[B] ∪ (OUT[B] - DEF[B])3.3 可用表达式分析Available Expressions分析在程序点处表达式是否已被计算且操作数未被修改。对于基本块BGEN[B]在B中生成且之后不被杀死的表达式KILL[B]被B杀死的表达式IN[B]在B入口处可用的表达式OUT[B]在B出口处可用的表达式方程IN[B] ∩ OUT[P], 其中P是B的所有前驱 OUT[B] GEN[B] ∪ (IN[B] - KILL[B])自然循环识别步骤构建控制流图(CFG)和支配节点树识别回边从节点n到d的边其中d支配n找出自然循环回边d→n对应的循环是d加上所有能不经过d到达n的节点示例推导 考虑以下控制流图B1 → B2 → B3 → B4 ↑ ↓ ↓ B6 ← B5 ← B7回边B5→B2自然循环{B2,B3,B4,B5,B6}循环体B3,B4,B5,B6循环头B2注意考试中常要求手工推导数据流方程的迭代求解过程建议从初始空集开始按拓扑顺序迭代计算各基本块的IN和OUT集合直到收敛。4. 综合应用从理论到实践的完整案例结合前述知识点我们分析一个包含数组访问和循环结构的C程序int sum(int a[], int n) { int s 0; for(int i 0; i n; i) { s a[i]; } return s; }优化过程中间代码生成转换为四元式表示基本块划分识别程序的基本块结构控制流分析构建控制流图数据流分析进行到达-定值和活跃变量分析循环优化循环不变量外提将不依赖循环变量的计算移出循环强度削弱将乘法运算转换为加法运算归纳变量消除删除不必要的归纳变量优化后的中间代码(:, 0, _, s) // s 0 (:, 0, _, i) // i 0 (*, n, 4, t1) // 计算数组字节长度 (label, L1, _, _) // 循环开始 (j, i, n, L2) // 循环条件判断 ([], a, i, t2) // t2 a[i] (, s, t2, s) // s t2 (, i, 1, i) // i (j, _, _, L1) // 跳回循环开始 (label, L2, _, _) // 循环结束 (ret, s, _, _) // return s性能对比优化技术优化前指令数优化后指令数执行效率提升循环不变量外提1210~15%强度削弱108~20%归纳变量消除86~25%在实际项目开发中这些优化技术可以显著提升程序性能。例如在HITCS的编译系统实验中通过合理应用这些优化技术学生可以将程序运行时间减少30%-50%。