[dev.regabi] cmd/compile: added limited //go:registerparams pragma for new ABI dev

This only works for functions; if you try it with a method, it will
fail.  It does work for both local package and imports.  For now,
it tells you when it thinks it sees either a declaration or a call of
such a function (this will normally be silent since no existing
code uses this pragma).

Note: it appears to be really darn hard to figure out if this
pragma was set for a method, and the method's call site.  Better
ir.Node wranglers than I might be able to make headway, but it
seemed unnecessary for this experiment.

Change-Id: I601c2ddd124457bf6d62f714d7ac871705743c0a
Reviewed-on: https://go-review.googlesource.com/c/go/+/279521
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
This commit is contained in:
David Chase 2021-01-04 13:32:10 -05:00
parent c1370e918f
commit 861707a8c8
10 changed files with 98 additions and 1 deletions

View File

@ -452,6 +452,9 @@ const (
// Go command pragmas
GoBuildPragma
RegisterParams // TODO remove after register abi is working
)
func AsNode(n types.Object) Node {

View File

@ -28,6 +28,7 @@ const (
ir.Nosplit |
ir.Noinline |
ir.NoCheckPtr |
ir.RegisterParams | // TODO remove after register abi is working
ir.CgoUnsafeArgs |
ir.UintptrEscapes |
ir.Systemstack |
@ -79,6 +80,8 @@ func pragmaFlag(verb string) ir.PragmaFlag {
// in the argument list.
// Used in syscall/dll_windows.go.
return ir.UintptrEscapes
case "go:registerparams": // TODO remove after register abi is working
return ir.RegisterParams
case "go:notinheap":
return ir.NotInHeap
}

View File

@ -356,6 +356,13 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func {
if fn.Pragma&ir.Nosplit != 0 {
s.f.NoSplit = true
}
if fn.Pragma&ir.RegisterParams != 0 { // TODO remove after register abi is working
if strings.Contains(name, ".") {
base.ErrorfAt(fn.Pos(), "Calls to //go:registerparams method %s won't work, remove the pragma from the declaration.", name)
}
s.f.Warnl(fn.Pos(), "Declared function %s has register params", name)
}
s.panics = map[funcLine]*ssa.Block{}
s.softFloat = s.config.SoftFloat
@ -4685,6 +4692,7 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
}
testLateExpansion := false
inRegisters := false
switch n.Op() {
case ir.OCALLFUNC:
@ -4692,6 +4700,13 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
if k == callNormal && fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC {
fn := fn.(*ir.Name)
sym = fn.Sym()
// TODO remove after register abi is working
inRegistersImported := fn.Pragma()&ir.RegisterParams != 0
inRegistersSamePackage := fn.Func != nil && fn.Func.Pragma&ir.RegisterParams != 0
inRegisters = inRegistersImported || inRegistersSamePackage
if inRegisters {
s.f.Warnl(n.Pos(), "Called function %s has register params", sym.Linksym().Name)
}
break
}
closure = s.expr(fn)

View File

@ -976,6 +976,9 @@ func (w *exportWriter) funcExt(n *ir.Name) {
w.linkname(n.Sym())
w.symIdx(n.Sym())
// TODO remove after register abi is working.
w.uint64(uint64(n.Func.Pragma))
// Escape analysis.
for _, fs := range &types.RecvsParams {
for _, f := range fs(n.Type()).FieldSlice() {

View File

@ -647,6 +647,9 @@ func (r *importReader) funcExt(n *ir.Name) {
r.linkname(n.Sym())
r.symIdx(n.Sym())
// TODO remove after register abi is working
n.SetPragma(ir.PragmaFlag(r.uint64()))
// Escape analysis.
for _, fs := range &types.RecvsParams {
for _, f := range fs(n.Type()).FieldSlice() {

View File

@ -0,0 +1,36 @@
// Copyright 2021 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 (
"fmt"
"regabipragma.dir/tmp"
)
type S string
//go:noinline
func (s S) ff(t string) string {
return string(s) + " " + t
}
//go:noinline
//go:registerparams
func f(s,t string) string { // ERROR "Declared function f has register params"
return s + " " + t
}
func check(s string) {
if s != "Hello world!" {
fmt.Printf("FAIL, wanted 'Hello world!' but got '%s'\n", s)
}
}
func main() {
check(f("Hello", "world!")) // ERROR "Called function ...f has register params"
check(tmp.F("Hello", "world!")) // ERROR "Called function regabipragma.dir/tmp.F has register params"
check(S("Hello").ff("world!"))
check(tmp.S("Hello").FF("world!"))
}

View File

@ -0,0 +1,19 @@
// Copyright 2021 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 tmp
type S string
//go:noinline
func (s S) FF(t string) string {
return string(s) + " " + t
}
//go:noinline
//go:registerparams
func F(s,t string) string {
return s + " " + t
}

9
test/abi/regabipragma.go Normal file
View File

@ -0,0 +1,9 @@
// runindir
// Copyright 2021 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.
// TODO May delete or adapt this test once regabi is the default
package ignore

View File

@ -0,0 +1,6 @@
# regabipragma.dir/tmp
tmp/foo.go:17:6: Declared function F has register params
# regabipragma.dir
./main.go:21:6: Declared function f has register params
./main.go:32:9: Called function "".f has register params
./main.go:33:13: Called function regabipragma.dir/tmp.F has register params

View File

@ -59,7 +59,7 @@ var (
// dirs are the directories to look for *.go files in.
// TODO(bradfitz): just use all directories?
dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "codegen", "runtime"}
dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "codegen", "runtime", "abi"}
// ratec controls the max number of tests running at a time.
ratec chan bool