From 59037ac93a49889eb6a7d6b3b8fbc70321615f1f Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 9 Aug 2023 04:05:35 -0700 Subject: cmd/compile: move early deadcode into unified writer This CL moves the early deadcode elimination pass into the unified writer. This allows shrinking the export data, by simplifying expressions and removing unreachable statements. It also means we don't need to repeatedly apply deadcode elimination on inlined calls or instantiated generics. Change-Id: I19bdb04861e50815fccdab39790f4aaa076121fd Reviewed-on: https://go-review.googlesource.com/c/go/+/517775 Reviewed-by: Michael Knyszek Reviewed-by: Cuong Manh Le Auto-Submit: Matthew Dempsky Run-TryBot: Matthew Dempsky Reviewed-by: Than McIntosh TryBot-Result: Gopher Robot --- src/cmd/compile/internal/deadcode/deadcode.go | 247 -------------------------- 1 file changed, 247 deletions(-) delete mode 100644 src/cmd/compile/internal/deadcode/deadcode.go (limited to 'src/cmd/compile/internal/deadcode/deadcode.go') diff --git a/src/cmd/compile/internal/deadcode/deadcode.go b/src/cmd/compile/internal/deadcode/deadcode.go deleted file mode 100644 index 46a2239f48..0000000000 --- a/src/cmd/compile/internal/deadcode/deadcode.go +++ /dev/null @@ -1,247 +0,0 @@ -// 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 { - ir.Visit(c2, markHiddenClosureDead) - } - if cas2 != cas { - ir.VisitList(cas2.Body, markHiddenClosureDead) - } - } - - // Rewrite to switch { case true: ... } - n.Tag = nil - cas.List[0] = ir.NewBool(c.Pos(), true) - 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) -} -- cgit v1.3-5-g9baa