]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/compile: better code generation for constant-fold switch
authorCuong Manh Le <cuong.manhle.vn@gmail.com>
Thu, 13 Apr 2023 07:51:05 +0000 (14:51 +0700)
committerCuong Manh Le <cuong.manhle.vn@gmail.com>
Fri, 14 Apr 2023 17:58:01 +0000 (17:58 +0000)
CL 399694 added constant-fold switch early in compilation. So function:

func f() string {
    switch intSize {
    case 32:
        return "32"
    case 64:
        return "64"
    default:
        panic("unreachable")
    }
}

will be constant-fold to:

func f() string {
    switch intSize {
    case 64:
        return "64"
    }
}

When this function get inlined, there is a check whether we can delay
declaring the result parameter until the "return" statement. For the
original function, we can't delay the result, because there's more than
one return statement. However, the constant-fold one can, because
there's on one return statement in the body now. The result parameter
~R0 ends up declaring inside the switch statement scope.

Now, when walking the switch statement, it's re-written into if-else
statement. Without typecheck.EvalConst, the if condition "if 64 == 64"
is passed as-is to the ssa generation pass. Because "64 == 64" is not a
constant, the ssagen creates normal blocks for branching the results.
This confuses the liveness analysis, because ~R0 is only live inside the
if block. With typecheck.EvalConst, "64 == 64" is evaluated to "true",
so ssagen can branch the result without emitting conditional blocks.

Instead, the constant-fold can be re-written as:

switch {
case true:
    // Body
}

So it does not depend on the delay results check during inlining. Adding
a test, which will fail when typecheck.EvalConst is removed, so we can
do the cleanup without breaking things.

Change-Id: I638730bb147140de84260653741431b807ff2f15
Reviewed-on: https://go-review.googlesource.com/c/go/+/484316
Reviewed-by: Keith Randall <khr@google.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>

src/cmd/compile/internal/deadcode/deadcode.go
src/cmd/compile/internal/inline/inl.go
test/inline.go

index decd2611836fb159d38a338e0f123ec553e0101f..46a2239f48177a2eb23308529805a6b5e76b78f8 100644 (file)
@@ -122,16 +122,16 @@ func stmts(nn *ir.Nodes) {
                                                        // 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)
-                                                                       }
+                                                                       ir.Visit(c2, markHiddenClosureDead)
                                                                }
                                                                if cas2 != cas {
                                                                        ir.VisitList(cas2.Body, markHiddenClosureDead)
                                                                }
                                                        }
 
-                                                       cas.List[0] = c
+                                                       // 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]
index d030a822fc252fe406ec4edb978e192ec4593321..1a65e16f5199f6a139db27470d1c3bde12907c11 100644 (file)
@@ -1035,7 +1035,7 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, bigCaller bool, inlCalls *[]*ir.Inli
        if ok, maxCost := inlineCostOK(n, ir.CurFunc, fn, bigCaller); !ok {
                if logopt.Enabled() {
                        logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc),
-                       fmt.Sprintf("cost %d of %s exceeds max caller cost %d", fn.Inl.Cost, ir.PkgFuncName(fn), maxCost))
+                               fmt.Sprintf("cost %d of %s exceeds max caller cost %d", fn.Inl.Cost, ir.PkgFuncName(fn), maxCost))
                }
                return n
        }
index 45a6255efc62809fb1432851b0e6ae86f1ba6dde..af39ad8cb5a6aef283a190263ac4f9db12068ba8 100644 (file)
@@ -195,6 +195,20 @@ func switchConst3() string { // ERROR "can inline switchConst3"
                return "oh nose!"
        }
 }
+func switchConst4() { // ERROR "can inline switchConst4"
+       const intSize = 32 << (^uint(0) >> 63)
+       want := func() string { // ERROR "can inline switchConst4.func1"
+               switch intSize {
+               case 32:
+                       return "32"
+               case 64:
+                       return "64"
+               default:
+                       panic("unreachable")
+               }
+       }() // ERROR "inlining call to switchConst4.func1"
+       _ = want
+}
 
 func inlineRangeIntoMe(data []int) { // ERROR "can inline inlineRangeIntoMe" "data does not escape"
        rangeFunc(data, 12) // ERROR "inlining call to rangeFunc"