ch13-ir-control-控制流语句的翻译
本讲内容颇有难度, 需要多多思考
虽然有非常简单的讲法, 教材龙书偏偏采用了让初学者望而生畏的讲法。
为什么要 “创造困难”?
简单的翻译方案会产生大量冗长的代码,选择困难的翻译方案是为了生成更短、更高效的代码
control.g4和bool表达式的文法
主要关注绿框中的控制流语句,assign上节课已经讲过了所以不关注
control.g4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 grammar Control; @header {package codegen;} prog : stat ; stat : ID '=' expr ';' # AssignStat | 'if' '(' bool ')' '{' ifStat = stat '}' 'else' '{' elseStat = stat '}' # IfElseStat | 'if' '(' bool ')' '{' stat '}' # IfStat | 'while' '(' bool ')' '{' stat '}' # WhileStat | 'break' ';' # BreakStat | first = stat second = stat # SeqStat ; bool : lhs = expr op = ('>' | '>=' | '==' ) rhs = expr # RelExpr | op = '!' bool # NotExpr | lhs = bool op = '&&' rhs = bool # AndExpr | lhs = bool op = '||' rhs = bool # OrExpr | 'true' # TrueExpr | 'false' # FalseExpr ; expr : '-' expr # NegExpr | expr '*' expr # MultExpr | expr '+' expr # ADDExpr | '(' expr ')' # ParenExpr | ID # IdExpr | INT # IntExpr ; GT : '>' ; GE : '>=' ; EE : '==' ; ID : [a-z] ; INT : [0 -9 ] ; WS : [ \t\r\n]+ -> skip;
一种非常简单的做法
分工合作:每个终结符负责且仅负责自己的翻译中间代码,不越俎代庖
例如:if (B) S1
结构:
先为B~1~生成code,计算真假,将最终逻辑值保存在t~1~
if用一个branch语句测试t~1~的真假,为真就跳到b.true,为假就跳转到b.false
b.true是一个标签,接着递归的为S~1~生成代码
B和if/while的作用是不同的!
控制流语句中间代码翻译代码展示:visitor
CodeGenVisitor.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 package codegen;import org.antlr.v4.runtime.Token;import java.io.FileWriter;import java.io.IOException;import java.sql.Array;import java.util.ArrayDeque;import java.util.Deque;public class CodeGenVisitor extends ControlBaseVisitor <String> { private final Deque<String> breakLabels = new ArrayDeque <>(); private final FileWriter fileWriter; private int tempCounter = 1 ; private int labelCounter = 1 ; public CodeGenVisitor (String file) throws IOException { fileWriter = new FileWriter (file); } @Override public String visitProg (ControlParser.ProgContext ctx) { visit(ctx.stat()); try { fileWriter.close(); } catch (IOException e) { throw new RuntimeException (e); } return null ; } @Override public String visitBreakStat (ControlParser.BreakStatContext ctx) { emit("br " + breakLabels.peek()); return null ; } @Override public String visitWhileStat (ControlParser.WhileStatContext ctx) { String beginLabel = getNewLabel("begin" ); emit(beginLabel); String bool = visit(ctx.bool()); String trueLabel = getNewLabel("b.true" ); String falseLabel = getNewLabel("b.false" ); emit("br " + bool + " " + trueLabel + " " + falseLabel); emitLabel(trueLabel); breakLabels.push(falseLabel); visit(ctx.stat()); breakLabels.pop(); emit("br " + beginLabel); emitLabel(falseLabel); return null ; } @Override public String visitIfElseStat (ControlParser.IfElseStatContext ctx) { String bool = visit(ctx.bool()); String trueLabel = getNewLabel("b.true" ); String falseLabel = getNewLabel("b.false" ); String endLabel = getNewLabel("b.end" ); emit("br " + bool + " " + trueLabel + " " + falseLabel); emitLabel(trueLabel); visit(ctx.ifStat); emit("br " + endLabel); emitLabel(falseLabel); visit(ctx.elseStat); emitLabel(endLabel); return null ; } @Override public String visitIfStat (ControlParser.IfStatContext ctx) { String bool = visit(ctx.bool()); String trueLabel = getNewLabel("b.true" ); String falseLabel = getNewLabel("b.false" ); emit("br " + bool + " " + trueLabel + " " + falseLabel); emitLabel(trueLabel); visit(ctx.stat()); emitLabel(falseLabel); return null ; } @Override public String visitSeqStat (ControlParser.SeqStatContext ctx) { visit(ctx.first); visit(ctx.second); return null ; } @Override public String visitAssignStat (ControlParser.AssignStatContext ctx) { String expr = visit(ctx.expr()); emit(ctx.ID().getText() + " = " + expr); return null ; } @Override public String visitRelExpr (ControlParser.RelExprContext ctx) { String lhs = visit(ctx.lhs); String rhs = visit(ctx.rhs); Token op = ctx.op; String cond = switch (op.getType()) { case ControlLexer.GT -> "sgt" ; case ControlLexer.GE -> "sge" ; case ControlLexer.EE -> "eq" ; default -> throw new IllegalArgumentException ("No such cond: " + op.getText()); }; String temp = getNewTemp(); emit(temp + " = " + "icmp " + cond + " " + lhs + " " + rhs); return temp; } @Override public String visitAndExpr (ControlParser.AndExprContext ctx) { String lhs = visit(ctx.lhs); String trueLabel = getNewLabel("b1.true" ); String falseLabel = getNewLabel("b1.false" ); String endLabel = getNewLabel("b1.end" ); emit("br " + lhs + " " + trueLabel + " " + falseLabel); emitLabel(trueLabel); String rhs = visit(ctx.rhs); String temp = getNewTemp(); emit(temp + " = " + "AND " + lhs + " " + rhs); emit("br " + endLabel); emitLabel(falseLabel); emit(temp + " = " + "false" ); emitLabel(endLabel); return temp; } @Override public String visitOrExpr (ControlParser.OrExprContext ctx) { String lhs = visit(ctx.lhs); String trueLabel = getNewLabel("or.true" ); String falseLabel = getNewLabel("or.false" ); String endLabel = getNewLabel("or.end" ); emit("br " + lhs + " " + trueLabel + " " + falseLabel); emitLabel(falseLabel); String rhs = visit(ctx.rhs); String temp = getNewTemp(); emit(temp + " = OR " + lhs + " " + rhs); emit("br " + endLabel); emitLabel(trueLabel); emit(temp + " = true" ); emitLabel(endLabel); return temp; } @Override public String visitNotExpr (ControlParser.NotExprContext ctx) { String bool = visit(ctx.bool()); String temp = getNewTemp(); emit(temp + " = " + "NOT " + bool); return temp; } @Override public String visitFalseExpr (ControlParser.FalseExprContext ctx) { String temp = getNewTemp(); emit(temp + " = " + ctx.getText()); return temp; } @Override public String visitTrueExpr (ControlParser.TrueExprContext ctx) { String temp = getNewTemp(); emit(temp + " = " + ctx.getText()); return temp; } @Override public String visitIdExpr (ControlParser.IdExprContext ctx) { String temp = getNewTemp(); emit(temp + " = " + ctx.getText()); return temp; } @Override public String visitIntExpr (ControlParser.IntExprContext ctx) { String temp = getNewTemp(); emit(temp + " = " + ctx.getText()); return temp; } private void emitLabel (String label) { try { fileWriter.write(label + ":\n" ); } catch (IOException ioe) { System.err.println(ioe.getMessage() + " : " + label); } } private void emit (String code) { try { fileWriter.write(code + '\n' ); } catch (IOException ioe) { System.err.println(ioe.getMessage() + " : " + code); } } private String getNewTemp () { return "t" + tempCounter++; } private String getNewLabel (String label) { return label + labelCounter++; } }
关于break的栈保存:
CodeGenVisitorTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package codegen;import org.antlr.v4.runtime.CharStream;import org.antlr.v4.runtime.CharStreams;import org.antlr.v4.runtime.CommonTokenStream;import org.antlr.v4.runtime.tree.ParseTree;import org.testng.annotations.BeforeMethod;import org.testng.annotations.Test;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.nio.file.Path;public class CodeGenVisitorTest { private InputStream is = System.in; private final String PATH = "src/test/antlr/codegen/control-flow-I/" ; private String srcFile; private String codeFile; @BeforeMethod public void setUp () throws IOException { srcFile = "while-if-II" ; is = new FileInputStream (Path.of(PATH + srcFile + ".txt" ).toFile()); } @Test public void testCodeGenVisitor () throws IOException { CharStream input = CharStreams.fromStream(is); ControlLexer lexer = new ControlLexer (input); CommonTokenStream tokens = new CommonTokenStream (lexer); ControlParser parser = new ControlParser (tokens); ParseTree tree = parser.prog(); codeFile = srcFile + "-code" ; CodeGenVisitor cg = new CodeGenVisitor (PATH + codeFile + ".txt" ); cg.visit(tree); } }
短路求值
B -> B~1~ && B~2~:
如果B~1~为false,则正常生成中间代码,但是B~2~不用判断真假,B.value = false,绕过B~2~
修改visitAndExpr
可以自行尝试or的短路求值
直接用布尔表达式改变控制流:比较复杂
(使用控制流跳转间接表名布尔表达式的值)