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 的条件语句的值是常量,则根据常量值在 BodyElse 中二选一。逻辑运算符的 &&|| 的处理逻辑如下:

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
}

如果第一个分量是常量,那么编译器会根据常量值从两个分量中二选一,用来替换掉之前的整个逻辑表达式。

最后更新于