1
0
mirror of https://github.com/golang/go synced 2024-09-29 21:34:28 -06:00

cmd/compile/internal/inline: add framework to compute func "properties"

Add some machinery to support computing function "properties" for use
in driving inlining heuristics, and a unit testing framework to check
to see if the property computations are correct for a given set of
canned Go source files. This CL is mainly the analysis skeleton and a
testing framework; the code to compute the actual props will arrive in
a later patch.

Updates #61502.

Change-Id: I7970b64f713d17d7fdd7e8e9ccc7d9b0490571bf
Reviewed-on: https://go-review.googlesource.com/c/go/+/511557
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Than McIntosh 2023-06-29 15:37:26 -04:00
parent 03d457a221
commit b888ec4ac3
8 changed files with 854 additions and 0 deletions

View File

@ -21,6 +21,7 @@ type DebugFlags struct {
Closure int `help:"print information about closure compilation"`
Defer int `help:"print information about defer compilation"`
DisableNil int `help:"disable nil checks" concurrent:"ok"`
DumpInlFuncProps string `help:"dump function properties from inl heuristics to specified file"`
DumpPtrs int `help:"show Node pointers values in dump output"`
DwarfInl int `help:"print information about DWARF inlined function creation"`
Export int `help:"print export data"`

View File

@ -33,6 +33,7 @@ import (
"strconv"
"cmd/compile/internal/base"
"cmd/compile/internal/inline/inlheur"
"cmd/compile/internal/ir"
"cmd/compile/internal/logopt"
"cmd/compile/internal/pgo"
@ -166,6 +167,10 @@ func InlinePackage(p *pgo.Profile) {
// are no longer reachable from top-level functions following
// inlining. See #59404 and #59638 for more context.
garbageCollectUnreferencedHiddenClosures()
if base.Debug.DumpInlFuncProps != "" {
inlheur.DumpFuncProps(nil, base.Debug.DumpInlFuncProps)
}
}
// InlineDecls applies inlining to the given batch of declarations.
@ -269,6 +274,10 @@ func CanInline(fn *ir.Func, profile *pgo.Profile) {
base.Fatalf("CanInline no nname %+v", fn)
}
if base.Debug.DumpInlFuncProps != "" {
defer inlheur.DumpFuncProps(fn, base.Debug.DumpInlFuncProps)
}
var reason string // reason, if any, that the function was not inlined
if base.Flag.LowerM > 1 || logopt.Enabled() {
defer func() {

View File

@ -0,0 +1,168 @@
// 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 (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
)
const (
debugTraceFuncs = 1 << iota
)
// fnInlHeur contains inline heuristics state information about
// a specific Go function being analyzed/considered by the inliner.
type fnInlHeur struct {
fname string
file string
line uint
props *FuncProps
}
// computeFuncProps examines the Go function 'fn' and computes for it
// a function "properties" object, to be used to drive inlining
// heuristics. See comments on the FuncProps type for more info.
func computeFuncProps(fn *ir.Func) *FuncProps {
if debugTrace&debugTraceFuncs != 0 {
fmt.Fprintf(os.Stderr, "=-= starting analysis of func %v:\n%+v\n",
fn.Sym().Name, fn)
}
// implementation stubbed out for now
return &FuncProps{}
}
func fnFileLine(fn *ir.Func) (string, uint) {
p := base.Ctxt.InnermostPos(fn.Pos())
return filepath.Base(p.Filename()), p.Line()
}
// DumpFuncProps computes and caches function properties for the func
// 'fn', or if fn is nil, writes out the cached set of properties to
// the file given in 'dumpfile'. Used for the "-d=dumpinlfuncprops=..."
// command line flag, intended for use primarily in unit testing.
func DumpFuncProps(fn *ir.Func, dumpfile string) {
if fn != nil {
captureFuncDumpEntry(fn)
} else {
emitDumpToFile(dumpfile)
}
}
// emitDumpToFile writes out the buffer function property dump entries
// to a file, for unit testing. Dump entries need to be sorted by
// definition line, and due to generics we need to account for the
// possibility that several ir.Func's will have the same def line.
func emitDumpToFile(dumpfile string) {
outf, err := os.OpenFile(dumpfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
base.Fatalf("opening function props dump file %q: %v\n", dumpfile, err)
}
defer outf.Close()
dumpFilePreamble(outf)
atline := map[uint]uint{}
sl := make([]fnInlHeur, 0, len(dumpBuffer))
for _, e := range dumpBuffer {
sl = append(sl, e)
atline[e.line] = atline[e.line] + 1
}
sl = sortFnInlHeurSlice(sl)
prevline := uint(0)
for _, entry := range sl {
idx := uint(0)
if prevline == entry.line {
idx++
}
prevline = entry.line
atl := atline[entry.line]
if err := dumpFnPreamble(outf, &entry, idx, atl); err != nil {
base.Fatalf("function props dump: %v\n", err)
}
}
dumpBuffer = nil
}
// captureFuncDumpEntry analyzes function 'fn' and adds a entry
// for it to 'dumpBuffer'. Used for unit testing.
func captureFuncDumpEntry(fn *ir.Func) {
// avoid capturing compiler-generated equality funcs.
if strings.HasPrefix(fn.Sym().Name, ".eq.") {
return
}
if dumpBuffer == nil {
dumpBuffer = make(map[*ir.Func]fnInlHeur)
}
if _, ok := dumpBuffer[fn]; ok {
// we can wind up seeing closures multiple times here,
// so don't add them more than once.
return
}
fp := computeFuncProps(fn)
file, line := fnFileLine(fn)
entry := fnInlHeur{
fname: fn.Sym().Name,
file: file,
line: line,
props: fp,
}
dumpBuffer[fn] = entry
}
// dumpFilePreamble writes out a file-level preamble for a given
// Go function as part of a function properties dump.
func dumpFilePreamble(w io.Writer) {
fmt.Fprintf(w, "// DO NOT EDIT (use 'go test -v -update-expected' instead.)\n")
fmt.Fprintf(w, "// See cmd/compile/internal/inline/inlheur/testdata/props/README.txt\n")
fmt.Fprintf(w, "// for more information on the format of this file.\n")
fmt.Fprintf(w, "// %s\n", preambleDelimiter)
}
// dumpFilePreamble writes out a function-level preamble for a given
// Go function as part of a function properties dump. See the
// README.txt file in testdata/props for more on the format of
// this preamble.
func dumpFnPreamble(w io.Writer, fih *fnInlHeur, idx, atl uint) error {
fmt.Fprintf(w, "// %s %s %d %d %d\n",
fih.file, fih.fname, fih.line, idx, atl)
// emit props as comments, followed by delimiter
fmt.Fprintf(w, "%s// %s\n", fih.props.ToString("// "), comDelimiter)
data, err := json.Marshal(fih.props)
if err != nil {
return fmt.Errorf("marshall error %v\n", err)
}
fmt.Fprintf(w, "// %s\n// %s\n", string(data), fnDelimiter)
return nil
}
// sortFnInlHeurSlice sorts a slice of fnInlHeur based on
// the starting line of the function definition, then by name.
func sortFnInlHeurSlice(sl []fnInlHeur) []fnInlHeur {
sort.SliceStable(sl, func(i, j int) bool {
if sl[i].line != sl[j].line {
return sl[i].line < sl[j].line
}
return sl[i].fname < sl[j].fname
})
return sl
}
// delimiters written to various preambles to make parsing of
// dumps easier.
const preambleDelimiter = "<endfilepreamble>"
const fnDelimiter = "<endfuncpreamble>"
const comDelimiter = "<endpropsdump>"
// dumpBuffer stores up function properties dumps when
// "-d=dumpinlfuncprops=..." is in effect.
var dumpBuffer map[*ir.Func]fnInlHeur

View File

@ -0,0 +1,462 @@
// 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 (
"bufio"
"encoding/json"
"flag"
"fmt"
"internal/testenv"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"time"
)
var remasterflag = flag.Bool("update-expected", false, "if true, generate updated golden results in testcases for all props tests")
func TestFuncProperties(t *testing.T) {
td := t.TempDir()
//td = "/tmp/qqq"
//os.RemoveAll(td)
//os.Mkdir(td, 0777)
testenv.MustHaveGoBuild(t)
// NOTE: this testpoint has the unfortunate characteristic that it
// relies on the installed compiler, meaning that if you make
// changes to the inline heuristics code in your working copy and
// then run the test, it will test the installed compiler and not
// your local modifications. TODO: decide whether to convert this
// to building a fresh compiler on the fly, or using some other
// scheme.
testcases := []string{"stub"}
for _, tc := range testcases {
dumpfile, err := gatherPropsDumpForFile(t, tc, td)
if err != nil {
t.Fatalf("dumping func props for %q: error %v", tc, err)
}
// Read in the newly generated dump.
dentries, derr := readDump(t, dumpfile)
if derr != nil {
t.Fatalf("reading func prop dump: %v", derr)
}
if *remasterflag {
updateExpected(t, tc, dentries)
continue
}
// Generate expected dump.
epath, gerr := genExpected(td, tc)
if gerr != nil {
t.Fatalf("generating expected func prop dump: %v", gerr)
}
// Read in the expected result entries.
eentries, eerr := readDump(t, epath)
if eerr != nil {
t.Fatalf("reading expected func prop dump: %v", eerr)
}
// Compare new vs expected.
n := len(dentries)
eidx := 0
for i := 0; i < n; i++ {
dentry := dentries[i]
if !interestingToCompare(dentry.fname) {
continue
}
if eidx >= len(eentries) {
t.Errorf("missing expected entry for %s, skipping",
dentry.fname)
continue
}
eentry := eentries[eidx]
eidx++
if dentry.fname != eentry.fname {
t.Errorf("got fn %q wanted %q, skipping checks",
dentry.fname, eentry.fname)
continue
}
compareEntries(t, tc, &dentry, &eentry)
}
}
}
func propBitsToString[T interface{ String() string }](sl []T) string {
var sb strings.Builder
for i, f := range sl {
fmt.Fprintf(&sb, "%d: %s\n", i, f.String())
}
return sb.String()
}
func compareEntries(t *testing.T, tc string, dentry *fnInlHeur, eentry *fnInlHeur) {
dfp := dentry.props
efp := eentry.props
dfn := dentry.fname
// Compare function flags.
if dfp.Flags != efp.Flags {
t.Errorf("testcase %s: Flags mismatch for %q: got %s, wanted %s",
tc, dfn, dfp.Flags.String(), efp.Flags.String())
}
// Compare returns
rgot := propBitsToString[ResultPropBits](dfp.ResultFlags)
rwant := propBitsToString[ResultPropBits](efp.ResultFlags)
if rgot != rwant {
t.Errorf("Results mismatch for %q: got:\n%swant:\n%s",
dfn, rgot, rwant)
}
// Compare receiver + params.
pgot := propBitsToString[ParamPropBits](dfp.ParamFlags)
pwant := propBitsToString[ParamPropBits](efp.ParamFlags)
if pgot != pwant {
t.Errorf("Params mismatch for %q: got:\n%swant:\n%s",
dfn, pgot, pwant)
}
}
type dumpReader struct {
s *bufio.Scanner
t *testing.T
p string
ln int
}
// readDump reads in the contents of a dump file produced
// by the "-d=dumpinlfuncprops=..." command line flag by the Go
// compiler. It breaks the dump down into separate sections
// by function, then deserializes each func section into a
// fnInlHeur object and returns a slice of those objects.
func readDump(t *testing.T, path string) ([]fnInlHeur, error) {
content, err := os.ReadFile(path)
if err != nil {
return nil, err
}
dr := &dumpReader{
s: bufio.NewScanner(strings.NewReader(string(content))),
t: t,
p: path,
ln: 1,
}
// consume header comment until preamble delimiter.
found := false
for dr.scan() {
if dr.curLine() == preambleDelimiter {
found = true
break
}
}
if !found {
return nil, fmt.Errorf("malformed testcase file %s, missing preamble delimiter", path)
}
res := []fnInlHeur{}
for {
dentry, err := dr.readEntry()
if err != nil {
t.Fatalf("reading func prop dump: %v", err)
}
if dentry.fname == "" {
break
}
res = append(res, dentry)
}
return res, nil
}
func (dr *dumpReader) scan() bool {
v := dr.s.Scan()
if v {
dr.ln++
}
return v
}
func (dr *dumpReader) curLine() string {
res := strings.TrimSpace(dr.s.Text())
if !strings.HasPrefix(res, "// ") {
dr.t.Fatalf("malformed line %s:%d, no comment: %s", dr.p, dr.ln, res)
}
return res[3:]
}
// readObjBlob reads in a series of commented lines until
// it hits a delimiter, then returns the contents of the comments.
func (dr *dumpReader) readObjBlob(delim string) (string, error) {
var sb strings.Builder
foundDelim := false
for dr.scan() {
line := dr.curLine()
if delim == line {
foundDelim = true
break
}
sb.WriteString(line + "\n")
}
if err := dr.s.Err(); err != nil {
return "", err
}
if !foundDelim {
return "", fmt.Errorf("malformed input %s, missing delimiter %q",
dr.p, delim)
}
return sb.String(), nil
}
// readEntry reads a single function's worth of material from
// a file produced by the "-d=dumpinlfuncprops=..." command line
// flag. It deserializes the json for the func properties and
// returns the resulting properties and function name. EOF is
// signaled by a nil FuncProps return (with no error
func (dr *dumpReader) readEntry() (fnInlHeur, error) {
var fih fnInlHeur
if !dr.scan() {
return fih, nil
}
// first line contains info about function: file/name/line
info := dr.curLine()
chunks := strings.Fields(info)
fih.file = chunks[0]
fih.fname = chunks[1]
if _, err := fmt.Sscanf(chunks[2], "%d", &fih.line); err != nil {
return fih, err
}
// consume comments until and including delimiter
for {
if !dr.scan() {
break
}
if dr.curLine() == comDelimiter {
break
}
}
// Consume JSON for encoded props.
dr.scan()
line := dr.curLine()
fp := &FuncProps{}
if err := json.Unmarshal([]byte(line), fp); err != nil {
return fih, err
}
fih.props = fp
// Consume delimiter.
dr.scan()
line = dr.curLine()
if line != fnDelimiter {
return fih, fmt.Errorf("malformed testcase file %q, missing delimiter %q", dr.p, fnDelimiter)
}
return fih, nil
}
// gatherPropsDumpForFile builds the specified testcase 'testcase' from
// testdata/props passing the "-d=dumpinlfuncprops=..." compiler option,
// to produce a properties dump, then returns the path of the newly
// created file. NB: we can't use "go tool compile" here, since
// some of the test cases import stdlib packages (such as "os").
// This means using "go build", which is problematic since the
// Go command can potentially cache the results of the compile step,
// causing the test to fail when being run interactively. E.g.
//
// $ rm -f dump.txt
// $ go build -o foo.a -gcflags=-d=dumpinlfuncprops=dump.txt foo.go
// $ rm -f dump.txt foo.a
// $ go build -o foo.a -gcflags=-d=dumpinlfuncprops=dump.txt foo.go
// $ ls foo.a dump.txt > /dev/null
// ls : cannot access 'dump.txt': No such file or directory
// $
//
// For this reason, pick a unique filename for the dump, so as to
// defeat the caching.
func gatherPropsDumpForFile(t *testing.T, testcase string, td string) (string, error) {
t.Helper()
gopath := "testdata/props/" + testcase + ".go"
outpath := filepath.Join(td, testcase+".a")
salt := fmt.Sprintf(".p%dt%d", os.Getpid(), time.Now().UnixNano())
dumpfile := filepath.Join(td, testcase+salt+".dump.txt")
run := []string{testenv.GoToolPath(t), "build",
"-gcflags=-d=dumpinlfuncprops=" + dumpfile, "-o", outpath, gopath}
out, err := testenv.Command(t, run[0], run[1:]...).CombinedOutput()
if strings.TrimSpace(string(out)) != "" {
t.Logf("%s", out)
}
return dumpfile, err
}
// genExpected reads in a given Go testcase file, strips out all the
// unindented (column 0) commands, writes them out to a new file, and
// returns the path of that new file. By picking out just the comments
// from the Go file we wind up with something that resembles the
// output from a "-d=dumpinlfuncprops=..." compilation.
func genExpected(td string, testcase string) (string, error) {
epath := filepath.Join(td, testcase+".expected")
outf, err := os.OpenFile(epath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return "", err
}
gopath := "testdata/props/" + testcase + ".go"
content, err := os.ReadFile(gopath)
if err != nil {
return "", err
}
lines := strings.Split(string(content), "\n")
for _, line := range lines[3:] {
if !strings.HasPrefix(line, "// ") {
continue
}
fmt.Fprintf(outf, "%s\n", line)
}
if err := outf.Close(); err != nil {
return "", err
}
return epath, nil
}
type upexState struct {
dentries []fnInlHeur
newgolines []string
atline map[uint]uint
}
func mkUpexState(dentries []fnInlHeur) *upexState {
atline := make(map[uint]uint)
for _, e := range dentries {
atline[e.line] = atline[e.line] + 1
}
return &upexState{
dentries: dentries,
atline: atline,
}
}
// updateExpected takes a given Go testcase file X.go and writes out a
// new/updated version of the file to X.go.new, where the column-0
// "expected" comments have been updated using fresh data from
// "dentries".
//
// Writing of expected results is complicated by closures and by
// generics, where you can have multiple functions that all share the
// same starting line. Currently we combine up all the dups and
// closures into the single pre-func comment.
func updateExpected(t *testing.T, testcase string, dentries []fnInlHeur) {
nd := len(dentries)
ues := mkUpexState(dentries)
gopath := "testdata/props/" + testcase + ".go"
newgopath := "testdata/props/" + testcase + ".go.new"
// Read the existing Go file.
content, err := os.ReadFile(gopath)
if err != nil {
t.Fatalf("opening %s: %v", gopath, err)
}
golines := strings.Split(string(content), "\n")
// Preserve copyright.
ues.newgolines = append(ues.newgolines, golines[:4]...)
if !strings.HasPrefix(golines[0], "// Copyright") {
t.Fatalf("missing copyright from existing testcase")
}
golines = golines[4:]
clore := regexp.MustCompile(`.+\.func\d+[\.\d]*$`)
emitFunc := func(e *fnInlHeur, instance, atl uint) {
var sb strings.Builder
dumpFnPreamble(&sb, e, instance, atl)
ues.newgolines = append(ues.newgolines,
strings.Split(strings.TrimSpace(sb.String()), "\n")...)
}
// Write file preamble with "DO NOT EDIT" message and such.
var sb strings.Builder
dumpFilePreamble(&sb)
ues.newgolines = append(ues.newgolines,
strings.Split(strings.TrimSpace(sb.String()), "\n")...)
// Helper to add a clump of functions to the output file.
processClump := func(idx int, emit bool) int {
// Process func itself, plus anything else defined
// on the same line
atl := ues.atline[dentries[idx].line]
for k := uint(0); k < atl; k++ {
if emit {
emitFunc(&dentries[idx], k, atl)
}
idx++
}
// now process any closures it contains
ncl := 0
for idx < nd {
nfn := dentries[idx].fname
if !clore.MatchString(nfn) {
break
}
ncl++
if emit {
emitFunc(&dentries[idx], 0, 1)
}
idx++
}
return idx
}
didx := 0
for _, line := range golines {
if strings.HasPrefix(line, "func ") {
// We have a function definition.
// Pick out the corresponding entry or entries in the dump
// and emit if interesting (or skip if not).
dentry := dentries[didx]
emit := interestingToCompare(dentry.fname)
didx = processClump(didx, emit)
}
// Consume all existing comments.
if strings.HasPrefix(line, "//") {
continue
}
ues.newgolines = append(ues.newgolines, line)
}
if didx != nd {
t.Logf("didx=%d wanted %d", didx, nd)
}
// Open new Go file and write contents.
of, err := os.OpenFile(newgopath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
t.Fatalf("opening %s: %v", newgopath, err)
}
fmt.Fprintf(of, "%s", strings.Join(ues.newgolines, "\n"))
if err := of.Close(); err != nil {
t.Fatalf("closing %s: %v", newgopath, err)
}
t.Logf("update-expected: emitted updated file %s", newgopath)
t.Logf("please compare the two files, then overwrite %s with %s\n",
gopath, newgopath)
}
// interestingToCompare returns TRUE if we want to compare results
// for function 'fname'.
func interestingToCompare(fname string) bool {
if strings.HasPrefix(fname, "init.") {
return true
}
if strings.HasPrefix(fname, "T_") {
return true
}
f := strings.Split(fname, ".")
if len(f) == 2 && strings.HasPrefix(f[1], "T_") {
return true
}
return false
}

View File

@ -0,0 +1,75 @@
// 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.
Notes on the format of the testcase files in
cmd/compile/internal/inline/inlheur/testdata/props:
- each (compilable) file contains input Go code and expected results
in the form of column-0 comments.
- functions or methods that begin with "T_" are targeted for testing,
as well as "init" functions; all other functions are ignored.
- function header comments begin with a line containing
the file name, function name, definition line, then index
and a count of the number of funcs that share that same
definition line (needed to support generics). Example:
// foo.go T_mumble 35 1 4
Here "T_mumble" is defined at line 35, and it is func 0
out of the 4 funcs that share that same line.
- function property expected results appear as comments in immediately
prior to the function. For example, here we have first the function
name ("T_feeds_if_simple"), then human-readable dump of the function
properties, as well as the JSON for the properties object, each
section separated by a "<>" delimiter.
// funcflags.go T_feeds_if_simple 35 0 1
// RecvrParamFlags:
// 0: ParamFeedsIfOrSwitch
// <endpropsdump>
// {"Flags":0,"RecvrParamFlags":[8],"ReturnFlags":[]}
// <endfuncpreamble>
func T_feeds_if_simple(x int) {
if x < 100 {
os.Exit(1)
}
println(x)
}
- when the test runs, it will compile the Go source file with an
option to dump out function properties, then compare the new dump
for each function with the JSON appearing in the header comment for
the function (in the example above, the JSON appears between
"<endpropsdump>" and "<endfuncpreamble>". The material prior to the
dump is simply there for human consumption, so that a developer can
easily see that "RecvrParamFlags":[8] means that the first parameter
has flag ParamFeedsIfOrSwitch.
- when making changes to the compiler (which can alter the expected
results) or edits/additions to the go code in the testcase files,
you can remaster the results by running
go test -v -count=1 .
In the trace output of this run, you'll see messages of the form
=== RUN TestFuncProperties
funcprops_test.go:NNN: update-expected: emitted updated file
testdata/props/XYZ.go.new
funcprops_test.go:MMM: please compare the two files, then overwrite
testdata/props/XYZ.go with testdata/props/XYZ.go.new
at which point you can compare the old and new files by hand, then
overwrite the *.go file with the *.go.new file if you are happy with
the diffs.
- note that the remastering process will strip out any existing
column-0 (unindented) comments; if you write comments that you
want to see preserved, use "/* */" or indent them.

View File

@ -0,0 +1,107 @@
// 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.
// DO NOT EDIT (use 'go test -v -update-expected' instead.)
// See cmd/compile/internal/inline/inlheur/testdata/props/README.txt
// for more information on the format of this file.
// <endfilepreamble>
package stub
// stub.go T_stub 16 0 1
// <endpropsdump>
// {"Flags":0,"ParamFlags":null,"ResultFlags":null}
// <endfuncpreamble>
func T_stub() {
}
func ThisFunctionShouldBeIgnored(x int) {
println(x)
}
// stub.go init.0 27 0 1
// <endpropsdump>
// {"Flags":0,"ParamFlags":null,"ResultFlags":null}
// <endfuncpreamble>
func init() {
ThisFunctionShouldBeIgnored(1)
}
// stub.go T_contains_closures 43 0 1
// <endpropsdump>
// {"Flags":0,"ParamFlags":null,"ResultFlags":null}
// <endfuncpreamble>
// stub.go T_contains_closures.func1 44 0 1
// <endpropsdump>
// {"Flags":0,"ParamFlags":null,"ResultFlags":null}
// <endfuncpreamble>
// stub.go T_contains_closures.func2 46 0 1
// <endpropsdump>
// {"Flags":0,"ParamFlags":null,"ResultFlags":null}
// <endfuncpreamble>
func T_contains_closures(q int) func() {
f := func() { M["a"] = 9 }
f()
f2 := func() { M["a"] = 4 }
if M["b"] != 9 {
return f
}
return f2
}
// stub.go T_Unique[go.shape.int] 69 0 4
// <endpropsdump>
// {"Flags":0,"ParamFlags":null,"ResultFlags":null}
// <endfuncpreamble>
// stub.go T_Unique[go.shape.string] 69 1 4
// <endpropsdump>
// {"Flags":0,"ParamFlags":null,"ResultFlags":null}
// <endfuncpreamble>
// stub.go T_Unique[int] 69 2 4
// <endpropsdump>
// {"Flags":0,"ParamFlags":null,"ResultFlags":null}
// <endfuncpreamble>
// stub.go T_Unique[string] 69 3 4
// <endpropsdump>
// {"Flags":0,"ParamFlags":null,"ResultFlags":null}
// <endfuncpreamble>
func T_Unique[T comparable](set []T) []T {
nset := make([]T, 0, 8)
loop:
for _, s := range set {
for _, e := range nset {
if s == e {
continue loop
}
}
nset = append(nset, s)
}
return nset
}
// stub.go T_uniq_int_count 88 0 1
// <endpropsdump>
// {"Flags":0,"ParamFlags":null,"ResultFlags":null}
// <endfuncpreamble>
func T_uniq_int_count(s []int) int {
return len(T_Unique[int](s))
}
// stub.go T_uniq_string_count 96 0 1
// <endpropsdump>
// {"Flags":0,"ParamFlags":null,"ResultFlags":null}
// <endfuncpreamble>
func T_uniq_string_count(s []string) int {
return len(T_Unique[string](s))
}
// stub.go T_epilog 104 0 1
// <endpropsdump>
// {"Flags":0,"ParamFlags":null,"ResultFlags":null}
// <endfuncpreamble>
func T_epilog() {
}
var M = map[string]int{}

View File

@ -0,0 +1,15 @@
// 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.
//go:build !debugtrace
package inlheur
const debugTrace = 0
func enableDebugTrace(x int) {
}
func disableDebugTrace() {
}

View File

@ -0,0 +1,17 @@
// 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.
//go:build debugtrace
package inlheur
var debugTrace = 0
func enableDebugTrace(x int) {
debugTrace = x
}
func disableDebugTrace() {
debugTrace = 0
}