// 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" "android/soong/android" "github.com/google/blueprint" ) // This flag needs to be in both CFlags and LdFlags to ensure correct symbol ordering const afdoFlagsFormat = "-fprofile-sample-use=%s -fprofile-sample-accurate" type AfdoProperties struct { // Afdo allows developers self-service enroll for // automatic feedback-directed optimization using profile data. Afdo bool AfdoDep bool `blueprint:"mutated"` } type afdo struct { Properties AfdoProperties } func (afdo *afdo) props() []interface{} { return []interface{}{&afdo.Properties} } func (afdo *afdo) begin(ctx BaseModuleContext) { // Disable on eng builds for faster build. if ctx.Config().Eng() { afdo.Properties.Afdo = false } } // afdoEnabled returns true for binaries and shared libraries // that set afdo prop to True. func (afdo *afdo) afdoEnabled() bool { return afdo != nil && afdo.Properties.Afdo } func (afdo *afdo) isAfdoCompile(ctx ModuleContext) bool { fdoProfilePath := getFdoProfilePathFromDep(ctx) return !ctx.Host() && (afdo.Properties.Afdo || afdo.Properties.AfdoDep) && (fdoProfilePath != "") } func getFdoProfilePathFromDep(ctx ModuleContext) string { fdoProfileDeps := ctx.GetDirectDepsWithTag(FdoProfileTag) if len(fdoProfileDeps) > 0 && fdoProfileDeps[0] != nil { if info, ok := android.OtherModuleProvider(ctx, fdoProfileDeps[0], FdoProfileProvider); ok { return info.Path.String() } } return "" } func (afdo *afdo) flags(ctx ModuleContext, flags Flags) Flags { if ctx.Host() { return flags } if afdo.Properties.Afdo || afdo.Properties.AfdoDep { // We use `-funique-internal-linkage-names` to associate profiles to the right internal // functions. This option should be used before generating a profile. Because a profile // generated for a binary without unique names doesn't work well building a binary with // unique names (they have different internal function names). // To avoid a chicken-and-egg problem, we enable `-funique-internal-linkage-names` when // `afdo=true`, whether a profile exists or not. // The profile can take effect in three steps: // 1. Add `afdo: true` in Android.bp, and build the binary. // 2. Collect an AutoFDO profile for the binary. // 3. Make the profile searchable by the build system. So it's used the next time the binary // is built. flags.Local.CFlags = append([]string{"-funique-internal-linkage-names"}, flags.Local.CFlags...) // Flags for Flow Sensitive AutoFDO flags.Local.CFlags = append([]string{"-mllvm", "-enable-fs-discriminator=true"}, flags.Local.CFlags...) // TODO(b/266595187): Remove the following feature once it is enabled in LLVM by default. flags.Local.CFlags = append([]string{"-mllvm", "-improved-fs-discriminator=true"}, flags.Local.CFlags...) } if fdoProfilePath := getFdoProfilePathFromDep(ctx); fdoProfilePath != "" { // The flags are prepended to allow overriding. profileUseFlag := fmt.Sprintf(afdoFlagsFormat, fdoProfilePath) flags.Local.CFlags = append([]string{profileUseFlag}, flags.Local.CFlags...) flags.Local.LdFlags = append([]string{profileUseFlag, "-Wl,-mllvm,-no-warn-sample-unused=true"}, flags.Local.LdFlags...) // Update CFlagsDeps and LdFlagsDeps so the module is rebuilt // if profileFile gets updated pathForSrc := android.PathForSource(ctx, fdoProfilePath) flags.CFlagsDeps = append(flags.CFlagsDeps, pathForSrc) flags.LdFlagsDeps = append(flags.LdFlagsDeps, pathForSrc) } return flags } func (a *afdo) addDep(ctx android.BottomUpMutatorContext, fdoProfileTarget string) { if fdoProfileName, err := ctx.DeviceConfig().AfdoProfile(fdoProfileTarget); fdoProfileName != "" && err == nil { ctx.AddFarVariationDependencies( []blueprint.Variation{ {Mutator: "arch", Variation: ctx.Target().ArchVariation()}, {Mutator: "os", Variation: "android"}, }, FdoProfileTag, fdoProfileName) } } func afdoPropagateViaDepTag(tag blueprint.DependencyTag) bool { libTag, isLibTag := tag.(libraryDependencyTag) // Do not recurse down non-static dependencies if isLibTag { return libTag.static() } else { return tag == objDepTag || tag == reuseObjTag || tag == staticVariantTag } } // afdoTransitionMutator creates afdo variants of cc modules. type afdoTransitionMutator struct{} func (a *afdoTransitionMutator) Split(ctx android.BaseModuleContext) []string { return []string{""} } func (a *afdoTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string { if ctx.Host() { return "" } if m, ok := ctx.Module().(*Module); ok && m.afdo != nil { if !afdoPropagateViaDepTag(ctx.DepTag()) { return "" } if sourceVariation != "" { return sourceVariation } if !m.afdo.afdoEnabled() { return "" } // TODO(b/324141705): this is designed to prevent propagating AFDO from static libraries that have afdo: true set, but // it should be m.static() && !m.staticBinary() so that static binaries use AFDO variants of dependencies. if m.static() { return "" } return encodeTarget(ctx.Module().Name()) } return "" } func (a *afdoTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string { if m, ok := ctx.Module().(*Module); ok && m.afdo != nil { return incomingVariation } return "" } func (a *afdoTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) { if m, ok := ctx.Module().(*Module); ok && m.afdo != nil { if variation == "" { // The empty variation is either a module that has enabled AFDO for itself, or the non-AFDO // variant of a dependency. if m.afdo.afdoEnabled() && !(m.static() && !m.staticBinary()) && !m.Host() { m.afdo.addDep(ctx, ctx.ModuleName()) } } else { // The non-empty variation is the AFDO variant of a dependency of a module that enabled AFDO // for itself. m.Properties.PreventInstall = true m.Properties.HideFromMake = true m.afdo.Properties.AfdoDep = true m.afdo.addDep(ctx, decodeTarget(variation)) } } } // 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-") }