• 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	"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