From 57ef719d617dbf9a139190452705f62592818c06 Mon Sep 17 00:00:00 2001 From: Preston Van Loon Date: Wed, 9 Aug 2023 14:21:27 -0500 Subject: [PATCH] Add pgo support for go 1.20 (#3641) * Add pgo support for go 1.20 * Pull request feedback from @fmeum * Add test for building a go_binary with and without a pgoprofile * Add //go/config:pgoprofile to _common_reset_transition_dict * Apply suggestions from code review Co-authored-by: Fabian Meumertzheim * Use `cquery --output=files` to simplify `pgo_test` --------- Co-authored-by: Fabian Meumertzheim --- BUILD.bazel | 1 + docs/go/core/rules.md | 3 +- go/config/BUILD.bazel | 11 ++++ go/private/actions/compilepkg.bzl | 4 ++ go/private/actions/stdlib.bzl | 5 ++ go/private/context.bzl | 7 +++ go/private/mode.bzl | 7 +++ go/private/rules/binary.bzl | 9 +++ go/private/rules/transition.bzl | 10 +++ go/tools/builders/compilepkg.go | 16 ++++- go/tools/builders/stdlib.go | 4 ++ tests/core/go_binary/BUILD.bazel | 14 +++++ tests/core/go_binary/pgo.go | 42 +++++++++++++ tests/core/go_binary/pgo.pprof | Bin 0 -> 40423 bytes tests/core/go_binary/pgo_test.go | 99 ++++++++++++++++++++++++++++++ 15 files changed, 228 insertions(+), 4 deletions(-) create mode 100644 tests/core/go_binary/pgo.go create mode 100644 tests/core/go_binary/pgo.pprof create mode 100644 tests/core/go_binary/pgo_test.go diff --git a/BUILD.bazel b/BUILD.bazel index 60bf72eb8e..e80f3e8081 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -123,6 +123,7 @@ go_config( gotags = "//go/config:tags", linkmode = "//go/config:linkmode", msan = "//go/config:msan", + pgoprofile = "//go/config:pgoprofile", pure = "//go/config:pure", race = "//go/config:race", stamp = select({ diff --git a/docs/go/core/rules.md b/docs/go/core/rules.md index 22e08a2434..99968b22e3 100644 --- a/docs/go/core/rules.md +++ b/docs/go/core/rules.md @@ -126,7 +126,7 @@ Rules
 go_binary(name, basename, cdeps, cgo, clinkopts, copts, cppopts, cxxopts, data, deps, embed,
           embedsrcs, env, gc_goopts, gc_linkopts, goarch, goos, gotags, importpath, linkmode, msan,
-          out, pure, race, srcs, static, x_defs)
+          out, pgoprofile, pure, race, srcs, static, x_defs)
 
This builds an executable from a set of source files, @@ -168,6 +168,7 @@ This builds an executable from a set of source files, | linkmode | Determines how the binary should be built and linked. This accepts some of the same values as `go build -buildmode` and works the same way.

  • `auto` (default): Controlled by `//go/config:linkmode`, which defaults to `normal`.
  • `normal`: Builds a normal executable with position-dependent code.
  • `pie`: Builds a position-independent executable.
  • `plugin`: Builds a shared library that can be loaded as a Go plugin. Only supported on platforms that support plugins.
  • `c-shared`: Builds a shared library that can be linked into a C program.
  • `c-archive`: Builds an archive that can be linked into a C program.
