• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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