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 "github.com/google/blueprint/proptools" 22 23 "android/soong/android" 24) 25 26var ( 27 globalAfdoProfileProjects = []string{ 28 "vendor/google_data/pgo_profile/sampling/", 29 "toolchain/pgo-profiles/sampling/", 30 } 31) 32 33var afdoProfileProjectsConfigKey = android.NewOnceKey("AfdoProfileProjects") 34 35const afdoCFlagsFormat = "-funique-internal-linkage-names -fprofile-sample-accurate -fprofile-sample-use=%s" 36 37func getAfdoProfileProjects(config android.DeviceConfig) []string { 38 return config.OnceStringSlice(afdoProfileProjectsConfigKey, func() []string { 39 return append(globalAfdoProfileProjects, config.AfdoAdditionalProfileDirs()...) 40 }) 41} 42 43func recordMissingAfdoProfileFile(ctx android.BaseModuleContext, missing string) { 44 getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true) 45} 46 47type AfdoProperties struct { 48 // Afdo allows developers self-service enroll for 49 // automatic feedback-directed optimization using profile data. 50 Afdo bool 51 52 AfdoTarget *string `blueprint:"mutated"` 53 AfdoDeps []string `blueprint:"mutated"` 54} 55 56type afdo struct { 57 Properties AfdoProperties 58} 59 60func (afdo *afdo) props() []interface{} { 61 return []interface{}{&afdo.Properties} 62} 63 64func (afdo *afdo) AfdoEnabled() bool { 65 return afdo != nil && afdo.Properties.Afdo && afdo.Properties.AfdoTarget != nil 66} 67 68// Get list of profile file names, ordered by level of specialisation. For example: 69// 1. libfoo_arm64.afdo 70// 2. libfoo.afdo 71// Add more specialisation as needed. 72func getProfileFiles(ctx android.BaseModuleContext, moduleName string) []string { 73 var files []string 74 files = append(files, moduleName+"_"+ctx.Arch().ArchType.String()+".afdo") 75 files = append(files, moduleName+".afdo") 76 return files 77} 78 79func (props *AfdoProperties) GetAfdoProfileFile(ctx android.BaseModuleContext, module string) android.OptionalPath { 80 // Test if the profile_file is present in any of the Afdo profile projects 81 for _, profileFile := range getProfileFiles(ctx, module) { 82 for _, profileProject := range getAfdoProfileProjects(ctx.DeviceConfig()) { 83 path := android.ExistentPathForSource(ctx, profileProject, profileFile) 84 if path.Valid() { 85 return path 86 } 87 } 88 } 89 90 // Record that this module's profile file is absent 91 missing := ctx.ModuleDir() + ":" + module 92 recordMissingAfdoProfileFile(ctx, missing) 93 94 return android.OptionalPathForPath(nil) 95} 96 97func (afdo *afdo) begin(ctx BaseModuleContext) { 98 if ctx.Host() { 99 return 100 } 101 if ctx.static() && !ctx.staticBinary() { 102 return 103 } 104 if afdo.Properties.Afdo { 105 module := ctx.ModuleName() 106 if afdo.Properties.GetAfdoProfileFile(ctx, module).Valid() { 107 afdo.Properties.AfdoTarget = proptools.StringPtr(module) 108 } 109 } 110} 111 112func (afdo *afdo) flags(ctx ModuleContext, flags Flags) Flags { 113 if profile := afdo.Properties.AfdoTarget; profile != nil { 114 if profileFile := afdo.Properties.GetAfdoProfileFile(ctx, *profile); profileFile.Valid() { 115 profileFilePath := profileFile.Path() 116 117 profileUseFlag := fmt.Sprintf(afdoCFlagsFormat, profileFile) 118 flags.Local.CFlags = append(flags.Local.CFlags, profileUseFlag) 119 flags.Local.LdFlags = append(flags.Local.LdFlags, profileUseFlag) 120 flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,-no-warn-sample-unused=true") 121 122 // Update CFlagsDeps and LdFlagsDeps so the module is rebuilt 123 // if profileFile gets updated 124 flags.CFlagsDeps = append(flags.CFlagsDeps, profileFilePath) 125 flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFilePath) 126 } 127 } 128 129 return flags 130} 131 132// Propagate afdo requirements down from binaries 133func afdoDepsMutator(mctx android.TopDownMutatorContext) { 134 if m, ok := mctx.Module().(*Module); ok && m.afdo.AfdoEnabled() { 135 afdoTarget := *m.afdo.Properties.AfdoTarget 136 mctx.WalkDeps(func(dep android.Module, parent android.Module) bool { 137 tag := mctx.OtherModuleDependencyTag(dep) 138 libTag, isLibTag := tag.(libraryDependencyTag) 139 140 // Do not recurse down non-static dependencies 141 if isLibTag { 142 if !libTag.static() { 143 return false 144 } 145 } else { 146 if tag != objDepTag && tag != reuseObjTag { 147 return false 148 } 149 } 150 151 if dep, ok := dep.(*Module); ok { 152 dep.afdo.Properties.AfdoDeps = append(dep.afdo.Properties.AfdoDeps, afdoTarget) 153 } 154 155 return true 156 }) 157 } 158} 159 160// Create afdo variants for modules that need them 161func afdoMutator(mctx android.BottomUpMutatorContext) { 162 if m, ok := mctx.Module().(*Module); ok && m.afdo != nil { 163 if m.afdo.AfdoEnabled() && !m.static() { 164 afdoTarget := *m.afdo.Properties.AfdoTarget 165 mctx.SetDependencyVariation(encodeTarget(afdoTarget)) 166 } 167 168 variationNames := []string{""} 169 afdoDeps := android.FirstUniqueStrings(m.afdo.Properties.AfdoDeps) 170 for _, dep := range afdoDeps { 171 variationNames = append(variationNames, encodeTarget(dep)) 172 } 173 if len(variationNames) > 1 { 174 modules := mctx.CreateVariations(variationNames...) 175 for i, name := range variationNames { 176 if name == "" { 177 continue 178 } 179 variation := modules[i].(*Module) 180 variation.Properties.PreventInstall = true 181 variation.Properties.HideFromMake = true 182 variation.afdo.Properties.AfdoTarget = proptools.StringPtr(decodeTarget(name)) 183 } 184 } 185 } 186} 187 188// Encode target name to variation name. 189func encodeTarget(target string) string { 190 if target == "" { 191 return "" 192 } 193 return "afdo-" + target 194} 195 196// Decode target name from variation name. 197func decodeTarget(variation string) string { 198 if variation == "" { 199 return "" 200 } 201 return strings.TrimPrefix(variation, "afdo-") 202} 203