7.2 处理逻辑
在编译器主函数中,处理无效代码的代码片段如下:
// file: cmd/compile/internal/gc/main.go
func Main() {
// 忽略之前代码
// 清除无效代码
for _, n := range typecheck.Target.Decls {
if n.Op() == ir.ODCLFUNC {
deadcode.Func(n.(*ir.Func))
}
}
// 忽略之后代码
}
对于所有的函数(包括方法),编译器将通过函数
deadcode.Func()
清除其无效代码。所有清除无效代码的逻辑都定义在文件 cmd/compile/internal/deadcode/deadcode.go
中,该文件总共只有100多行,逻辑相对简单。总体思路是遍历函数的语法树(IR Tree),然后对分支(Branch)及逻辑运算表达式进行判定,如果确实有可以清除掉的无效代码,则直接修改语法树。我们先看一下其对
if
语句的处理:if n.Op() == ir.OIF {
n := n.(*ir.IfStmt)
n.Cond = expr(n.Cond)
if ir.IsConst(n.Cond, constant.Bool) {
var body ir.Nodes
if ir.BoolVal(n.Cond) {
n.Else = ir.Nodes{}
body = n.Body
} else {
n.Body = ir.Nodes{}
body = n.Else
}
}
}
如果
if
的条件语句的值是常量,则根据常量值在 Body
与 Else
中二选一。逻辑运算符的 &&
与 ||
的处理逻辑如下:func expr(n ir.Node) ir.Node {
// Perform dead-code elimination on short-circuited boolean
// expressions involving constants with the intent of
// producing a constant 'if' condition.
switch n.Op() {
case ir.OANDAND:
n := n.(*ir.LogicalExpr)
n.X = expr(n.X)
n.Y = expr(n.Y)
if ir.IsConst(n.X, constant.Bool) {
if ir.BoolVal(n.X) {
return n.Y // true && x => x
} else {
return n.X // false && x => false
}
}
case ir.OOROR:
n := n.(*ir.LogicalExpr)
n.X = expr(n.X)
n.Y = expr(n.Y)
if ir.IsConst(n.X, constant.Bool) {
if ir.BoolVal(n.X) {
return n.X // true || x => true
} else {
return n.Y // false || x => x
}
}
}
return n
}
如果第一个分量是常量,那么编译器会根据常量值从两个分量中二选一,用来替换掉之前的整个逻辑表达式。