diff options
author | 2021-12-09 18:06:29 +0800 | |
---|---|---|
committer | 2021-12-21 16:09:23 +0800 | |
commit | eb8efc902dd6008d22601d192ff63ea99d6eff1d (patch) | |
tree | 8b2c545395385b565b316892f7d9dc84f1e11d72 /cc/afdo.go | |
parent | 55f84a74c5b824f9ff5e5b320fc8fc598f25f46d (diff) |
Introduce afdo
A new configuration rule for sampling PGO. This differs from the
original pgo.go rule in the following ways:
* Automatic propagation to static dependencies
* Simpler configuration (just put `afdo: true` to optimsation targets)
http://go/android-afdo
Test: build
Bug: 79161490
Change-Id: Ie194824cd523bca19e10ced41d2078fc598f13b3
Diffstat (limited to 'cc/afdo.go')
-rw-r--r-- | cc/afdo.go | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/cc/afdo.go b/cc/afdo.go new file mode 100644 index 000000000..022f2833b --- /dev/null +++ b/cc/afdo.go @@ -0,0 +1,194 @@ +// Copyright 2021 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cc + +import ( + "fmt" + "strings" + + "github.com/google/blueprint/proptools" + + "android/soong/android" +) + +var ( + globalAfdoProfileProjects = []string{ + "vendor/google_data/pgo_profile/sampling/", + "toolchain/pgo-profiles/sampling/", + } +) + +var afdoProfileProjectsConfigKey = android.NewOnceKey("AfdoProfileProjects") + +const afdoCFlagsFormat = "-fprofile-sample-accurate -fprofile-sample-use=%s" + +func getAfdoProfileProjects(config android.DeviceConfig) []string { + return config.OnceStringSlice(afdoProfileProjectsConfigKey, func() []string { + return append(globalAfdoProfileProjects, config.AfdoAdditionalProfileDirs()...) + }) +} + +func recordMissingAfdoProfileFile(ctx BaseModuleContext, missing string) { + getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true) +} + +type AfdoProperties struct { + Afdo bool + + AfdoTarget *string `blueprint:"mutated"` + AfdoDeps []string `blueprint:"mutated"` +} + +type afdo struct { + Properties AfdoProperties +} + +func (afdo *afdo) props() []interface{} { + return []interface{}{&afdo.Properties} +} + +func (afdo *afdo) AfdoEnabled() bool { + return afdo != nil && afdo.Properties.Afdo && afdo.Properties.AfdoTarget != nil +} + +// Get list of profile file names, ordered by level of specialisation. For example: +// 1. libfoo_arm64.afdo +// 2. libfoo.afdo +// Add more specialisation as needed. +func getProfileFiles(ctx BaseModuleContext, moduleName string) []string { + var files []string + files = append(files, moduleName+"_"+ctx.Arch().ArchType.String()+".afdo") + files = append(files, moduleName+".afdo") + return files +} + +func (props *AfdoProperties) getAfdoProfileFile(ctx BaseModuleContext, module string) android.OptionalPath { + // Test if the profile_file is present in any of the Afdo profile projects + for _, profileFile := range getProfileFiles(ctx, module) { + for _, profileProject := range getAfdoProfileProjects(ctx.DeviceConfig()) { + path := android.ExistentPathForSource(ctx, profileProject, profileFile) + if path.Valid() { + return path + } + } + } + + // Record that this module's profile file is absent + missing := ctx.ModuleDir() + ":" + module + recordMissingAfdoProfileFile(ctx, missing) + + return android.OptionalPathForPath(nil) +} + +func (afdo *afdo) begin(ctx BaseModuleContext) { + if afdo.Properties.Afdo && !ctx.static() && !ctx.Host() { + module := ctx.ModuleName() + if afdo.Properties.getAfdoProfileFile(ctx, module).Valid() { + afdo.Properties.AfdoTarget = proptools.StringPtr(module) + } + } +} + +func (afdo *afdo) flags(ctx ModuleContext, flags Flags) Flags { + if profile := afdo.Properties.AfdoTarget; profile != nil { + if profileFile := afdo.Properties.getAfdoProfileFile(ctx, *profile); profileFile.Valid() { + profileFilePath := profileFile.Path() + + profileUseFlag := fmt.Sprintf(afdoCFlagsFormat, profileFile) + flags.Local.CFlags = append(flags.Local.CFlags, profileUseFlag) + flags.Local.LdFlags = append(flags.Local.LdFlags, profileUseFlag) + flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,-no-warn-sample-unused=true") + + // Update CFlagsDeps and LdFlagsDeps so the module is rebuilt + // if profileFile gets updated + flags.CFlagsDeps = append(flags.CFlagsDeps, profileFilePath) + flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFilePath) + } + } + + return flags +} + +// Propagate afdo requirements down from binaries +func afdoDepsMutator(mctx android.TopDownMutatorContext) { + if m, ok := mctx.Module().(*Module); ok && m.afdo.AfdoEnabled() { + afdoTarget := *m.afdo.Properties.AfdoTarget + mctx.WalkDeps(func(dep android.Module, parent android.Module) bool { + tag := mctx.OtherModuleDependencyTag(dep) + libTag, isLibTag := tag.(libraryDependencyTag) + + // Do not recurse down non-static dependencies + if isLibTag { + if !libTag.static() { + return false + } + } else { + if tag != objDepTag && tag != reuseObjTag { + return false + } + } + + if dep, ok := dep.(*Module); ok { + dep.afdo.Properties.AfdoDeps = append(dep.afdo.Properties.AfdoDeps, afdoTarget) + } + + return true + }) + } +} + +// Create afdo variants for modules that need them +func afdoMutator(mctx android.BottomUpMutatorContext) { + if m, ok := mctx.Module().(*Module); ok && m.afdo != nil { + if m.afdo.AfdoEnabled() && !m.static() { + afdoTarget := *m.afdo.Properties.AfdoTarget + mctx.SetDependencyVariation(encodeTarget(afdoTarget)) + } + + variationNames := []string{""} + afdoDeps := android.FirstUniqueStrings(m.afdo.Properties.AfdoDeps) + for _, dep := range afdoDeps { + variationNames = append(variationNames, encodeTarget(dep)) + } + if len(variationNames) > 1 { + modules := mctx.CreateVariations(variationNames...) + for i, name := range variationNames { + if name == "" { + continue + } + variation := modules[i].(*Module) + variation.Properties.PreventInstall = true + variation.Properties.HideFromMake = true + variation.afdo.Properties.AfdoTarget = proptools.StringPtr(decodeTarget(name)) + } + } + } +} + +// Encode target name to variation name. +func encodeTarget(target string) string { + if target == "" { + return "" + } + return "afdo-" + target +} + +// Decode target name from variation name. +func decodeTarget(variation string) string { + if variation == "" { + return "" + } + return strings.TrimPrefix(variation, "afdo-") +} |