| String | optional | "auto" | | msan | Controls whether code is instrumented for memory sanitization. May be one of on, off, or auto. Not available when cgo is disabled. In most cases, it's better to control this on the command line with --@io_bazel_rules_go//go/config:msan. See [mode attributes], specifically [msan]. | String | optional | "auto" | | out | Sets the output filename for the generated executable. When set, go_binary will write this file without mode-specific directory prefixes, without linkmode-specific prefixes like "lib", and without platform-specific suffixes like ".exe". Note that without a mode-specific directory prefix, the output file (but not its dependencies) will be invalidated in Bazel's cache when changing configurations. | String | optional | "" | +| pgoprofile | Provides a pprof file to be used for profile guided optimization when compiling go targets. A pprof file can also be provided via --@io_bazel_rules_go//go/config:pgoprofile=<label of a pprof file>. Profile guided optimization is only supported on go 1.20+. See https://go.dev/doc/pgo for more information. | Label | optional | //go/config:empty | | pure | Controls whether cgo source code and dependencies are compiled and linked, similar to setting CGO_ENABLED. May be one of on, off, or auto. If auto, pure mode is enabled when no C/C++ toolchain is configured or when cross-compiling. It's usually better to control this on the command line with --@io_bazel_rules_go//go/config:pure. See [mode attributes], specifically [pure]. | String | optional | "auto" | | race | Controls whether code is instrumented for race detection. May be one of on, off, or auto. Not available when cgo is disabled. In most cases, it's better to control this on the command line with --@io_bazel_rules_go//go/config:race. See [mode attributes], specifically [race]. | String | optional | "auto" | | srcs | The list of Go source files that are compiled to create the package. Only .go and .s files are permitted, unless the cgo attribute is set, in which case, .c .cc .cpp .cxx .h .hh .hpp .hxx .inc .m .mm files are also permitted. Files may be filtered at build time using Go [build constraints]. | List of labels | optional | [] | diff --git a/go/config/BUILD.bazel b/go/config/BUILD.bazel index a292fc9dcf..d33e47ec95 100644 --- a/go/config/BUILD.bazel +++ b/go/config/BUILD.bazel @@ -79,3 +79,14 @@ string_list_flag( build_setting_default = [], visibility = ["//visibility:public"], ) + +label_flag( + name = "pgoprofile", + build_setting_default = ":empty", + visibility = ["//visibility:public"], +) + +filegroup( + name = "empty", + visibility = ["//visibility:public"], +) diff --git a/go/private/actions/compilepkg.bzl b/go/private/actions/compilepkg.bzl index fff807fa3f..4085f0cf2a 100644 --- a/go/private/actions/compilepkg.bzl +++ b/go/private/actions/compilepkg.bzl @@ -151,6 +151,10 @@ def emit_compilepkg( if clinkopts: args.add("-ldflags", quote_opts(clinkopts)) + if go.mode.pgoprofile: + args.add("-pgoprofile", go.mode.pgoprofile) + inputs.append(go.mode.pgoprofile) + go.actions.run( inputs = inputs, outputs = outputs, diff --git a/go/private/actions/stdlib.bzl b/go/private/actions/stdlib.bzl index abacbc8079..3dc6fe74d8 100644 --- a/go/private/actions/stdlib.bzl +++ b/go/private/actions/stdlib.bzl @@ -136,6 +136,11 @@ def _build_stdlib(go): go.sdk.tools + [go.sdk.go, go.sdk.package_list, go.sdk.root_file] + go.crosstool) + + if go.mode.pgoprofile: + args.add("-pgoprofile", go.mode.pgoprofile) + inputs.append(go.mode.pgoprofile) + outputs = [pkg] go.actions.run( inputs = inputs, diff --git a/go/private/context.bzl b/go/private/context.bzl index db4fe09d43..fad1de36fc 100644 --- a/go/private/context.bzl +++ b/go/private/context.bzl @@ -269,6 +269,7 @@ def _library_to_source(go, attr, library, coverage_instrumented): "cgo_deps": [], "cgo_exports": [], "cc_info": None, + "pgoprofile": getattr(attr, "pgoprofile", None), } if coverage_instrumented: source["cover"] = attr_srcs @@ -530,6 +531,7 @@ def go_context(ctx, attr = None): stamp = mode.stamp, label = ctx.label, cover_format = mode.cover_format, + pgoprofile = mode.pgoprofile, # Action generators archive = toolchain.actions.archive, binary = toolchain.actions.binary, @@ -836,6 +838,7 @@ def _go_config_impl(ctx): cover_format = ctx.attr.cover_format[BuildSettingInfo].value, gc_goopts = ctx.attr.gc_goopts[BuildSettingInfo].value, amd64 = ctx.attr.amd64, + pgoprofile = ctx.attr.pgoprofile, )] go_config = rule( @@ -884,6 +887,10 @@ go_config = rule( providers = [BuildSettingInfo], ), "amd64": attr.string(), + "pgoprofile": attr.label( + mandatory = True, + allow_files = True, + ), }, provides = [GoConfigInfo], doc = """Collects information about build settings in the current diff --git a/go/private/mode.bzl b/go/private/mode.bzl index 83ccd045b5..e18964394e 100644 --- a/go/private/mode.bzl +++ b/go/private/mode.bzl @@ -93,6 +93,12 @@ def get_mode(ctx, go_toolchain, cgo_context_info, go_config_info): goos = go_toolchain.default_goos if getattr(ctx.attr, "goos", "auto") == "auto" else ctx.attr.goos goarch = go_toolchain.default_goarch if getattr(ctx.attr, "goarch", "auto") == "auto" else ctx.attr.goarch gc_goopts = go_config_info.gc_goopts if go_config_info else [] + pgoprofile = None + if go_config_info: + if len(go_config_info.pgoprofile.files.to_list()) > 2: + fail("providing more than one pprof file to pgoprofile is not supported") + elif len(go_config_info.pgoprofile.files.to_list()) == 1: + pgoprofile = go_config_info.pgoprofile.files.to_list()[0] # TODO(jayconrod): check for more invalid and contradictory settings. if pure and race: @@ -130,6 +136,7 @@ def get_mode(ctx, go_toolchain, cgo_context_info, go_config_info): cover_format = cover_format, amd64 = amd64, gc_goopts = gc_goopts, + pgoprofile = pgoprofile, ) def installsuffix(mode): diff --git a/go/private/rules/binary.bzl b/go/private/rules/binary.bzl index 690eb14a5e..7184e2154d 100644 --- a/go/private/rules/binary.bzl +++ b/go/private/rules/binary.bzl @@ -400,6 +400,15 @@ _go_binary_kwargs = { """, ), + "pgoprofile": attr.label( + allow_files = True, + doc = """Provides a pprof file to be used for profile guided optimization when compiling go targets. + A pprof file can also be provided via `--@io_bazel_rules_go//go/config:pgoprofile=