]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: function "property" defs for inl heuristics
authorThan McIntosh <thanm@google.com>
Thu, 29 Jun 2023 17:43:50 +0000 (13:43 -0400)
committerThan McIntosh <thanm@google.com>
Thu, 10 Aug 2023 18:52:53 +0000 (18:52 +0000)
Add definitions for a set of Go function "properties" intended to be
useful for driving inlining decisions. This CL just defines a set of
flags and a container to hold them; a subsequent CL will add code to
compute the properties for a function given its IR/AST representation.

Updates #61502.

Change-Id: Ifa26c1ad055c02ca0ce9cf37078cee7b3385e18a
Reviewed-on: https://go-review.googlesource.com/c/go/+/511556
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Than McIntosh <thanm@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
src/cmd/compile/internal/inline/inlheur/funcprop_string.go [new file with mode: 0644]
src/cmd/compile/internal/inline/inlheur/funcpropbits_string.go [new file with mode: 0644]
src/cmd/compile/internal/inline/inlheur/function_properties.go [new file with mode: 0644]
src/cmd/compile/internal/inline/inlheur/parampropbits_string.go [new file with mode: 0644]
src/cmd/compile/internal/inline/inlheur/resultpropbits_string.go [new file with mode: 0644]
src/cmd/compile/internal/inline/inlheur/serialize.go [new file with mode: 0644]
src/cmd/compile/internal/inline/inlheur/tserial_test.go [new file with mode: 0644]

diff --git a/src/cmd/compile/internal/inline/inlheur/funcprop_string.go b/src/cmd/compile/internal/inline/inlheur/funcprop_string.go
new file mode 100644 (file)
index 0000000..d16e4d3
--- /dev/null
@@ -0,0 +1,44 @@
+// 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 inlheur
+
+import (
+       "fmt"
+       "strings"
+)
+
+func (fp *FuncProps) String() string {
+       return fp.ToString("")
+}
+
+func (fp *FuncProps) ToString(prefix string) string {
+       var sb strings.Builder
+       if fp.Flags != 0 {
+               fmt.Fprintf(&sb, "%sFlags %s\n", prefix, fp.Flags)
+       }
+       flagSliceToSB[ParamPropBits](&sb, fp.ParamFlags,
+               prefix, "ParamFlags")
+       flagSliceToSB[ResultPropBits](&sb, fp.ResultFlags,
+               prefix, "ResultFlags")
+       return sb.String()
+}
+
+func flagSliceToSB[T interface {
+       ~uint32
+       String() string
+}](sb *strings.Builder, sl []T, prefix string, tag string) {
+       var sb2 strings.Builder
+       foundnz := false
+       fmt.Fprintf(&sb2, "%s%s\n", prefix, tag)
+       for i, e := range sl {
+               if e != 0 {
+                       foundnz = true
+               }
+               fmt.Fprintf(&sb2, "%s  %d %s\n", prefix, i, e.String())
+       }
+       if foundnz {
+               sb.WriteString(sb2.String())
+       }
+}
diff --git a/src/cmd/compile/internal/inline/inlheur/funcpropbits_string.go b/src/cmd/compile/internal/inline/inlheur/funcpropbits_string.go
new file mode 100644 (file)
index 0000000..28de4a9
--- /dev/null
@@ -0,0 +1,58 @@
+// 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.
+
+// Code generated by "stringer -bitset -type FuncPropBits"; DO NOT EDIT.
+
+package inlheur
+
+import (
+       "bytes"
+       "strconv"
+)
+
+func _() {
+       // An "invalid array index" compiler error signifies that the constant values have changed.
+       // Re-run the stringer command to generate them again.
+       var x [1]struct{}
+       _ = x[FuncPropNeverReturns-1]
+}
+
+var _FuncPropBits_value = [...]uint64{
+       0x1, /* FuncPropNeverReturns */
+}
+
+const _FuncPropBits_name = "FuncPropNeverReturns"
+
+var _FuncPropBits_index = [...]uint8{0, 20}
+
+func (i FuncPropBits) String() string {
+       var b bytes.Buffer
+
+       remain := uint64(i)
+       seen := false
+
+       for k, v := range _FuncPropBits_value {
+               x := _FuncPropBits_name[_FuncPropBits_index[k]:_FuncPropBits_index[k+1]]
+               if v == 0 {
+                       if i == 0 {
+                               b.WriteString(x)
+                               return b.String()
+                       }
+                       continue
+               }
+               if (v & remain) == v {
+                       remain &^= v
+                       x := _FuncPropBits_name[_FuncPropBits_index[k]:_FuncPropBits_index[k+1]]
+                       if seen {
+                               b.WriteString("|")
+                       }
+                       seen = true
+                       b.WriteString(x)
+               }
+       }
+       if remain == 0 {
+               return b.String()
+       }
+       return "FuncPropBits(0x" + strconv.FormatInt(int64(i), 16) + ")"
+}
diff --git a/src/cmd/compile/internal/inline/inlheur/function_properties.go b/src/cmd/compile/internal/inline/inlheur/function_properties.go
new file mode 100644 (file)
index 0000000..b90abf9
--- /dev/null
@@ -0,0 +1,98 @@
+// 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 inlheur
+
+// This file defines a set of Go function "properties" intended to
+// guide inlining heuristics; these properties may apply to the
+// function as a whole, or to one or more function return values or
+// parameters.
+//
+// IMPORTANT: function properties are produced on a "best effort"
+// basis, meaning that the code that computes them doesn't verify that
+// the properties are guaranteed to be true in 100% of cases. For this
+// reason, properties should only be used to drive always-safe
+// optimization decisions (e.g. "should I inline this call", or
+// "should I unroll this loop") as opposed to potentially unsafe IR
+// alterations that could change program semantics (e.g. "can I delete
+// this variable" or "can I move this statement to a new location").
+//
+//----------------------------------------------------------------
+
+// FuncProps describes a set of function or method properties that may
+// be useful for inlining heuristics. Here 'Flags' are properties that
+// we think apply to the entire function; 'RecvrParamFlags' are
+// properties of specific function params (or the receiver), and
+// 'ResultFlags' are things properties we think will apply to values
+// of specific results. Note that 'ParamFlags' includes and entry for
+// the receiver if applicable, and does include etries for blank
+// params; for a function such as "func foo(_ int, b byte, _ float32)"
+// the length of ParamFlags will be 3.
+type FuncProps struct {
+       Flags       FuncPropBits
+       ParamFlags  []ParamPropBits // slot 0 receiver if applicable
+       ResultFlags []ResultPropBits
+}
+
+type FuncPropBits uint32
+
+const (
+       // Function always panics or invokes os.Exit() or a func that does
+       // likewise.
+       FuncPropNeverReturns FuncPropBits = 1 << iota
+)
+
+type ParamPropBits uint32
+
+const (
+       // No info about this param
+       ParamNoInfo ParamPropBits = 0
+
+       // Parameter value feeds unmodified into a top-level interface
+       // call (this assumes the parameter is of interface type).
+       ParamFeedsInterfaceMethodCall ParamPropBits = 1 << iota
+
+       // Parameter value feeds unmodified into an interface call that
+       // may be conditional/nested and not always executed (this assumes
+       // the parameter is of interface type).
+       ParamMayFeedInterfaceMethodCall ParamPropBits = 1 << iota
+
+       // Parameter value feeds unmodified into a top level indirect
+       // function call (assumes parameter is of function type).
+       ParamFeedsIndirectCall
+
+       // Parameter value feeds unmodified into an indirect function call
+       // that is conditional/nested (not guaranteed to execute). Assumes
+       // parameter is of function type.
+       ParamMayFeedIndirectCall
+
+       // Parameter value feeds unmodified into a top level "switch"
+       // statement or "if" statement simple expressions (see more on
+       // "simple" expression classification below).
+       ParamFeedsIfOrSwitch
+
+       // Parameter value feeds unmodified into a "switch" or "if"
+       // statement simple expressions (see more on "simple" expression
+       // classification below), where the if/switch is
+       // conditional/nested.
+       ParamMayFeedIfOrSwitch
+)
+
+type ResultPropBits uint32
+
+const (
+       // No info about this result
+       ResultNoInfo ResultPropBits = 0
+       // This result always contains allocated memory.
+       ResultIsAllocatedMem ResultPropBits = 1 << iota
+       // This result is always a single concrete type that is
+       // implicitly converted to interface.
+       ResultIsConcreteTypeConvertedToInterface
+       // Result is always the same non-composite compile time constant.
+       ResultAlwaysSameConstant
+       // Result is always the same function or closure.
+       ResultAlwaysSameFunc
+       // Result is always the same (potentially) inlinable function or closure.
+       ResultAlwaysSameInlinableFunc
+)
diff --git a/src/cmd/compile/internal/inline/inlheur/parampropbits_string.go b/src/cmd/compile/internal/inline/inlheur/parampropbits_string.go
new file mode 100644 (file)
index 0000000..bf4d3ca
--- /dev/null
@@ -0,0 +1,70 @@
+// 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.
+
+// Code generated by "stringer -bitset -type ParamPropBits"; DO NOT EDIT.
+
+package inlheur
+
+import (
+       "bytes"
+       "strconv"
+)
+
+func _() {
+       // An "invalid array index" compiler error signifies that the constant values have changed.
+       // Re-run the stringer command to generate them again.
+       var x [1]struct{}
+       _ = x[ParamNoInfo-0]
+       _ = x[ParamFeedsInterfaceMethodCall-2]
+       _ = x[ParamMayFeedInterfaceMethodCall-4]
+       _ = x[ParamFeedsIndirectCall-8]
+       _ = x[ParamMayFeedIndirectCall-16]
+       _ = x[ParamFeedsIfOrSwitch-32]
+       _ = x[ParamMayFeedIfOrSwitch-64]
+}
+
+var _ParamPropBits_value = [...]uint64{
+       0x0,  /* ParamNoInfo */
+       0x2,  /* ParamFeedsInterfaceMethodCall */
+       0x4,  /* ParamMayFeedInterfaceMethodCall */
+       0x8,  /* ParamFeedsIndirectCall */
+       0x10, /* ParamMayFeedIndirectCall */
+       0x20, /* ParamFeedsIfOrSwitch */
+       0x40, /* ParamMayFeedIfOrSwitch */
+}
+
+const _ParamPropBits_name = "ParamNoInfoParamFeedsInterfaceMethodCallParamMayFeedInterfaceMethodCallParamFeedsIndirectCallParamMayFeedIndirectCallParamFeedsIfOrSwitchParamMayFeedIfOrSwitch"
+
+var _ParamPropBits_index = [...]uint8{0, 11, 40, 71, 93, 117, 137, 159}
+
+func (i ParamPropBits) String() string {
+       var b bytes.Buffer
+
+       remain := uint64(i)
+       seen := false
+
+       for k, v := range _ParamPropBits_value {
+               x := _ParamPropBits_name[_ParamPropBits_index[k]:_ParamPropBits_index[k+1]]
+               if v == 0 {
+                       if i == 0 {
+                               b.WriteString(x)
+                               return b.String()
+                       }
+                       continue
+               }
+               if (v & remain) == v {
+                       remain &^= v
+                       x := _ParamPropBits_name[_ParamPropBits_index[k]:_ParamPropBits_index[k+1]]
+                       if seen {
+                               b.WriteString("|")
+                       }
+                       seen = true
+                       b.WriteString(x)
+               }
+       }
+       if remain == 0 {
+               return b.String()
+       }
+       return "ParamPropBits(0x" + strconv.FormatInt(int64(i), 16) + ")"
+}
diff --git a/src/cmd/compile/internal/inline/inlheur/resultpropbits_string.go b/src/cmd/compile/internal/inline/inlheur/resultpropbits_string.go
new file mode 100644 (file)
index 0000000..888af98
--- /dev/null
@@ -0,0 +1,68 @@
+// 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.
+
+// Code generated by "stringer -bitset -type ResultPropBits"; DO NOT EDIT.
+
+package inlheur
+
+import (
+       "bytes"
+       "strconv"
+)
+
+func _() {
+       // An "invalid array index" compiler error signifies that the constant values have changed.
+       // Re-run the stringer command to generate them again.
+       var x [1]struct{}
+       _ = x[ResultNoInfo-0]
+       _ = x[ResultIsAllocatedMem-2]
+       _ = x[ResultIsConcreteTypeConvertedToInterface-4]
+       _ = x[ResultAlwaysSameConstant-8]
+       _ = x[ResultAlwaysSameFunc-16]
+       _ = x[ResultAlwaysSameInlinableFunc-32]
+}
+
+var _ResultPropBits_value = [...]uint64{
+       0x0,  /* ResultNoInfo */
+       0x2,  /* ResultIsAllocatedMem */
+       0x4,  /* ResultIsConcreteTypeConvertedToInterface */
+       0x8,  /* ResultAlwaysSameConstant */
+       0x10, /* ResultAlwaysSameFunc */
+       0x20, /* ResultAlwaysSameInlinableFunc */
+}
+
+const _ResultPropBits_name = "ResultNoInfoResultIsAllocatedMemResultIsConcreteTypeConvertedToInterfaceResultAlwaysSameConstantResultAlwaysSameFuncResultAlwaysSameInlinableFunc"
+
+var _ResultPropBits_index = [...]uint8{0, 12, 32, 72, 96, 116, 145}
+
+func (i ResultPropBits) String() string {
+       var b bytes.Buffer
+
+       remain := uint64(i)
+       seen := false
+
+       for k, v := range _ResultPropBits_value {
+               x := _ResultPropBits_name[_ResultPropBits_index[k]:_ResultPropBits_index[k+1]]
+               if v == 0 {
+                       if i == 0 {
+                               b.WriteString(x)
+                               return b.String()
+                       }
+                       continue
+               }
+               if (v & remain) == v {
+                       remain &^= v
+                       x := _ResultPropBits_name[_ResultPropBits_index[k]:_ResultPropBits_index[k+1]]
+                       if seen {
+                               b.WriteString("|")
+                       }
+                       seen = true
+                       b.WriteString(x)
+               }
+       }
+       if remain == 0 {
+               return b.String()
+       }
+       return "ResultPropBits(0x" + strconv.FormatInt(int64(i), 16) + ")"
+}
diff --git a/src/cmd/compile/internal/inline/inlheur/serialize.go b/src/cmd/compile/internal/inline/inlheur/serialize.go
new file mode 100644 (file)
index 0000000..924511b
--- /dev/null
@@ -0,0 +1,80 @@
+// 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 inlheur
+
+import "strings"
+
+func (fp *FuncProps) SerializeToString() string {
+       if fp == nil {
+               return ""
+       }
+       var sb strings.Builder
+       writeUleb128(&sb, uint64(fp.Flags))
+       writeUleb128(&sb, uint64(len(fp.ParamFlags)))
+       for _, pf := range fp.ParamFlags {
+               writeUleb128(&sb, uint64(pf))
+       }
+       writeUleb128(&sb, uint64(len(fp.ResultFlags)))
+       for _, rf := range fp.ResultFlags {
+               writeUleb128(&sb, uint64(rf))
+       }
+       return sb.String()
+}
+
+func DeserializeFromString(s string) *FuncProps {
+       if len(s) == 0 {
+               return nil
+       }
+       var fp FuncProps
+       var v uint64
+       sl := []byte(s)
+       v, sl = readULEB128(sl)
+       fp.Flags = FuncPropBits(v)
+       v, sl = readULEB128(sl)
+       fp.ParamFlags = make([]ParamPropBits, v)
+       for i := range fp.ParamFlags {
+               v, sl = readULEB128(sl)
+               fp.ParamFlags[i] = ParamPropBits(v)
+       }
+       v, sl = readULEB128(sl)
+       fp.ResultFlags = make([]ResultPropBits, v)
+       for i := range fp.ResultFlags {
+               v, sl = readULEB128(sl)
+               fp.ResultFlags[i] = ResultPropBits(v)
+       }
+       return &fp
+}
+
+func readULEB128(sl []byte) (value uint64, rsl []byte) {
+       var shift uint
+
+       for {
+               b := sl[0]
+               sl = sl[1:]
+               value |= (uint64(b&0x7F) << shift)
+               if b&0x80 == 0 {
+                       break
+               }
+               shift += 7
+       }
+       return value, sl
+}
+
+func writeUleb128(sb *strings.Builder, v uint64) {
+       if v < 128 {
+               sb.WriteByte(uint8(v))
+               return
+       }
+       more := true
+       for more {
+               c := uint8(v & 0x7f)
+               v >>= 7
+               more = v != 0
+               if more {
+                       c |= 0x80
+               }
+               sb.WriteByte(c)
+       }
+}
diff --git a/src/cmd/compile/internal/inline/inlheur/tserial_test.go b/src/cmd/compile/internal/inline/inlheur/tserial_test.go
new file mode 100644 (file)
index 0000000..def12f5
--- /dev/null
@@ -0,0 +1,65 @@
+// 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 inlheur
+
+import "testing"
+
+func fpeq(fp1, fp2 FuncProps) bool {
+       if fp1.Flags != fp2.Flags {
+               return false
+       }
+       if len(fp1.ParamFlags) != len(fp2.ParamFlags) {
+               return false
+       }
+       for i := range fp1.ParamFlags {
+               if fp1.ParamFlags[i] != fp2.ParamFlags[i] {
+                       return false
+               }
+       }
+       if len(fp1.ResultFlags) != len(fp2.ResultFlags) {
+               return false
+       }
+       for i := range fp1.ResultFlags {
+               if fp1.ResultFlags[i] != fp2.ResultFlags[i] {
+                       return false
+               }
+       }
+       return true
+}
+
+func TestSerDeser(t *testing.T) {
+       testcases := []FuncProps{
+               FuncProps{},
+               FuncProps{
+                       Flags: 0xfffff,
+               },
+               FuncProps{
+                       Flags:       1,
+                       ResultFlags: []ResultPropBits{ResultAlwaysSameConstant},
+               },
+               FuncProps{
+                       Flags:       1,
+                       ParamFlags:  []ParamPropBits{0x99, 0xaa, 0xfffff},
+                       ResultFlags: []ResultPropBits{0xfeedface},
+               },
+       }
+
+       for k, tc := range testcases {
+               s := tc.SerializeToString()
+               fp := DeserializeFromString(s)
+               got := fp.String()
+               want := tc.String()
+               if !fpeq(*fp, tc) {
+                       t.Errorf("eq check failed for test %d: got:\n%s\nwant:\n%s\n", k, got, want)
+               }
+       }
+
+       var nilt *FuncProps
+       ns := nilt.SerializeToString()
+       nfp := DeserializeFromString(ns)
+       if len(ns) != 0 || nfp != nil {
+               t.Errorf("nil serialize/deserialize failed")
+       }
+}