Skip to content

Commit 103f374

Browse files
committed
cmd/compile: ensure first instruction in a function is not inlined
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 <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Heschi Kreinick <[email protected]> Reviewed-by: David Chase <[email protected]>
1 parent 55bd193 commit 103f374

File tree

3 files changed

+75
-0
lines changed

3 files changed

+75
-0
lines changed

src/cmd/compile/internal/ssagen/ssa.go

+44
Original file line numberDiff line numberDiff line change
@@ -7120,6 +7120,8 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
71207120
}
71217121

71227122
if inlMarks != nil {
7123+
hasCall := false
7124+
71237125
// We have some inline marks. Try to find other instructions we're
71247126
// going to emit anyway, and use those instructions instead of the
71257127
// inline marks.
@@ -7137,6 +7139,9 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
71377139
// whether they will be zero-sized or not yet.
71387140
continue
71397141
}
7142+
if p.As == obj.ACALL || p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
7143+
hasCall = true
7144+
}
71407145
pos := p.Pos.AtColumn1()
71417146
s := inlMarksByPos[pos]
71427147
if len(s) == 0 {
@@ -7162,6 +7167,45 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
71627167
pp.CurFunc.LSym.Func().AddInlMark(p, inlMarks[p])
71637168
}
71647169
}
7170+
7171+
if e.stksize == 0 && !hasCall {
7172+
// Frameless leaf function. It doesn't need any preamble,
7173+
// so make sure its first instruction isn't from an inlined callee.
7174+
// If it is, add a nop at the start of the function with a position
7175+
// equal to the start of the function.
7176+
// This ensures that runtime.FuncForPC(uintptr(reflect.ValueOf(fn).Pointer())).Name()
7177+
// returns the right answer. See issue 58300.
7178+
for p := pp.Text; p != nil; p = p.Link {
7179+
if p.As == obj.AFUNCDATA || p.As == obj.APCDATA || p.As == obj.ATEXT {
7180+
continue
7181+
}
7182+
if base.Ctxt.PosTable.Pos(p.Pos).Base().InliningIndex() >= 0 {
7183+
// Make a real (not 0-sized) nop.
7184+
nop := Arch.Ginsnop(pp)
7185+
nop.Pos = e.curfn.Pos().WithIsStmt()
7186+
7187+
// Unfortunately, Ginsnop puts the instruction at the
7188+
// end of the list. Move it up to just before p.
7189+
7190+
// Unlink from the current list.
7191+
for x := pp.Text; x != nil; x = x.Link {
7192+
if x.Link == nop {
7193+
x.Link = nop.Link
7194+
break
7195+
}
7196+
}
7197+
// Splice in right before p.
7198+
for x := pp.Text; x != nil; x = x.Link {
7199+
if x.Link == p {
7200+
nop.Link = p
7201+
x.Link = nop
7202+
break
7203+
}
7204+
}
7205+
}
7206+
break
7207+
}
7208+
}
71657209
}
71667210

71677211
if base.Ctxt.Flag_locationlists {

test/fixedbugs/issue58300.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// run
2+
3+
// Copyright 2023 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package main
8+
9+
import (
10+
"reflect"
11+
"runtime"
12+
)
13+
14+
func f(n int) int {
15+
return n % 2
16+
}
17+
18+
func g(n int) int {
19+
return f(n)
20+
}
21+
22+
func name(fn any) (res string) {
23+
return runtime.FuncForPC(uintptr(reflect.ValueOf(fn).Pointer())).Name()
24+
}
25+
26+
func main() {
27+
println(name(f))
28+
println(name(g))
29+
}

test/fixedbugs/issue58300.out

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
main.f
2+
main.g

0 commit comments

Comments
 (0)