summaryrefslogtreecommitdiffstats
path: root/src/cmd/compile/internal/deadcode
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/deadcode')
-rw-r--r--src/cmd/compile/internal/deadcode/deadcode.go247
1 files changed, 247 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/deadcode/deadcode.go b/src/cmd/compile/internal/deadcode/deadcode.go
new file mode 100644
index 0000000..decd261
--- /dev/null
+++ b/src/cmd/compile/internal/deadcode/deadcode.go
@@ -0,0 +1,247 @@
+// 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"
+ "go/token"
+
+ "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 n.Op() == ir.OSWITCH {
+ n := n.(*ir.SwitchStmt)
+ // Use a closure wrapper here so we can use "return" to abort the analysis.
+ func() {
+ if n.Tag != nil && n.Tag.Op() == ir.OTYPESW {
+ return // no special type-switch case yet.
+ }
+ var x constant.Value // value we're switching on
+ if n.Tag != nil {
+ if ir.ConstType(n.Tag) == constant.Unknown {
+ return
+ }
+ x = n.Tag.Val()
+ } else {
+ x = constant.MakeBool(true) // switch { ... } => switch true { ... }
+ }
+ var def *ir.CaseClause
+ for _, cas := range n.Cases {
+ if len(cas.List) == 0 { // default case
+ def = cas
+ continue
+ }
+ for _, c := range cas.List {
+ if ir.ConstType(c) == constant.Unknown {
+ return // can't statically tell if it matches or not - give up.
+ }
+ if constant.Compare(x, token.EQL, c.Val()) {
+ for _, n := range cas.Body {
+ if n.Op() == ir.OFALL {
+ return // fallthrough makes it complicated - abort.
+ }
+ }
+ // This switch entry is the one that always triggers.
+ for _, cas2 := range n.Cases {
+ for _, c2 := range cas2.List {
+ if cas2 != cas || c2 != c {
+ ir.Visit(c2, markHiddenClosureDead)
+ }
+ }
+ if cas2 != cas {
+ ir.VisitList(cas2.Body, markHiddenClosureDead)
+ }
+ }
+
+ cas.List[0] = c
+ cas.List = cas.List[:1]
+ n.Cases[0] = cas
+ n.Cases = n.Cases[:1]
+ return
+ }
+ }
+ }
+ if def != nil {
+ for _, n := range def.Body {
+ if n.Op() == ir.OFALL {
+ return // fallthrough makes it complicated - abort.
+ }
+ }
+ for _, cas := range n.Cases {
+ if cas != def {
+ ir.VisitList(cas.List, markHiddenClosureDead)
+ ir.VisitList(cas.Body, markHiddenClosureDead)
+ }
+ }
+ n.Cases[0] = def
+ n.Cases = n.Cases[:1]
+ return
+ }
+
+ // TODO: handle case bodies ending with panic/return as we do in the IF case above.
+
+ // entire switch is a nop - no case ever triggers
+ for _, cas := range n.Cases {
+ ir.VisitList(cas.List, markHiddenClosureDead)
+ ir.VisitList(cas.Body, markHiddenClosureDead)
+ }
+ n.Cases = n.Cases[:0]
+ }()
+ }
+
+ 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)
+}