From: Than McIntosh Date: Thu, 29 Jun 2023 17:43:50 +0000 (-0400) Subject: cmd/compile: function "property" defs for inl heuristics X-Git-Tag: go1.22rc1~1339 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=03d457a2214366f209f5855afc0a93e6c36c6e1f;p=gostls13.git cmd/compile: function "property" defs for inl heuristics 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 Reviewed-by: Matthew Dempsky Run-TryBot: Than McIntosh Reviewed-by: Than McIntosh --- 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 index 0000000000..d16e4d3378 --- /dev/null +++ b/src/cmd/compile/internal/inline/inlheur/funcprop_string.go @@ -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 index 0000000000..28de4a9ced --- /dev/null +++ b/src/cmd/compile/internal/inline/inlheur/funcpropbits_string.go @@ -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 index 0000000000..b90abf976a --- /dev/null +++ b/src/cmd/compile/internal/inline/inlheur/function_properties.go @@ -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 index 0000000000..bf4d3ca4ad --- /dev/null +++ b/src/cmd/compile/internal/inline/inlheur/parampropbits_string.go @@ -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 index 0000000000..888af98fc3 --- /dev/null +++ b/src/cmd/compile/internal/inline/inlheur/resultpropbits_string.go @@ -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 index 0000000000..924511bd1a --- /dev/null +++ b/src/cmd/compile/internal/inline/inlheur/serialize.go @@ -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 index 0000000000..def12f5aaf --- /dev/null +++ b/src/cmd/compile/internal/inline/inlheur/tserial_test.go @@ -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") + } +}