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  }