aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/deadcode/deadcode.go
diff options
context:
space:
mode:
authorMatthew Dempsky <mdempsky@google.com>2023-08-09 04:05:35 -0700
committerGopher Robot <gobot@golang.org>2023-08-11 18:03:52 +0000
commit59037ac93a49889eb6a7d6b3b8fbc70321615f1f (patch)
treeae6897c814f010098508ea40f297ecbed12210ff /src/cmd/compile/internal/deadcode/deadcode.go
parent832212df9aba985bdc6a8378a821e1030554fa2f (diff)
downloadgo-59037ac93a49889eb6a7d6b3b8fbc70321615f1f.tar.xz
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 <mknyszek@google.com> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com> Auto-Submit: Matthew Dempsky <mdempsky@google.com> Run-TryBot: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
Diffstat (limited to 'src/cmd/compile/internal/deadcode/deadcode.go')
-rw-r--r--src/cmd/compile/internal/deadcode/deadcode.go247
1 files changed, 0 insertions, 247 deletions
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)
-}