]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/compile: ensure first instruction in a function is not inlined
authorKeith Randall <khr@golang.org>
Fri, 3 Feb 2023 17:59:55 +0000 (09:59 -0800)
committerKeith Randall <khr@golang.org>
Mon, 6 Feb 2023 20:39:54 +0000 (20:39 +0000)
People are using this to get the name of the function from a function type:

runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()

Unfortunately, this technique falls down when the first instruction
of the function is from an inlined callee. Then the expression above
gets you the name of the inlined function instead of the function itself.

To fix this, ensure that the first instruction is never from an inlinee.
Normally functions have prologs so those are already fine. In just the
cases where a function is a leaf with no local variables, and an instruction
from an inlinee appears first in the prog list, add a nop at the start
of the function to hold a non-inlined position.

Consider the nop a "mini-prolog" for leaf functions.

Fixes #58300

Change-Id: Ie37092f4ac3167fe8e5ef4a2207b14abc1786897
Reviewed-on: https://go-review.googlesource.com/c/go/+/465076
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: David Chase <drchase@google.com>
src/cmd/compile/internal/ssagen/ssa.go
test/fixedbugs/issue58300.go [new file with mode: 0644]
test/fixedbugs/issue58300.out [new file with mode: 0644]

index 48f813a48fee579ee098c19de95ac4e5750d76c6..f31cf29925e83be05a3e8d7d369736fb0549752d 100644 (file)
@@ -7120,6 +7120,8 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
        }
 
        if inlMarks != nil {
+               hasCall := false
+
                // We have some inline marks. Try to find other instructions we're
                // going to emit anyway, and use those instructions instead of the
                // inline marks.
@@ -7137,6 +7139,9 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
                                // whether they will be zero-sized or not yet.
                                continue
                        }
+                       if p.As == obj.ACALL || p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
+                               hasCall = true
+                       }
                        pos := p.Pos.AtColumn1()
                        s := inlMarksByPos[pos]
                        if len(s) == 0 {
@@ -7162,6 +7167,45 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
                                pp.CurFunc.LSym.Func().AddInlMark(p, inlMarks[p])
                        }
                }
+
+               if e.stksize == 0 && !hasCall {
+                       // Frameless leaf function. It doesn't need any preamble,
+                       // so make sure its first instruction isn't from an inlined callee.
+                       // If it is, add a nop at the start of the function with a position
+                       // equal to the start of the function.
+                       // This ensures that runtime.FuncForPC(uintptr(reflect.ValueOf(fn).Pointer())).Name()
+                       // returns the right answer. See issue 58300.
+                       for p := pp.Text; p != nil; p = p.Link {
+                               if p.As == obj.AFUNCDATA || p.As == obj.APCDATA || p.As == obj.ATEXT {
+                                       continue
+                               }
+                               if base.Ctxt.PosTable.Pos(p.Pos).Base().InliningIndex() >= 0 {
+                                       // Make a real (not 0-sized) nop.
+                                       nop := Arch.Ginsnop(pp)
+                                       nop.Pos = e.curfn.Pos().WithIsStmt()
+
+                                       // Unfortunately, Ginsnop puts the instruction at the
+                                       // end of the list. Move it up to just before p.
+
+                                       // Unlink from the current list.
+                                       for x := pp.Text; x != nil; x = x.Link {
+                                               if x.Link == nop {
+                                                       x.Link = nop.Link
+                                                       break
+                                               }
+                                       }
+                                       // Splice in right before p.
+                                       for x := pp.Text; x != nil; x = x.Link {
+                                               if x.Link == p {
+                                                       nop.Link = p
+                                                       x.Link = nop
+                                                       break
+                                               }
+                                       }
+                               }
+                               break
+                       }
+               }
        }
 
        if base.Ctxt.Flag_locationlists {
diff --git a/test/fixedbugs/issue58300.go b/test/fixedbugs/issue58300.go
new file mode 100644 (file)
index 0000000..fff3d21
--- /dev/null
@@ -0,0 +1,29 @@
+// run
+
+// Copyright 2023 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 main
+
+import (
+       "reflect"
+       "runtime"
+)
+
+func f(n int) int {
+       return n % 2
+}
+
+func g(n int) int {
+       return f(n)
+}
+
+func name(fn any) (res string) {
+       return runtime.FuncForPC(uintptr(reflect.ValueOf(fn).Pointer())).Name()
+}
+
+func main() {
+       println(name(f))
+       println(name(g))
+}
diff --git a/test/fixedbugs/issue58300.out b/test/fixedbugs/issue58300.out
new file mode 100644 (file)
index 0000000..f347287
--- /dev/null
@@ -0,0 +1,2 @@
+main.f
+main.g