1
0
mirror of https://github.com/golang/go synced 2024-11-25 19:07:57 -07:00

cmd/compile/internal/ssagen: provide intrinsicBuilders

Create an intrinsicBuilders type that has functions for adding and
looking up intrinsics. This makes the implementation more self contained,
readable and testable. Additionally, pass an *intrinsicBuildConfig to
initIntrinsics to improve testability without needing to modify package
level variables.

Change-Id: I0ee0a19c192dd6da9f1c5f1c29b98a3ad8161fe2
Reviewed-on: https://go-review.googlesource.com/c/go/+/605478
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Keith Randall <khr@google.com>
Auto-Submit: Joel Sing <joel@sing.id.au>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
Joel Sing 2024-08-04 04:45:14 +10:00 committed by Gopher Robot
parent 5d82dbb59c
commit dff15aa610
4 changed files with 138 additions and 39 deletions

View File

@ -15,7 +15,7 @@ import (
"cmd/internal/sys" "cmd/internal/sys"
) )
var intrinsics map[intrinsicKey]intrinsicBuilder var intrinsics intrinsicBuilders
// An intrinsicBuilder converts a call node n into an ssa value that // An intrinsicBuilder converts a call node n into an ssa value that
// implements that call as an intrinsic. args is a list of arguments to the func. // implements that call as an intrinsic. args is a list of arguments to the func.
@ -27,8 +27,80 @@ type intrinsicKey struct {
fn string fn string
} }
func initIntrinsics() { // intrinsicBuildConfig specifies the config to use for intrinsic building.
intrinsics = map[intrinsicKey]intrinsicBuilder{} type intrinsicBuildConfig struct {
instrumenting bool
go386 string
goamd64 int
goarm buildcfg.GoarmFeatures
goarm64 buildcfg.Goarm64Features
gomips string
gomips64 string
goppc64 int
goriscv64 int
}
type intrinsicBuilders map[intrinsicKey]intrinsicBuilder
// add adds the intrinsic builder b for pkg.fn for the given architecture.
func (ib intrinsicBuilders) add(arch *sys.Arch, pkg, fn string, b intrinsicBuilder) {
ib[intrinsicKey{arch, pkg, fn}] = b
}
// addForArchs adds the intrinsic builder b for pkg.fn for the given architectures.
func (ib intrinsicBuilders) addForArchs(pkg, fn string, b intrinsicBuilder, archs ...*sys.Arch) {
for _, arch := range archs {
ib.add(arch, pkg, fn, b)
}
}
// addForFamilies does the same as addForArchs but operates on architecture families.
func (ib intrinsicBuilders) addForFamilies(pkg, fn string, b intrinsicBuilder, archFamilies ...sys.ArchFamily) {
for _, arch := range sys.Archs {
if arch.InFamily(archFamilies...) {
intrinsics.add(arch, pkg, fn, b)
}
}
}
// alias aliases pkg.fn to targetPkg.targetFn for all architectures in archs
// for which targetPkg.targetFn already exists.
func (ib intrinsicBuilders) alias(pkg, fn, targetPkg, targetFn string, archs ...*sys.Arch) {
// TODO(jsing): Consider making this work even if the alias is added
// before the intrinsic.
aliased := false
for _, arch := range archs {
if b := intrinsics.lookup(arch, targetPkg, targetFn); b != nil {
intrinsics.add(arch, pkg, fn, b)
aliased = true
}
}
if !aliased {
panic(fmt.Sprintf("attempted to alias undefined intrinsic: %s.%s", pkg, fn))
}
}
// lookup looks up the intrinsic for a pkg.fn on the specified architecture.
func (ib intrinsicBuilders) lookup(arch *sys.Arch, pkg, fn string) intrinsicBuilder {
return intrinsics[intrinsicKey{arch, pkg, fn}]
}
func initIntrinsics(cfg *intrinsicBuildConfig) {
if cfg == nil {
cfg = &intrinsicBuildConfig{
instrumenting: base.Flag.Cfg.Instrumenting,
go386: buildcfg.GO386,
goamd64: buildcfg.GOAMD64,
goarm: buildcfg.GOARM,
goarm64: buildcfg.GOARM64,
gomips: buildcfg.GOMIPS,
gomips64: buildcfg.GOMIPS64,
goppc64: buildcfg.GOPPC64,
goriscv64: buildcfg.GORISCV64,
}
}
intrinsics = intrinsicBuilders{}
var p4 []*sys.Arch var p4 []*sys.Arch
var p8 []*sys.Arch var p8 []*sys.Arch
@ -45,36 +117,18 @@ func initIntrinsics() {
} }
all := sys.Archs[:] all := sys.Archs[:]
// add adds the intrinsic b for pkg.fn for the given list of architectures.
add := func(pkg, fn string, b intrinsicBuilder, archs ...*sys.Arch) { add := func(pkg, fn string, b intrinsicBuilder, archs ...*sys.Arch) {
for _, a := range archs { intrinsics.addForArchs(pkg, fn, b, archs...)
intrinsics[intrinsicKey{a, pkg, fn}] = b
}
} }
// addF does the same as add but operates on architecture families.
addF := func(pkg, fn string, b intrinsicBuilder, archFamilies ...sys.ArchFamily) { addF := func(pkg, fn string, b intrinsicBuilder, archFamilies ...sys.ArchFamily) {
for _, a := range sys.Archs { intrinsics.addForFamilies(pkg, fn, b, archFamilies...)
if a.InFamily(archFamilies...) {
intrinsics[intrinsicKey{a, pkg, fn}] = b
}
}
} }
// alias defines pkg.fn = pkg2.fn2 for all architectures in archs for which pkg2.fn2 exists.
alias := func(pkg, fn, pkg2, fn2 string, archs ...*sys.Arch) { alias := func(pkg, fn, pkg2, fn2 string, archs ...*sys.Arch) {
aliased := false intrinsics.alias(pkg, fn, pkg2, fn2, archs...)
for _, a := range archs {
if b, ok := intrinsics[intrinsicKey{a, pkg2, fn2}]; ok {
intrinsics[intrinsicKey{a, pkg, fn}] = b
aliased = true
}
}
if !aliased {
panic(fmt.Sprintf("attempted to alias undefined intrinsic: %s.%s", pkg, fn))
}
} }
/******** runtime ********/ /******** runtime ********/
if !base.Flag.Cfg.Instrumenting { if !cfg.instrumenting {
add("runtime", "slicebytetostringtmp", add("runtime", "slicebytetostringtmp",
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
// Compiler frontend optimizations emit OBYTES2STRTMP nodes // Compiler frontend optimizations emit OBYTES2STRTMP nodes
@ -125,7 +179,7 @@ func initIntrinsics() {
sys.ARM64, sys.PPC64, sys.RISCV64) sys.ARM64, sys.PPC64, sys.RISCV64)
brev_arch := []sys.ArchFamily{sys.AMD64, sys.I386, sys.ARM64, sys.ARM, sys.S390X} brev_arch := []sys.ArchFamily{sys.AMD64, sys.I386, sys.ARM64, sys.ARM, sys.S390X}
if buildcfg.GOPPC64 >= 10 { if cfg.goppc64 >= 10 {
// Use only on Power10 as the new byte reverse instructions that Power10 provide // Use only on Power10 as the new byte reverse instructions that Power10 provide
// make it worthwhile as an intrinsic // make it worthwhile as an intrinsic
brev_arch = append(brev_arch, sys.PPC64) brev_arch = append(brev_arch, sys.PPC64)
@ -258,7 +312,7 @@ func initIntrinsics() {
makeAtomicGuardedIntrinsicARM64common := func(op0, op1 ssa.Op, typ types.Kind, emit atomicOpEmitter, needReturn bool) intrinsicBuilder { makeAtomicGuardedIntrinsicARM64common := func(op0, op1 ssa.Op, typ types.Kind, emit atomicOpEmitter, needReturn bool) intrinsicBuilder {
return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
if buildcfg.GOARM64.LSE { if cfg.goarm64.LSE {
emit(s, n, args, op1, typ, needReturn) emit(s, n, args, op1, typ, needReturn)
} else { } else {
// Target Atomic feature is identified by dynamic detection // Target Atomic feature is identified by dynamic detection
@ -565,7 +619,7 @@ func initIntrinsics() {
return s.variable(n, types.Types[types.TFLOAT64]) return s.variable(n, types.Types[types.TFLOAT64])
} }
if buildcfg.GOAMD64 >= 3 { if cfg.goamd64 >= 3 {
return s.newValue3(ssa.OpFMA, types.Types[types.TFLOAT64], args[0], args[1], args[2]) return s.newValue3(ssa.OpFMA, types.Types[types.TFLOAT64], args[0], args[1], args[2])
} }
@ -631,7 +685,7 @@ func initIntrinsics() {
makeRoundAMD64 := func(op ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { makeRoundAMD64 := func(op ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
if buildcfg.GOAMD64 >= 2 { if cfg.goamd64 >= 2 {
return s.newValue1(op, types.Types[types.TFLOAT64], args[0]) return s.newValue1(op, types.Types[types.TFLOAT64], args[0])
} }
@ -732,7 +786,7 @@ func initIntrinsics() {
// ReverseBytes inlines correctly, no need to intrinsify it. // ReverseBytes inlines correctly, no need to intrinsify it.
// Nothing special is needed for targets where ReverseBytes16 lowers to a rotate // Nothing special is needed for targets where ReverseBytes16 lowers to a rotate
// On Power10, 16-bit rotate is not available so use BRH instruction // On Power10, 16-bit rotate is not available so use BRH instruction
if buildcfg.GOPPC64 >= 10 { if cfg.goppc64 >= 10 {
addF("math/bits", "ReverseBytes16", addF("math/bits", "ReverseBytes16",
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
return s.newValue1(ssa.OpBswap16, types.Types[types.TUINT], args[0]) return s.newValue1(ssa.OpBswap16, types.Types[types.TUINT], args[0])
@ -847,7 +901,7 @@ func initIntrinsics() {
makeOnesCountAMD64 := func(op ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { makeOnesCountAMD64 := func(op ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
if buildcfg.GOAMD64 >= 2 { if cfg.goamd64 >= 2 {
return s.newValue1(op, types.Types[types.TINT], args[0]) return s.newValue1(op, types.Types[types.TINT], args[0])
} }
@ -1032,7 +1086,7 @@ func findIntrinsic(sym *types.Sym) intrinsicBuilder {
return nil return nil
} }
} }
return intrinsics[intrinsicKey{Arch.LinkArch.Arch, pkg, fn}] return intrinsics.lookup(Arch.LinkArch.Arch, pkg, fn)
} }
func IsIntrinsicCall(n *ir.CallExpr) bool { func IsIntrinsicCall(n *ir.CallExpr) bool {

View File

@ -7,6 +7,8 @@ package ssagen
import ( import (
"internal/buildcfg" "internal/buildcfg"
"testing" "testing"
"cmd/internal/sys"
) )
type testIntrinsicKey struct { type testIntrinsicKey struct {
@ -1231,7 +1233,7 @@ var wantIntrinsicsPower10 = map[testIntrinsicKey]struct{}{
} }
func TestIntrinsics(t *testing.T) { func TestIntrinsics(t *testing.T) {
initIntrinsics() initIntrinsics(nil)
want := make(map[testIntrinsicKey]struct{}) want := make(map[testIntrinsicKey]struct{})
for ik, iv := range wantIntrinsics { for ik, iv := range wantIntrinsics {
@ -1258,3 +1260,46 @@ func TestIntrinsics(t *testing.T) {
} }
} }
} }
func TestIntrinsicBuilders(t *testing.T) {
cfg := &intrinsicBuildConfig{}
initIntrinsics(cfg)
for _, arch := range sys.Archs {
if intrinsics.lookup(arch, "runtime", "getcallersp") == nil {
t.Errorf("No intrinsic for runtime.getcallersp on arch %v", arch)
}
}
if intrinsics.lookup(sys.ArchAMD64, "runtime", "slicebytetostringtmp") == nil {
t.Error("No intrinsic for runtime.slicebytetostringtmp")
}
if intrinsics.lookup(sys.ArchRISCV64, "runtime", "publicationBarrier") == nil {
t.Errorf("No intrinsic for runtime.publicationBarrier on arch %v", sys.ArchRISCV64)
}
if intrinsics.lookup(sys.ArchAMD64, "internal/runtime/sys", "Bswap32") == nil {
t.Errorf("No intrinsic for internal/runtime/sys.Bswap32 on arch %v", sys.ArchAMD64)
}
if intrinsics.lookup(sys.ArchAMD64, "internal/runtime/sys", "Bswap64") == nil {
t.Errorf("No intrinsic for internal/runtime/sys.Bswap64 on arch %v", sys.ArchAMD64)
}
if intrinsics.lookup(sys.ArchPPC64, "internal/runtime/sys", "Bswap64") != nil {
t.Errorf("Found intrinsic for internal/runtime/sys.Bswap64 on arch %v", sys.ArchPPC64)
}
cfg.goppc64 = 10
cfg.instrumenting = true
initIntrinsics(cfg)
if intrinsics.lookup(sys.ArchAMD64, "runtime", "slicebytetostringtmp") != nil {
t.Error("Intrinsic incorrectly exists for runtime.slicebytetostringtmp")
}
if intrinsics.lookup(sys.ArchPPC64, "internal/runtime/sys", "Bswap64") == nil {
t.Errorf("No intrinsic for internal/runtime/sys.Bswap64 on arch %v", sys.ArchPPC64)
}
}

View File

@ -222,7 +222,7 @@ func InitConfig() {
} }
func InitTables() { func InitTables() {
initIntrinsics() initIntrinsics(nil)
} }
// AbiForBodylessFuncStackMap returns the ABI for a bodyless function's stack map. // AbiForBodylessFuncStackMap returns the ABI for a bodyless function's stack map.
@ -2037,7 +2037,7 @@ func (s *state) stmt(n ir.Node) {
if base.Flag.N == 0 && rtabi.UseInterfaceSwitchCache(Arch.LinkArch.Name) { if base.Flag.N == 0 && rtabi.UseInterfaceSwitchCache(Arch.LinkArch.Name) {
// Note: we can only use the cache if we have the right atomic load instruction. // Note: we can only use the cache if we have the right atomic load instruction.
// Double-check that here. // Double-check that here.
if _, ok := intrinsics[intrinsicKey{Arch.LinkArch.Arch, "internal/runtime/atomic", "Loadp"}]; !ok { if intrinsics.lookup(Arch.LinkArch.Arch, "internal/runtime/atomic", "Loadp") == nil {
s.Fatalf("atomic load not available") s.Fatalf("atomic load not available")
} }
merge = s.f.NewBlock(ssa.BlockPlain) merge = s.f.NewBlock(ssa.BlockPlain)
@ -5763,7 +5763,7 @@ func (s *state) dottype1(pos src.XPos, src, dst *types.Type, iface, source, targ
if base.Flag.N == 0 && rtabi.UseInterfaceSwitchCache(Arch.LinkArch.Name) { if base.Flag.N == 0 && rtabi.UseInterfaceSwitchCache(Arch.LinkArch.Name) {
// Note: we can only use the cache if we have the right atomic load instruction. // Note: we can only use the cache if we have the right atomic load instruction.
// Double-check that here. // Double-check that here.
if _, ok := intrinsics[intrinsicKey{Arch.LinkArch.Arch, "internal/runtime/atomic", "Loadp"}]; !ok { if intrinsics.lookup(Arch.LinkArch.Arch, "internal/runtime/atomic", "Loadp") == nil {
s.Fatalf("atomic load not available") s.Fatalf("atomic load not available")
} }
// Pick right size ops. // Pick right size ops.

View File

@ -70,12 +70,12 @@ func goamd64() int {
return int(defaultGOAMD64[len("v")] - '0') return int(defaultGOAMD64[len("v")] - '0')
} }
type goarmFeatures struct { type GoarmFeatures struct {
Version int Version int
SoftFloat bool SoftFloat bool
} }
func (g goarmFeatures) String() string { func (g GoarmFeatures) String() string {
armStr := strconv.Itoa(g.Version) armStr := strconv.Itoa(g.Version)
if g.SoftFloat { if g.SoftFloat {
armStr += ",softfloat" armStr += ",softfloat"
@ -85,7 +85,7 @@ func (g goarmFeatures) String() string {
return armStr return armStr
} }
func goarm() (g goarmFeatures) { func goarm() (g GoarmFeatures) {
const ( const (
softFloatOpt = ",softfloat" softFloatOpt = ",softfloat"
hardFloatOpt = ",hardfloat" hardFloatOpt = ",hardfloat"