github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/deadcode/deadcode.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package deadcode 6 7 import ( 8 "github.com/bir3/gocompiler/src/go/constant" 9 "github.com/bir3/gocompiler/src/go/token" 10 11 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 12 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 13 ) 14 15 func Func(fn *ir.Func) { 16 stmts(&fn.Body) 17 18 if len(fn.Body) == 0 { 19 return 20 } 21 22 for _, n := range fn.Body { 23 if len(n.Init()) > 0 { 24 return 25 } 26 switch n.Op() { 27 case ir.OIF: 28 n := n.(*ir.IfStmt) 29 if !ir.IsConst(n.Cond, constant.Bool) || len(n.Body) > 0 || len(n.Else) > 0 { 30 return 31 } 32 case ir.OFOR: 33 n := n.(*ir.ForStmt) 34 if !ir.IsConst(n.Cond, constant.Bool) || ir.BoolVal(n.Cond) { 35 return 36 } 37 default: 38 return 39 } 40 } 41 42 ir.VisitList(fn.Body, markHiddenClosureDead) 43 fn.Body = []ir.Node{ir.NewBlockStmt(base.Pos, nil)} 44 } 45 46 func stmts(nn *ir.Nodes) { 47 var lastLabel = -1 48 for i, n := range *nn { 49 if n != nil && n.Op() == ir.OLABEL { 50 lastLabel = i 51 } 52 } 53 for i, n := range *nn { 54 // Cut is set to true when all nodes after i'th position 55 // should be removed. 56 // In other words, it marks whole slice "tail" as dead. 57 cut := false 58 if n == nil { 59 continue 60 } 61 if n.Op() == ir.OIF { 62 n := n.(*ir.IfStmt) 63 n.Cond = expr(n.Cond) 64 if ir.IsConst(n.Cond, constant.Bool) { 65 var body ir.Nodes 66 if ir.BoolVal(n.Cond) { 67 ir.VisitList(n.Else, markHiddenClosureDead) 68 n.Else = ir.Nodes{} 69 body = n.Body 70 } else { 71 ir.VisitList(n.Body, markHiddenClosureDead) 72 n.Body = ir.Nodes{} 73 body = n.Else 74 } 75 // If "then" or "else" branch ends with panic or return statement, 76 // it is safe to remove all statements after this node. 77 // isterminating is not used to avoid goto-related complications. 78 // We must be careful not to deadcode-remove labels, as they 79 // might be the target of a goto. See issue 28616. 80 if body := body; len(body) != 0 { 81 switch body[(len(body) - 1)].Op() { 82 case ir.ORETURN, ir.OTAILCALL, ir.OPANIC: 83 if i > lastLabel { 84 cut = true 85 } 86 } 87 } 88 } 89 } 90 if n.Op() == ir.OSWITCH { 91 n := n.(*ir.SwitchStmt) 92 // Use a closure wrapper here so we can use "return" to abort the analysis. 93 func() { 94 if n.Tag != nil && n.Tag.Op() == ir.OTYPESW { 95 return // no special type-switch case yet. 96 } 97 var x constant.Value // value we're switching on 98 if n.Tag != nil { 99 if ir.ConstType(n.Tag) == constant.Unknown { 100 return 101 } 102 x = n.Tag.Val() 103 } else { 104 x = constant.MakeBool(true) // switch { ... } => switch true { ... } 105 } 106 var def *ir.CaseClause 107 for _, cas := range n.Cases { 108 if len(cas.List) == 0 { // default case 109 def = cas 110 continue 111 } 112 for _, c := range cas.List { 113 if ir.ConstType(c) == constant.Unknown { 114 return // can't statically tell if it matches or not - give up. 115 } 116 if constant.Compare(x, token.EQL, c.Val()) { 117 for _, n := range cas.Body { 118 if n.Op() == ir.OFALL { 119 return // fallthrough makes it complicated - abort. 120 } 121 } 122 // This switch entry is the one that always triggers. 123 for _, cas2 := range n.Cases { 124 for _, c2 := range cas2.List { 125 if cas2 != cas || c2 != c { 126 ir.Visit(c2, markHiddenClosureDead) 127 } 128 } 129 if cas2 != cas { 130 ir.VisitList(cas2.Body, markHiddenClosureDead) 131 } 132 } 133 134 cas.List[0] = c 135 cas.List = cas.List[:1] 136 n.Cases[0] = cas 137 n.Cases = n.Cases[:1] 138 return 139 } 140 } 141 } 142 if def != nil { 143 for _, n := range def.Body { 144 if n.Op() == ir.OFALL { 145 return // fallthrough makes it complicated - abort. 146 } 147 } 148 for _, cas := range n.Cases { 149 if cas != def { 150 ir.VisitList(cas.List, markHiddenClosureDead) 151 ir.VisitList(cas.Body, markHiddenClosureDead) 152 } 153 } 154 n.Cases[0] = def 155 n.Cases = n.Cases[:1] 156 return 157 } 158 159 // TODO: handle case bodies ending with panic/return as we do in the IF case above. 160 161 // entire switch is a nop - no case ever triggers 162 for _, cas := range n.Cases { 163 ir.VisitList(cas.List, markHiddenClosureDead) 164 ir.VisitList(cas.Body, markHiddenClosureDead) 165 } 166 n.Cases = n.Cases[:0] 167 }() 168 } 169 170 if len(n.Init()) != 0 { 171 stmts(n.(ir.InitNode).PtrInit()) 172 } 173 switch n.Op() { 174 case ir.OBLOCK: 175 n := n.(*ir.BlockStmt) 176 stmts(&n.List) 177 case ir.OFOR: 178 n := n.(*ir.ForStmt) 179 stmts(&n.Body) 180 case ir.OIF: 181 n := n.(*ir.IfStmt) 182 stmts(&n.Body) 183 stmts(&n.Else) 184 case ir.ORANGE: 185 n := n.(*ir.RangeStmt) 186 stmts(&n.Body) 187 case ir.OSELECT: 188 n := n.(*ir.SelectStmt) 189 for _, cas := range n.Cases { 190 stmts(&cas.Body) 191 } 192 case ir.OSWITCH: 193 n := n.(*ir.SwitchStmt) 194 for _, cas := range n.Cases { 195 stmts(&cas.Body) 196 } 197 } 198 199 if cut { 200 ir.VisitList((*nn)[i+1:len(*nn)], markHiddenClosureDead) 201 *nn = (*nn)[:i+1] 202 break 203 } 204 } 205 } 206 207 func expr(n ir.Node) ir.Node { 208 // Perform dead-code elimination on short-circuited boolean 209 // expressions involving constants with the intent of 210 // producing a constant 'if' condition. 211 switch n.Op() { 212 case ir.OANDAND: 213 n := n.(*ir.LogicalExpr) 214 n.X = expr(n.X) 215 n.Y = expr(n.Y) 216 if ir.IsConst(n.X, constant.Bool) { 217 if ir.BoolVal(n.X) { 218 return n.Y // true && x => x 219 } else { 220 return n.X // false && x => false 221 } 222 } 223 case ir.OOROR: 224 n := n.(*ir.LogicalExpr) 225 n.X = expr(n.X) 226 n.Y = expr(n.Y) 227 if ir.IsConst(n.X, constant.Bool) { 228 if ir.BoolVal(n.X) { 229 return n.X // true || x => true 230 } else { 231 return n.Y // false || x => x 232 } 233 } 234 } 235 return n 236 } 237 238 func markHiddenClosureDead(n ir.Node) { 239 if n.Op() != ir.OCLOSURE { 240 return 241 } 242 clo := n.(*ir.ClosureExpr) 243 if clo.Func.IsHiddenClosure() { 244 clo.Func.SetIsDeadcodeClosure(true) 245 } 246 ir.VisitList(clo.Func.Body, markHiddenClosureDead) 247 }