Implement fdo_profile module type

Introducing fdo_profile module type to reimplement the afdo support in cc moduels. This change allows the feature to be compatible with Bazel migration.

How it works:

PreDepsMutators:
  * BeginMutator: If non-static cc modules sets afdo prop, search and add corresponding fdo_profile module as a dep with fdoProfileTag
  * fdoProfileMutator:
    * If in fdo_profile module, set FdoProfileProvider with full path to profile
    * If in cc module, read FdoProfileProvider from dep with fdoProfileTag and set FdoProfileInfo.Path to FdoProfilePath field

PostDepsMutators:
  * afdoDepsMutator: If a module has FdoProfilePath set, walk to its static deps and set itself to the deps' AfdoRdeps
  * afdoMutator: If a static dep has AfdoRDeps set, create afdo variant.

Ignore-AOSP-First: Other CLs in the same topic are internal-only
Test: go test
Bug: b/267229065
Change-Id: I687d798a02d9743c92804fea36fb4ae3a7a0e5e3
Merged-In: I687d798a02d9743c92804fea36fb4ae3a7a0e5e3
diff --git a/cc/afdo.go b/cc/afdo.go
index d36f4af..0b662eb 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -18,9 +18,10 @@
 	"fmt"
 	"strings"
 
-	"github.com/google/blueprint/proptools"
-
 	"android/soong/android"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 var (
@@ -34,6 +35,7 @@
 
 const afdoCFlagsFormat = "-funique-internal-linkage-names -fprofile-sample-accurate -fprofile-sample-use=%s"
 
+// TODO(b/267229065): Remove getAfdoProfileProjects after reimplementing afdo support for rust
 func getAfdoProfileProjects(config android.DeviceConfig) []string {
 	return config.OnceStringSlice(afdoProfileProjectsConfigKey, func() []string {
 		return globalAfdoProfileProjects
@@ -44,13 +46,19 @@
 	getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true)
 }
 
+type afdoRdep struct {
+	VariationName *string
+	ProfilePath   *string
+}
+
 type AfdoProperties struct {
 	// Afdo allows developers self-service enroll for
 	// automatic feedback-directed optimization using profile data.
 	Afdo bool
 
-	AfdoTarget *string  `blueprint:"mutated"`
-	AfdoDeps   []string `blueprint:"mutated"`
+	FdoProfilePath *string `blueprint:"mutated"`
+
+	AfdoRDeps []afdoRdep `blueprint:"mutated"`
 }
 
 type afdo struct {
@@ -61,8 +69,10 @@
 	return []interface{}{&afdo.Properties}
 }
 
-func (afdo *afdo) AfdoEnabled() bool {
-	return afdo != nil && afdo.Properties.Afdo && afdo.Properties.AfdoTarget != nil
+// afdoEnabled returns true for binaries and shared libraries
+// that set afdo prop to True and there is a profile available
+func (afdo *afdo) afdoEnabled() bool {
+	return afdo != nil && afdo.Properties.Afdo && afdo.Properties.FdoProfilePath != nil
 }
 
 // Get list of profile file names, ordered by level of specialisation. For example:
@@ -70,6 +80,7 @@
 //  2. libfoo.afdo
 //
 // Add more specialisation as needed.
+// TODO(b/267229065): Remove getProfileFiles after reimplementing afdo support for rust
 func getProfileFiles(ctx android.BaseModuleContext, moduleName string) []string {
 	var files []string
 	files = append(files, moduleName+"_"+ctx.Arch().ArchType.String()+".afdo")
@@ -77,6 +88,7 @@
 	return files
 }
 
+// TODO(b/267229065): Remove GetAfdoProfileFile after reimplementing afdo support for rust
 func (props *AfdoProperties) GetAfdoProfileFile(ctx android.BaseModuleContext, module string) android.OptionalPath {
 	// Test if the profile_file is present in any of the Afdo profile projects
 	for _, profileFile := range getProfileFiles(ctx, module) {
@@ -95,82 +107,125 @@
 	return android.OptionalPathForPath(nil)
 }
 
-func (afdo *afdo) begin(ctx BaseModuleContext) {
-	if ctx.Host() {
-		return
-	}
-	if ctx.static() && !ctx.staticBinary() {
-		return
-	}
-	if afdo.Properties.Afdo {
-		module := ctx.ModuleName()
-		if afdo.Properties.GetAfdoProfileFile(ctx, module).Valid() {
-			afdo.Properties.AfdoTarget = proptools.StringPtr(module)
-		}
-	}
-}
-
 func (afdo *afdo) flags(ctx ModuleContext, flags Flags) Flags {
-	if profile := afdo.Properties.AfdoTarget; profile != nil {
-		if profileFile := afdo.Properties.GetAfdoProfileFile(ctx, *profile); profileFile.Valid() {
-			profileFilePath := profileFile.Path()
+	if path := afdo.Properties.FdoProfilePath; path != nil {
+		profileUseFlag := fmt.Sprintf(afdoCFlagsFormat, *path)
+		flags.Local.CFlags = append(flags.Local.CFlags, profileUseFlag)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, profileUseFlag)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,-no-warn-sample-unused=true")
 
-			profileUseFlag := fmt.Sprintf(afdoCFlagsFormat, profileFile)
-			flags.Local.CFlags = append(flags.Local.CFlags, profileUseFlag)
-			flags.Local.LdFlags = append(flags.Local.LdFlags, profileUseFlag)
-			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,-no-warn-sample-unused=true")
-
-			// Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
-			// if profileFile gets updated
-			flags.CFlagsDeps = append(flags.CFlagsDeps, profileFilePath)
-			flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFilePath)
-		}
+		// Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
+		// if profileFile gets updated
+		pathForSrc := android.PathForSource(ctx, *path)
+		flags.CFlagsDeps = append(flags.CFlagsDeps, pathForSrc)
+		flags.LdFlagsDeps = append(flags.LdFlagsDeps, pathForSrc)
 	}
 
 	return flags
 }
 
-// Propagate afdo requirements down from binaries
+func (afdo *afdo) addDep(ctx BaseModuleContext, actx android.BottomUpMutatorContext) {
+	if ctx.Host() {
+		return
+	}
+
+	if ctx.static() && !ctx.staticBinary() {
+		return
+	}
+
+	if c, ok := ctx.Module().(*Module); ok && c.Enabled() {
+		if fdoProfileName, err := actx.DeviceConfig().AfdoProfile(actx.ModuleName()); fdoProfileName != nil && err == nil {
+			actx.AddFarVariationDependencies(
+				[]blueprint.Variation{
+					{Mutator: "arch", Variation: actx.Target().ArchVariation()},
+					{Mutator: "os", Variation: "android"},
+				},
+				FdoProfileTag,
+				[]string{*fdoProfileName}...,
+			)
+		}
+	}
+}
+
+// FdoProfileMutator reads the FdoProfileProvider from a direct dep with FdoProfileTag
+// assigns FdoProfileInfo.Path to the FdoProfilePath mutated property
+func (c *Module) FdoProfileMutator(ctx android.BottomUpMutatorContext) {
+	if !c.Enabled() {
+		return
+	}
+
+	ctx.VisitDirectDepsWithTag(FdoProfileTag, func(m android.Module) {
+		if ctx.OtherModuleHasProvider(m, FdoProfileProvider) {
+			info := ctx.OtherModuleProvider(m, FdoProfileProvider).(FdoProfileInfo)
+			c.afdo.Properties.FdoProfilePath = proptools.StringPtr(info.Path.String())
+		}
+	})
+}
+
+var _ FdoProfileMutatorInterface = (*Module)(nil)
+
+// Propagate afdo requirements down from binaries and shared libraries
 func afdoDepsMutator(mctx android.TopDownMutatorContext) {
-	if m, ok := mctx.Module().(*Module); ok && m.afdo.AfdoEnabled() {
-		afdoTarget := *m.afdo.Properties.AfdoTarget
-		mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
-			tag := mctx.OtherModuleDependencyTag(dep)
-			libTag, isLibTag := tag.(libraryDependencyTag)
+	if m, ok := mctx.Module().(*Module); ok && m.afdo.afdoEnabled() {
+		if path := m.afdo.Properties.FdoProfilePath; path != nil {
+			mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
+				tag := mctx.OtherModuleDependencyTag(dep)
+				libTag, isLibTag := tag.(libraryDependencyTag)
 
-			// Do not recurse down non-static dependencies
-			if isLibTag {
-				if !libTag.static() {
-					return false
+				// Do not recurse down non-static dependencies
+				if isLibTag {
+					if !libTag.static() {
+						return false
+					}
+				} else {
+					if tag != objDepTag && tag != reuseObjTag {
+						return false
+					}
 				}
-			} else {
-				if tag != objDepTag && tag != reuseObjTag {
-					return false
+
+				if dep, ok := dep.(*Module); ok {
+					dep.afdo.Properties.AfdoRDeps = append(
+						dep.afdo.Properties.AfdoRDeps,
+						afdoRdep{
+							VariationName: proptools.StringPtr(encodeTarget(m.Name())),
+							ProfilePath:   path,
+						},
+					)
 				}
-			}
 
-			if dep, ok := dep.(*Module); ok {
-				dep.afdo.Properties.AfdoDeps = append(dep.afdo.Properties.AfdoDeps, afdoTarget)
-			}
-
-			return true
-		})
+				return true
+			})
+		}
 	}
 }
 
 // Create afdo variants for modules that need them
 func afdoMutator(mctx android.BottomUpMutatorContext) {
 	if m, ok := mctx.Module().(*Module); ok && m.afdo != nil {
-		if m.afdo.AfdoEnabled() && !m.static() {
-			afdoTarget := *m.afdo.Properties.AfdoTarget
-			mctx.SetDependencyVariation(encodeTarget(afdoTarget))
+		if !m.static() && m.afdo.Properties.Afdo && m.afdo.Properties.FdoProfilePath != nil {
+			mctx.SetDependencyVariation(encodeTarget(m.Name()))
+			return
 		}
 
 		variationNames := []string{""}
-		afdoDeps := android.FirstUniqueStrings(m.afdo.Properties.AfdoDeps)
-		for _, dep := range afdoDeps {
-			variationNames = append(variationNames, encodeTarget(dep))
+
+		variantNameToProfilePath := make(map[string]*string)
+
+		for _, afdoRDep := range m.afdo.Properties.AfdoRDeps {
+			variantName := *afdoRDep.VariationName
+			// An rdep can be set twice in AfdoRDeps because there can be
+			// more than one path from an afdo-enabled module to
+			// a static dep such as
+			// afdo_enabled_foo -> static_bar ----> static_baz
+			//                   \                      ^
+			//                    ----------------------|
+			// We only need to create one variant per unique rdep
+			if variantNameToProfilePath[variantName] == nil {
+				variationNames = append(variationNames, variantName)
+				variantNameToProfilePath[variantName] = afdoRDep.ProfilePath
+			}
 		}
+
 		if len(variationNames) > 1 {
 			modules := mctx.CreateVariations(variationNames...)
 			for i, name := range variationNames {
@@ -180,7 +235,7 @@
 				variation := modules[i].(*Module)
 				variation.Properties.PreventInstall = true
 				variation.Properties.HideFromMake = true
-				variation.afdo.Properties.AfdoTarget = proptools.StringPtr(decodeTarget(name))
+				variation.afdo.Properties.FdoProfilePath = variantNameToProfilePath[name]
 			}
 		}
 	}