mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.simd] cmd/compile: generate function body for bodyless intrinsics
For a compiler intrinsic, if it is used in a non-call context, e.g. as a function pointer, currently it requires fallback implementation (e.g. assembly code for atomic operations), otherwise it will result in a build failure. The fallback implementation needs to be maintained and tested, albeit rarely used in practice. Also, for SIMD, we're currently adding a large number of compiler intrinsics without providing fallback implementations (we might in the future). As methods, it is not unlikely that they are used in a non-call context, e.g. referenced from the type descriptor. This CL lets the compiler generate the function body for bodyless intrinsics. The compiler already recognizes a call to the function as an intrinsic and can directly generate code for it. So we just fill in the body with a call to the same function. Change-Id: I2636e3128f28301c9abaf2b48bc962ab56e7d1a9 Reviewed-on: https://go-review.googlesource.com/c/go/+/683096 Reviewed-by: David Chase <drchase@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
a8669c78f5
commit
88c013d6ff
5 changed files with 110 additions and 17 deletions
|
|
@ -29,7 +29,7 @@ var (
|
||||||
compilequeue []*ir.Func // functions waiting to be compiled
|
compilequeue []*ir.Func // functions waiting to be compiled
|
||||||
)
|
)
|
||||||
|
|
||||||
func enqueueFunc(fn *ir.Func) {
|
func enqueueFunc(fn *ir.Func, symABIs *ssagen.SymABIs) {
|
||||||
if ir.CurFunc != nil {
|
if ir.CurFunc != nil {
|
||||||
base.FatalfAt(fn.Pos(), "enqueueFunc %v inside %v", fn, ir.CurFunc)
|
base.FatalfAt(fn.Pos(), "enqueueFunc %v inside %v", fn, ir.CurFunc)
|
||||||
}
|
}
|
||||||
|
|
@ -49,22 +49,30 @@ func enqueueFunc(fn *ir.Func) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(fn.Body) == 0 {
|
if len(fn.Body) == 0 {
|
||||||
// Initialize ABI wrappers if necessary.
|
if ir.IsIntrinsicSym(fn.Sym()) && fn.Sym().Linkname == "" && !symABIs.HasDef(fn.Sym()) {
|
||||||
ir.InitLSym(fn, false)
|
// Generate the function body for a bodyless intrinsic, in case it
|
||||||
types.CalcSize(fn.Type())
|
// is used in a non-call context (e.g. as a function pointer).
|
||||||
a := ssagen.AbiForBodylessFuncStackMap(fn)
|
// We skip functions defined in assembly, or has a linkname (which
|
||||||
abiInfo := a.ABIAnalyzeFuncType(fn.Type()) // abiInfo has spill/home locations for wrapper
|
// could be defined in another package).
|
||||||
if fn.ABI == obj.ABI0 {
|
ssagen.GenIntrinsicBody(fn)
|
||||||
// The current args_stackmap generation assumes the function
|
} else {
|
||||||
// is ABI0, and only ABI0 assembly function can have a FUNCDATA
|
// Initialize ABI wrappers if necessary.
|
||||||
// reference to args_stackmap (see cmd/internal/obj/plist.go:Flushplist).
|
ir.InitLSym(fn, false)
|
||||||
// So avoid introducing an args_stackmap if the func is not ABI0.
|
types.CalcSize(fn.Type())
|
||||||
liveness.WriteFuncMap(fn, abiInfo)
|
a := ssagen.AbiForBodylessFuncStackMap(fn)
|
||||||
|
abiInfo := a.ABIAnalyzeFuncType(fn.Type()) // abiInfo has spill/home locations for wrapper
|
||||||
|
if fn.ABI == obj.ABI0 {
|
||||||
|
// The current args_stackmap generation assumes the function
|
||||||
|
// is ABI0, and only ABI0 assembly function can have a FUNCDATA
|
||||||
|
// reference to args_stackmap (see cmd/internal/obj/plist.go:Flushplist).
|
||||||
|
// So avoid introducing an args_stackmap if the func is not ABI0.
|
||||||
|
liveness.WriteFuncMap(fn, abiInfo)
|
||||||
|
|
||||||
x := ssagen.EmitArgInfo(fn, abiInfo)
|
x := ssagen.EmitArgInfo(fn, abiInfo)
|
||||||
objw.Global(x, int32(len(x.P)), obj.RODATA|obj.LOCAL)
|
objw.Global(x, int32(len(x.P)), obj.RODATA|obj.LOCAL)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
errorsBefore := base.Errors()
|
errorsBefore := base.Errors()
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,7 @@ func Main(archInit func(*ssagen.ArchInfo)) {
|
||||||
|
|
||||||
ir.EscFmt = escape.Fmt
|
ir.EscFmt = escape.Fmt
|
||||||
ir.IsIntrinsicCall = ssagen.IsIntrinsicCall
|
ir.IsIntrinsicCall = ssagen.IsIntrinsicCall
|
||||||
|
ir.IsIntrinsicSym = ssagen.IsIntrinsicSym
|
||||||
inline.SSADumpInline = ssagen.DumpInline
|
inline.SSADumpInline = ssagen.DumpInline
|
||||||
ssagen.InitEnv()
|
ssagen.InitEnv()
|
||||||
ssagen.InitTables()
|
ssagen.InitTables()
|
||||||
|
|
@ -304,7 +305,7 @@ func Main(archInit func(*ssagen.ArchInfo)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if nextFunc < len(typecheck.Target.Funcs) {
|
if nextFunc < len(typecheck.Target.Funcs) {
|
||||||
enqueueFunc(typecheck.Target.Funcs[nextFunc])
|
enqueueFunc(typecheck.Target.Funcs[nextFunc], symABIs)
|
||||||
nextFunc++
|
nextFunc++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1022,6 +1022,9 @@ func StaticCalleeName(n Node) *Name {
|
||||||
// IsIntrinsicCall reports whether the compiler back end will treat the call as an intrinsic operation.
|
// IsIntrinsicCall reports whether the compiler back end will treat the call as an intrinsic operation.
|
||||||
var IsIntrinsicCall = func(*CallExpr) bool { return false }
|
var IsIntrinsicCall = func(*CallExpr) bool { return false }
|
||||||
|
|
||||||
|
// IsIntrinsicSym reports whether the compiler back end will treat a call to this symbol as an intrinsic operation.
|
||||||
|
var IsIntrinsicSym = func(*types.Sym) bool { return false }
|
||||||
|
|
||||||
// SameSafeExpr checks whether it is safe to reuse one of l and r
|
// SameSafeExpr checks whether it is safe to reuse one of l and r
|
||||||
// instead of computing both. SameSafeExpr assumes that l and r are
|
// instead of computing both. SameSafeExpr assumes that l and r are
|
||||||
// used in the same statement or expression. In order for it to be
|
// used in the same statement or expression. In order for it to be
|
||||||
|
|
@ -1140,6 +1143,14 @@ func ParamNames(ft *types.Type) []Node {
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RecvParamNames(ft *types.Type) []Node {
|
||||||
|
args := make([]Node, ft.NumRecvs()+ft.NumParams())
|
||||||
|
for i, f := range ft.RecvParams() {
|
||||||
|
args[i] = f.Nname.(*Name)
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
// MethodSym returns the method symbol representing a method name
|
// MethodSym returns the method symbol representing a method name
|
||||||
// associated with a specific receiver type.
|
// associated with a specific receiver type.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,18 @@ func (s *SymABIs) ReadSymABIs(file string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasDef returns whether the given symbol has an assembly definition.
|
||||||
|
func (s *SymABIs) HasDef(sym *types.Sym) bool {
|
||||||
|
symName := sym.Linkname
|
||||||
|
if symName == "" {
|
||||||
|
symName = sym.Pkg.Prefix + "." + sym.Name
|
||||||
|
}
|
||||||
|
symName = s.canonicalize(symName)
|
||||||
|
|
||||||
|
_, hasDefABI := s.defs[symName]
|
||||||
|
return hasDefABI
|
||||||
|
}
|
||||||
|
|
||||||
// GenABIWrappers applies ABI information to Funcs and generates ABI
|
// GenABIWrappers applies ABI information to Funcs and generates ABI
|
||||||
// wrapper functions where necessary.
|
// wrapper functions where necessary.
|
||||||
func (s *SymABIs) GenABIWrappers() {
|
func (s *SymABIs) GenABIWrappers() {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"cmd/compile/internal/base"
|
"cmd/compile/internal/base"
|
||||||
"cmd/compile/internal/ir"
|
"cmd/compile/internal/ir"
|
||||||
"cmd/compile/internal/ssa"
|
"cmd/compile/internal/ssa"
|
||||||
|
"cmd/compile/internal/typecheck"
|
||||||
"cmd/compile/internal/types"
|
"cmd/compile/internal/types"
|
||||||
"cmd/internal/sys"
|
"cmd/internal/sys"
|
||||||
)
|
)
|
||||||
|
|
@ -1751,5 +1752,65 @@ func IsIntrinsicCall(n *ir.CallExpr) bool {
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return findIntrinsic(name.Sym()) != nil
|
return IsIntrinsicSym(name.Sym())
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsIntrinsicSym(sym *types.Sym) bool {
|
||||||
|
return findIntrinsic(sym) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenIntrinsicBody generates the function body for a bodyless intrinsic.
|
||||||
|
// This is used when the intrinsic is used in a non-call context, e.g.
|
||||||
|
// as a function pointer, or (for a method) being referenced from the type
|
||||||
|
// descriptor.
|
||||||
|
//
|
||||||
|
// The compiler already recognizes a call to fn as an intrinsic and can
|
||||||
|
// directly generate code for it. So we just fill in the body with a call
|
||||||
|
// to fn.
|
||||||
|
func GenIntrinsicBody(fn *ir.Func) {
|
||||||
|
if ir.CurFunc != nil {
|
||||||
|
base.FatalfAt(fn.Pos(), "enqueueFunc %v inside %v", fn, ir.CurFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
if base.Flag.LowerR != 0 {
|
||||||
|
fmt.Println("generate intrinsic for", ir.FuncName(fn))
|
||||||
|
}
|
||||||
|
|
||||||
|
pos := fn.Pos()
|
||||||
|
ft := fn.Type()
|
||||||
|
var ret ir.Node
|
||||||
|
|
||||||
|
// For a method, it usually starts with an ODOTMETH (pre-typecheck) or
|
||||||
|
// OMETHEXPR (post-typecheck) referencing the method symbol without the
|
||||||
|
// receiver type, and Walk rewrites it to a call directly to the
|
||||||
|
// type-qualified method symbol, moving the receiver to an argument.
|
||||||
|
// Here fn has already the type-qualified method symbol, and it is hard
|
||||||
|
// to get the unqualified symbol. So we just generate the post-Walk form
|
||||||
|
// and mark it typechecked and Walked.
|
||||||
|
call := ir.NewCallExpr(pos, ir.OCALLFUNC, fn.Nname, nil)
|
||||||
|
call.Args = ir.RecvParamNames(ft)
|
||||||
|
call.IsDDD = ft.IsVariadic()
|
||||||
|
typecheck.Exprs(call.Args)
|
||||||
|
call.SetTypecheck(1)
|
||||||
|
call.SetWalked(true)
|
||||||
|
ret = call
|
||||||
|
if ft.NumResults() > 0 {
|
||||||
|
if ft.NumResults() == 1 {
|
||||||
|
call.SetType(ft.Result(0).Type)
|
||||||
|
} else {
|
||||||
|
call.SetType(ft.ResultsTuple())
|
||||||
|
}
|
||||||
|
n := ir.NewReturnStmt(base.Pos, nil)
|
||||||
|
n.Results = []ir.Node{call}
|
||||||
|
ret = n
|
||||||
|
}
|
||||||
|
fn.Body.Append(ret)
|
||||||
|
|
||||||
|
if base.Flag.LowerR != 0 {
|
||||||
|
ir.DumpList("generate intrinsic body", fn.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
ir.CurFunc = fn
|
||||||
|
typecheck.Stmts(fn.Body)
|
||||||
|
ir.CurFunc = nil // we know CurFunc is nil at entry
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue