--- /dev/null
+// 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())
+ }
+}
--- /dev/null
+// 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) + ")"
+}
--- /dev/null
+// 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
+)
--- /dev/null
+// 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) + ")"
+}
--- /dev/null
+// 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) + ")"
+}
--- /dev/null
+// 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)
+ }
+}
--- /dev/null
+// 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")
+ }
+}