aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/deadcode/deadcode.go
diff options
context:
space:
mode:
authorKeith Randall <khr@golang.org>2022-02-06 23:25:04 -0800
committerKeith Randall <khr@golang.org>2022-04-14 19:37:30 +0000
commit01b9ae22ed3c0f9c9ea29adbcd23bd97de6d18dd (patch)
treed4094cc6e7ae327852ff299b639dc1810391983d /src/cmd/compile/internal/deadcode/deadcode.go
parent1ba96d8c0909eca59e28c048150c3834982f79fb (diff)
downloadgo-01b9ae22ed3c0f9c9ea29adbcd23bd97de6d18dd.tar.xz
cmd/compile: constant-fold switches early in compilation
So that the inliner knows all the other cases are dead and doesn't accumulate any cost for them. The canonical case for this is switching on runtime.GOOS, which occurs several places in the stdlib. Fixes #50253 Change-Id: I44823aaebb6c1b03c9b0c12d10086db81954350f Reviewed-on: https://go-review.googlesource.com/c/go/+/399694 Run-TryBot: Keith Randall <khr@golang.org> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com> Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Russ Cox <rsc@golang.org> 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.go80
1 files changed, 80 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/deadcode/deadcode.go b/src/cmd/compile/internal/deadcode/deadcode.go
index c37a5a6990..decd261183 100644
--- a/src/cmd/compile/internal/deadcode/deadcode.go
+++ b/src/cmd/compile/internal/deadcode/deadcode.go
@@ -6,6 +6,7 @@ package deadcode
import (
"go/constant"
+ "go/token"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
@@ -86,6 +87,85 @@ func stmts(nn *ir.Nodes) {
}
}
}
+ 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())