1// Copyright 2021 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package cc 16 17import ( 18 "fmt" 19 "strings" 20 21 "android/soong/android" 22 23 "github.com/google/blueprint" 24) 25 26// This flag needs to be in both CFlags and LdFlags to ensure correct symbol ordering 27const afdoFlagsFormat = "-fprofile-sample-use=%s -fprofile-sample-accurate" 28 29type AfdoProperties struct { 30 // Afdo allows developers self-service enroll for 31 // automatic feedback-directed optimization using profile data. 32 Afdo bool 33 34 AfdoDep bool `blueprint:"mutated"` 35} 36 37type afdo struct { 38 Properties AfdoProperties 39} 40 41func (afdo *afdo) props() []interface{} { 42 return []interface{}{&afdo.Properties} 43} 44 45func (afdo *afdo) begin(ctx BaseModuleContext) { 46 // Disable on eng builds for faster build. 47 if ctx.Config().Eng() { 48 afdo.Properties.Afdo = false 49 } 50 // Disable for native coverage builds. 51 if ctx.DeviceConfig().NativeCoverageEnabled() { 52 afdo.Properties.Afdo = false 53 } 54} 55 56// afdoEnabled returns true for binaries and shared libraries 57// that set afdo prop to True. 58func (afdo *afdo) afdoEnabled() bool { 59 return afdo != nil && afdo.Properties.Afdo 60} 61 62func (afdo *afdo) isAfdoCompile(ctx ModuleContext) bool { 63 fdoProfilePath := getFdoProfilePathFromDep(ctx) 64 return !ctx.Host() && (afdo.Properties.Afdo || afdo.Properties.AfdoDep) && (fdoProfilePath != "") 65} 66 67func getFdoProfilePathFromDep(ctx ModuleContext) string { 68 fdoProfileDeps := ctx.GetDirectDepsProxyWithTag(FdoProfileTag) 69 if len(fdoProfileDeps) > 0 { 70 if info, ok := android.OtherModuleProvider(ctx, fdoProfileDeps[0], FdoProfileProvider); ok { 71 return info.Path.String() 72 } 73 } 74 return "" 75} 76 77func (afdo *afdo) flags(ctx ModuleContext, flags Flags) Flags { 78 if ctx.Host() { 79 return flags 80 } 81 82 if afdo.Properties.Afdo || afdo.Properties.AfdoDep { 83 // Emit additional debug info for AutoFDO 84 flags.Local.CFlags = append([]string{"-fdebug-info-for-profiling"}, flags.Local.CFlags...) 85 // We use `-funique-internal-linkage-names` to associate profiles to the right internal 86 // functions. This option should be used before generating a profile. Because a profile 87 // generated for a binary without unique names doesn't work well building a binary with 88 // unique names (they have different internal function names). 89 // To avoid a chicken-and-egg problem, we enable `-funique-internal-linkage-names` when 90 // `afdo=true`, whether a profile exists or not. 91 // The profile can take effect in three steps: 92 // 1. Add `afdo: true` in Android.bp, and build the binary. 93 // 2. Collect an AutoFDO profile for the binary. 94 // 3. Make the profile searchable by the build system. So it's used the next time the binary 95 // is built. 96 flags.Local.CFlags = append([]string{"-funique-internal-linkage-names"}, flags.Local.CFlags...) 97 // Flags for Flow Sensitive AutoFDO 98 flags.Local.CFlags = append([]string{"-mllvm", "-enable-fs-discriminator=true"}, flags.Local.CFlags...) 99 flags.Local.LdFlags = append([]string{"-Wl,-mllvm,-enable-fs-discriminator=true"}, flags.Local.LdFlags...) 100 // TODO(b/266595187): Remove the following feature once it is enabled in LLVM by default. 101 flags.Local.CFlags = append([]string{"-mllvm", "-improved-fs-discriminator=true"}, flags.Local.CFlags...) 102 flags.Local.LdFlags = append([]string{"-Wl,-mllvm,-improved-fs-discriminator=true"}, flags.Local.LdFlags...) 103 } 104 if fdoProfilePath := getFdoProfilePathFromDep(ctx); fdoProfilePath != "" { 105 // The flags are prepended to allow overriding. 106 profileUseFlag := fmt.Sprintf(afdoFlagsFormat, fdoProfilePath) 107 flags.Local.CFlags = append([]string{profileUseFlag}, flags.Local.CFlags...) 108 // Salvage stale profile by fuzzy matching and use the remapped location for sample profile query. 109 flags.Local.CFlags = append([]string{"-mllvm", "--salvage-stale-profile=true"}, flags.Local.CFlags...) 110 flags.Local.CFlags = append([]string{"-mllvm", "--salvage-stale-profile-max-callsites=2000"}, flags.Local.CFlags...) 111 // Salvage stale profile by fuzzy matching renamed functions. 112 flags.Local.CFlags = append([]string{"-mllvm", "--salvage-unused-profile=true"}, flags.Local.CFlags...) 113 flags.Local.LdFlags = append([]string{profileUseFlag, "-Wl,-mllvm,-no-warn-sample-unused=true"}, flags.Local.LdFlags...) 114 115 // Update CFlagsDeps and LdFlagsDeps so the module is rebuilt 116 // if profileFile gets updated 117 pathForSrc := android.PathForSource(ctx, fdoProfilePath) 118 flags.CFlagsDeps = append(flags.CFlagsDeps, pathForSrc) 119 flags.LdFlagsDeps = append(flags.LdFlagsDeps, pathForSrc) 120 } 121 122 return flags 123} 124 125func (a *afdo) addDep(ctx android.BottomUpMutatorContext, fdoProfileTarget string) { 126 if fdoProfileName, err := ctx.DeviceConfig().AfdoProfile(fdoProfileTarget); fdoProfileName != "" && err == nil { 127 ctx.AddFarVariationDependencies( 128 []blueprint.Variation{ 129 {Mutator: "arch", Variation: ctx.Target().ArchVariation()}, 130 {Mutator: "os", Variation: "android"}, 131 }, 132 FdoProfileTag, 133 fdoProfileName) 134 } 135} 136 137func afdoPropagateViaDepTag(tag blueprint.DependencyTag) bool { 138 libTag, isLibTag := tag.(libraryDependencyTag) 139 // Do not recurse down non-static dependencies 140 if isLibTag { 141 return libTag.static() 142 } else { 143 return tag == objDepTag || tag == reuseObjTag || tag == staticVariantTag 144 } 145} 146 147// afdoTransitionMutator creates afdo variants of cc modules. 148type afdoTransitionMutator struct{} 149 150func (a *afdoTransitionMutator) Split(ctx android.BaseModuleContext) []string { 151 return []string{""} 152} 153 154func (a *afdoTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string { 155 if ctx.Host() { 156 return "" 157 } 158 159 if m, ok := ctx.Module().(*Module); ok && m.afdo != nil { 160 if !afdoPropagateViaDepTag(ctx.DepTag()) { 161 return "" 162 } 163 164 if sourceVariation != "" { 165 return sourceVariation 166 } 167 168 if !m.afdo.afdoEnabled() { 169 return "" 170 } 171 172 // TODO(b/324141705): this is designed to prevent propagating AFDO from static libraries that have afdo: true set, but 173 // it should be m.staticLibrary() so that static binaries use AFDO variants of dependencies. 174 if m.static() { 175 return "" 176 } 177 178 return encodeTarget(ctx.Module().Name()) 179 } 180 return "" 181} 182 183func (a *afdoTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string { 184 if m, ok := ctx.Module().(*Module); ok && m.afdo != nil { 185 return incomingVariation 186 } 187 return "" 188} 189 190func (a *afdoTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) { 191 if m, ok := ctx.Module().(*Module); ok && m.afdo != nil { 192 if !m.Enabled(ctx) { 193 return 194 } 195 if variation == "" { 196 // The empty variation is either a module that has enabled AFDO for itself, or the non-AFDO 197 // variant of a dependency. 198 if m.afdo.afdoEnabled() && !m.staticLibrary() && !m.Host() { 199 m.afdo.addDep(ctx, ctx.ModuleName()) 200 } 201 } else { 202 // The non-empty variation is the AFDO variant of a dependency of a module that enabled AFDO 203 // for itself. 204 m.Properties.PreventInstall = true 205 m.Properties.HideFromMake = true 206 m.afdo.Properties.AfdoDep = true 207 m.afdo.addDep(ctx, decodeTarget(variation)) 208 } 209 } 210} 211 212// Encode target name to variation name. 213func encodeTarget(target string) string { 214 if target == "" { 215 return "" 216 } 217 return "afdo-" + target 218} 219 220// Decode target name from variation name. 221func decodeTarget(variation string) string { 222 if variation == "" { 223 return "" 224 } 225 return strings.TrimPrefix(variation, "afdo-") 226} 227