// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package deadcode import ( "go/constant" "cmd/compile/internal/base" "cmd/compile/internal/ir" ) func Func(fn *ir.Func) { stmts(&fn.Body) if len(fn.Body) == 0 { return } for _, n := range fn.Body { if len(n.Init()) > 0 { return } switch n.Op() { case ir.OIF: n := n.(*ir.IfStmt) if !ir.IsConst(n.Cond, constant.Bool) || len(n.Body) > 0 || len(n.Else) > 0 { return } case ir.OFOR: n := n.(*ir.ForStmt) if !ir.IsConst(n.Cond, constant.Bool) || ir.BoolVal(n.Cond) { return } default: return } } ir.VisitList(fn.Body, markHiddenClosureDead) fn.Body = []ir.Node{ir.NewBlockStmt(base.Pos, nil)} } func stmts(nn *ir.Nodes) { var lastLabel = -1 for i, n := range *nn { if n != nil && n.Op() == ir.OLABEL { lastLabel = i } } for i, n := range *nn { // Cut is set to true when all nodes after i'th position // should be removed. // In other words, it marks whole slice "tail" as dead. cut := false if n == nil { continue } 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) { ir.VisitList(n.Else, markHiddenClosureDead) n.Else = ir.Nodes{} body = n.Body } else { ir.VisitList(n.Body, markHiddenClosureDead) n.Body = ir.Nodes{} body = n.Else } // If "then" or "else" branch ends with panic or return statement, // it is safe to remove all statements after this node. // isterminating is not used to avoid goto-related complications. // We must be careful not to deadcode-remove labels, as they // might be the target of a goto. See issue 28616. if body := body; len(body) != 0 { switch body[(len(body) - 1)].Op() { case ir.ORETURN, ir.OTAILCALL, ir.OPANIC: if i > lastLabel { cut = true } } } } } if len(n.Init()) != 0 { stmts(n.(ir.InitNode).PtrInit()) } switch n.Op() { case ir.OBLOCK: n := n.(*ir.BlockStmt) stmts(&n.List) case ir.OFOR: n := n.(*ir.ForStmt) stmts(&n.Body) case ir.OIF: n := n.(*ir.IfStmt) stmts(&n.Body) stmts(&n.Else) case ir.ORANGE: n := n.(*ir.RangeStmt) stmts(&n.Body) case ir.OSELECT: n := n.(*ir.SelectStmt) for _, cas := range n.Cases { stmts(&cas.Body) } case ir.OSWITCH: n := n.(*ir.SwitchStmt) for _, cas := range n.Cases { stmts(&cas.Body) } } if cut { ir.VisitList((*nn)[i+1:len(*nn)], markHiddenClosureDead) *nn = (*nn)[:i+1] break } } } 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 } func markHiddenClosureDead(n ir.Node) { if n.Op() != ir.OCLOSURE { return } clo := n.(*ir.ClosureExpr) if clo.Func.IsHiddenClosure() { clo.Func.SetIsDeadcodeClosure(true) } ir.VisitList(clo.Func.Body, markHiddenClosureDead) }