From 0ccc2326917e18d439c76f63925e40b7d6bc4149 Mon Sep 17 00:00:00 2001 From: Vinh Tran Date: Thu, 9 Mar 2023 22:07:19 -0500 Subject: [PATCH] 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 --- android/config.go | 15 ++++ android/variable.go | 2 + cc/Android.bp | 2 + cc/afdo.go | 177 +++++++++++++++++++++++++++-------------- cc/afdo_test.go | 189 ++++++++++++++++++++++++++++++++++---------- cc/cc.go | 11 ++- cc/fdo_profile.go | 85 ++++++++++++++++++++ 7 files changed, 375 insertions(+), 106 deletions(-) create mode 100644 cc/fdo_profile.go diff --git a/android/config.go b/android/config.go index 33deba500..0ef94a5fc 100644 --- a/android/config.go +++ b/android/config.go @@ -1418,6 +1418,21 @@ func (c *deviceConfig) PgoAdditionalProfileDirs() []string { return c.config.productVariables.PgoAdditionalProfileDirs } +// AfdoProfile returns fully qualified path associated to the given module name +func (c *deviceConfig) AfdoProfile(name string) (*string, error) { + for _, afdoProfile := range c.config.productVariables.AfdoProfiles { + split := strings.Split(afdoProfile, ":") + if len(split) != 3 { + return nil, fmt.Errorf("AFDO_PROFILES has invalid value: %s. "+ + "The expected format is :", afdoProfile) + } + if split[0] == name { + return proptools.StringPtr(strings.Join([]string{split[1], split[2]}, ":")), nil + } + } + return nil, nil +} + func (c *deviceConfig) VendorSepolicyDirs() []string { return c.config.productVariables.BoardVendorSepolicyDirs } diff --git a/android/variable.go b/android/variable.go index 1da5974ea..249d53b07 100644 --- a/android/variable.go +++ b/android/variable.go @@ -464,6 +464,8 @@ type productVariables struct { IncludeTags []string `json:",omitempty"` SourceRootDirs []string `json:",omitempty"` + + AfdoProfiles []string `json:",omitempty"` } func boolPtr(v bool) *bool { diff --git a/cc/Android.bp b/cc/Android.bp index 5fd9afea9..be2cc5a34 100644 --- a/cc/Android.bp +++ b/cc/Android.bp @@ -21,6 +21,8 @@ bootstrap_go_package { ], srcs: [ "afdo.go", + "fdo_profile.go", + "androidmk.go", "api_level.go", "bp2build.go", diff --git a/cc/afdo.go b/cc/afdo.go index d36f4afcf..0b662ebf4 100644 --- a/cc/afdo.go +++ b/cc/afdo.go @@ -18,9 +18,10 @@ import ( "fmt" "strings" - "github.com/google/blueprint/proptools" - "android/soong/android" + + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" ) var ( @@ -34,6 +35,7 @@ var afdoProfileProjectsConfigKey = android.NewOnceKey("AfdoProfileProjects") 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 @@ func recordMissingAfdoProfileFile(ctx android.BaseModuleContext, missing string) 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 @@ func (afdo *afdo) props() []interface{} { 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 @@ func (afdo *afdo) AfdoEnabled() bool { // 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 @@ func getProfileFiles(ctx android.BaseModuleContext, moduleName string) []string 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 @@ func (props *AfdoProperties) GetAfdoProfileFile(ctx android.BaseModuleContext, m 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 @@ func afdoMutator(mctx android.BottomUpMutatorContext) { 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] } } } diff --git a/cc/afdo_test.go b/cc/afdo_test.go index 40f705b87..ef95b3f6b 100644 --- a/cc/afdo_test.go +++ b/cc/afdo_test.go @@ -23,6 +23,11 @@ import ( "github.com/google/blueprint" ) +var prepareForTestWithFdoProfile = android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { + ctx.RegisterModuleType("soong_namespace", android.NamespaceFactory) + ctx.RegisterModuleType("fdo_profile", fdoProfileFactory) +}) + type visitDirectDepsInterface interface { VisitDirectDeps(blueprint.Module, func(dep blueprint.Module)) } @@ -58,38 +63,77 @@ func TestAfdoDeps(t *testing.T) { srcs: ["bar.c"], } ` - prepareForAfdoTest := android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libTest.afdo", "TEST") result := android.GroupFixturePreparers( + prepareForTestWithFdoProfile, prepareForCcTest, - prepareForAfdoTest, + android.FixtureAddTextFile("afdo_profiles_package/libTest.afdo", ""), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.AfdoProfiles = []string{ + "libTest://afdo_profiles_package:libTest_afdo", + } + }), + android.MockFS{ + "afdo_profiles_package/Android.bp": []byte(` + fdo_profile { + name: "libTest_afdo", + profile: "libTest.afdo", + } + `), + }.AddToFixture(), ).RunTestWithBp(t, bp) - libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared") - libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest") - libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static_afdo-libTest") + expectedCFlag := "-fprofile-sample-use=afdo_profiles_package/libTest.afdo" - if !hasDirectDep(result, libTest.Module(), libFoo.Module()) { + libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared") + libFooAfdoVariant := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest") + libBarAfdoVariant := result.ModuleForTests("libBar", "android_arm64_armv8-a_static_afdo-libTest") + + // Check cFlags of afdo-enabled module and the afdo-variant of its static deps + cFlags := libTest.Rule("cc").Args["cFlags"] + if !strings.Contains(cFlags, expectedCFlag) { + t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags) + } + + cFlags = libFooAfdoVariant.Rule("cc").Args["cFlags"] + if !strings.Contains(cFlags, expectedCFlag) { + t.Errorf("Expected 'libFooAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags) + } + + cFlags = libBarAfdoVariant.Rule("cc").Args["cFlags"] + if !strings.Contains(cFlags, expectedCFlag) { + t.Errorf("Expected 'libBarAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags) + } + + // Check dependency edge from afdo-enabled module to static deps + if !hasDirectDep(result, libTest.Module(), libFooAfdoVariant.Module()) { t.Errorf("libTest missing dependency on afdo variant of libFoo") } - if !hasDirectDep(result, libFoo.Module(), libBar.Module()) { + if !hasDirectDep(result, libFooAfdoVariant.Module(), libBarAfdoVariant.Module()) { t.Errorf("libTest missing dependency on afdo variant of libBar") } - cFlags := libTest.Rule("cc").Args["cFlags"] - if w := "-fprofile-sample-accurate"; !strings.Contains(cFlags, w) { - t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", w, cFlags) - } + // Verify non-afdo variant exists and doesn't contain afdo + libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static") + libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static") cFlags = libFoo.Rule("cc").Args["cFlags"] - if w := "-fprofile-sample-accurate"; !strings.Contains(cFlags, w) { - t.Errorf("Expected 'libFoo' to enable afdo, but did not find %q in cflags %q", w, cFlags) + if strings.Contains(cFlags, expectedCFlag) { + t.Errorf("Expected 'libFoo' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags) + } + cFlags = libBar.Rule("cc").Args["cFlags"] + if strings.Contains(cFlags, expectedCFlag) { + t.Errorf("Expected 'libBar' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags) } - cFlags = libBar.Rule("cc").Args["cFlags"] - if w := "-fprofile-sample-accurate"; !strings.Contains(cFlags, w) { - t.Errorf("Expected 'libBar' to enable afdo, but did not find %q in cflags %q", w, cFlags) + // Check dependency edges of static deps + if hasDirectDep(result, libTest.Module(), libFoo.Module()) { + t.Errorf("libTest should not depend on non-afdo variant of libFoo") + } + + if !hasDirectDep(result, libFoo.Module(), libBar.Module()) { + t.Errorf("libFoo missing dependency on non-afdo variant of libBar") } } @@ -113,11 +157,21 @@ func TestAfdoEnabledOnStaticDepNoAfdo(t *testing.T) { name: "libBar", } ` - prepareForAfdoTest := android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libFoo.afdo", "TEST") result := android.GroupFixturePreparers( prepareForCcTest, - prepareForAfdoTest, + prepareForTestWithFdoProfile, + android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libFoo.afdo", ""), + android.MockFS{ + "afdo_profiles_package/Android.bp": []byte(` + soong_namespace { + } + fdo_profile { + name: "libFoo_afdo", + profile: "libFoo.afdo", + } + `), + }.AddToFixture(), ).RunTestWithBp(t, bp) libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared").Module() @@ -150,7 +204,6 @@ func TestAfdoEnabledOnStaticDepNoAfdo(t *testing.T) { t.Errorf("Expected no afdo variant of 'bar', got %q", v) } } - } func TestAfdoEnabledWithRuntimeDepNoAfdo(t *testing.T) { @@ -166,11 +219,24 @@ func TestAfdoEnabledWithRuntimeDepNoAfdo(t *testing.T) { name: "libFoo", } ` - prepareForAfdoTest := android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libTest.afdo", "TEST") result := android.GroupFixturePreparers( prepareForCcTest, - prepareForAfdoTest, + prepareForTestWithFdoProfile, + android.FixtureAddTextFile("afdo_profiles_package/libTest.afdo", ""), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.AfdoProfiles = []string{ + "libTest://afdo_profiles_package:libTest_afdo", + } + }), + android.MockFS{ + "afdo_profiles_package/Android.bp": []byte(` + fdo_profile { + name: "libTest_afdo", + profile: "libTest.afdo", + } + `), + }.AddToFixture(), ).RunTestWithBp(t, bp) libFooVariants := result.ModuleVariantsForTests("libFoo") @@ -182,7 +248,6 @@ func TestAfdoEnabledWithRuntimeDepNoAfdo(t *testing.T) { } func TestAfdoEnabledWithMultiArchs(t *testing.T) { - t.Parallel() bp := ` cc_library_shared { name: "foo", @@ -192,20 +257,43 @@ func TestAfdoEnabledWithMultiArchs(t *testing.T) { } ` result := android.GroupFixturePreparers( + prepareForTestWithFdoProfile, prepareForCcTest, - android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/foo_arm.afdo", "TEST"), - android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/foo_arm64.afdo", "TEST"), + android.FixtureAddTextFile("afdo_profiles_package/foo_arm.afdo", ""), + android.FixtureAddTextFile("afdo_profiles_package/foo_arm64.afdo", ""), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.AfdoProfiles = []string{ + "foo://afdo_profiles_package:foo_afdo", + } + }), + android.MockFS{ + "afdo_profiles_package/Android.bp": []byte(` + soong_namespace { + } + fdo_profile { + name: "foo_afdo", + arch: { + arm: { + profile: "foo_arm.afdo", + }, + arm64: { + profile: "foo_arm64.afdo", + } + } + } + `), + }.AddToFixture(), ).RunTestWithBp(t, bp) fooArm := result.ModuleForTests("foo", "android_arm_armv7-a-neon_shared") fooArmCFlags := fooArm.Rule("cc").Args["cFlags"] - if w := "-fprofile-sample-use=toolchain/pgo-profiles/sampling/foo_arm.afdo"; !strings.Contains(fooArmCFlags, w) { + if w := "-fprofile-sample-use=afdo_profiles_package/foo_arm.afdo"; !strings.Contains(fooArmCFlags, w) { t.Errorf("Expected 'foo' to enable afdo, but did not find %q in cflags %q", w, fooArmCFlags) } fooArm64 := result.ModuleForTests("foo", "android_arm64_armv8-a_shared") fooArm64CFlags := fooArm64.Rule("cc").Args["cFlags"] - if w := "-fprofile-sample-use=toolchain/pgo-profiles/sampling/foo_arm64.afdo"; !strings.Contains(fooArm64CFlags, w) { + if w := "-fprofile-sample-use=afdo_profiles_package/foo_arm64.afdo"; !strings.Contains(fooArm64CFlags, w) { t.Errorf("Expected 'foo' to enable afdo, but did not find %q in cflags %q", w, fooArm64CFlags) } } @@ -234,46 +322,65 @@ func TestMultipleAfdoRDeps(t *testing.T) { ` result := android.GroupFixturePreparers( + prepareForTestWithFdoProfile, prepareForCcTest, - android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libTest.afdo", "TEST"), - android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libBar.afdo", "TEST"), + android.FixtureAddTextFile("afdo_profiles_package/libTest.afdo", ""), + android.FixtureAddTextFile("afdo_profiles_package/libBar.afdo", ""), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.AfdoProfiles = []string{ + "libTest://afdo_profiles_package:libTest_afdo", + "libBar://afdo_profiles_package:libBar_afdo", + } + }), + android.MockFS{ + "afdo_profiles_package/Android.bp": []byte(` + fdo_profile { + name: "libTest_afdo", + profile: "libTest.afdo", + } + fdo_profile { + name: "libBar_afdo", + profile: "libBar.afdo", + } + `), + }.AddToFixture(), ).RunTestWithBp(t, bp) - expectedCFlagLibTest := "-fprofile-sample-use=toolchain/pgo-profiles/sampling/libTest.afdo" - expectedCFlagLibBar := "-fprofile-sample-use=toolchain/pgo-profiles/sampling/libBar.afdo" + expectedCFlagLibTest := "-fprofile-sample-use=afdo_profiles_package/libTest.afdo" + expectedCFlagLibBar := "-fprofile-sample-use=afdo_profiles_package/libBar.afdo" libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared") - libTestAfdoVariantOfLibFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest") + libFooAfdoVariantWithLibTest := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest") libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_shared") - libBarAfdoVariantOfLibFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libBar") + libFooAfdoVariantWithLibBar := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libBar") - // Check cFlags of afdo-enabled modules and the afdo-variant of their static deps + // Check cFlags of afdo-enabled module and the afdo-variant of its static deps cFlags := libTest.Rule("cc").Args["cFlags"] if !strings.Contains(cFlags, expectedCFlagLibTest) { t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibTest, cFlags) } cFlags = libBar.Rule("cc").Args["cFlags"] if !strings.Contains(cFlags, expectedCFlagLibBar) { - t.Errorf("Expected 'libBar' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibBar, cFlags) + t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibBar, cFlags) } - cFlags = libTestAfdoVariantOfLibFoo.Rule("cc").Args["cFlags"] + cFlags = libFooAfdoVariantWithLibTest.Rule("cc").Args["cFlags"] if !strings.Contains(cFlags, expectedCFlagLibTest) { - t.Errorf("Expected 'libTestAfdoVariantOfLibFoo' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibTest, cFlags) + t.Errorf("Expected 'libFooAfdoVariantWithLibTest' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibTest, cFlags) } - cFlags = libBarAfdoVariantOfLibFoo.Rule("cc").Args["cFlags"] + cFlags = libFooAfdoVariantWithLibBar.Rule("cc").Args["cFlags"] if !strings.Contains(cFlags, expectedCFlagLibBar) { - t.Errorf("Expected 'libBarAfdoVariantOfLibFoo' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibBar, cFlags) + t.Errorf("Expected 'libBarAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibBar, cFlags) } // Check dependency edges of static deps - if !hasDirectDep(result, libTest.Module(), libTestAfdoVariantOfLibFoo.Module()) { + if !hasDirectDep(result, libTest.Module(), libFooAfdoVariantWithLibTest.Module()) { t.Errorf("libTest missing dependency on afdo variant of libFoo") } - if !hasDirectDep(result, libBar.Module(), libBarAfdoVariantOfLibFoo.Module()) { - t.Errorf("libBar missing dependency on afdo variant of libFoo") + if !hasDirectDep(result, libBar.Module(), libFooAfdoVariantWithLibBar.Module()) { + t.Errorf("libFoo missing dependency on non-afdo variant of libBar") } } diff --git a/cc/cc.go b/cc/cc.go index b029d712e..9c555a11e 100644 --- a/cc/cc.go +++ b/cc/cc.go @@ -52,6 +52,7 @@ func RegisterCCBuildComponents(ctx android.RegistrationContext) { ctx.BottomUp("test_per_src", TestPerSrcMutator).Parallel() ctx.BottomUp("version", versionMutator).Parallel() ctx.BottomUp("begin", BeginMutator).Parallel() + ctx.BottomUp("fdo_profile", fdoProfileMutator) }) ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { @@ -763,6 +764,7 @@ var ( testPerSrcDepTag = dependencyTag{name: "test_per_src"} stubImplDepTag = dependencyTag{name: "stub_impl"} JniFuzzLibTag = dependencyTag{name: "jni_fuzz_lib_tag"} + FdoProfileTag = dependencyTag{name: "fdo_profile"} ) func IsSharedDepTag(depTag blueprint.DependencyTag) bool { @@ -1336,7 +1338,7 @@ func (c *Module) IsVndk() bool { func (c *Module) isAfdoCompile() bool { if afdo := c.afdo; afdo != nil { - return afdo.Properties.AfdoTarget != nil + return afdo.Properties.FdoProfilePath != nil } return false } @@ -2162,9 +2164,6 @@ func (c *Module) begin(ctx BaseModuleContext) { if c.lto != nil { c.lto.begin(ctx) } - if c.afdo != nil { - c.afdo.begin(ctx) - } if c.pgo != nil { c.pgo.begin(ctx) } @@ -2239,6 +2238,10 @@ func (c *Module) beginMutator(actx android.BottomUpMutatorContext) { } ctx.ctx = ctx + if !actx.Host() || !ctx.static() || ctx.staticBinary() { + c.afdo.addDep(ctx, actx) + } + c.begin(ctx) } diff --git a/cc/fdo_profile.go b/cc/fdo_profile.go new file mode 100644 index 000000000..18af8b593 --- /dev/null +++ b/cc/fdo_profile.go @@ -0,0 +1,85 @@ +// Copyright 2023 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cc + +import ( + "android/soong/android" + + "github.com/google/blueprint" +) + +func init() { + RegisterFdoProfileBuildComponents(android.InitRegistrationContext) +} + +func RegisterFdoProfileBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("fdo_profile", fdoProfileFactory) +} + +type fdoProfile struct { + android.ModuleBase + + properties fdoProfileProperties +} + +type fdoProfileProperties struct { + Profile *string `android:"arch_variant"` +} + +// FdoProfileInfo is provided by FdoProfileProvider +type FdoProfileInfo struct { + Path android.Path +} + +// FdoProfileProvider is used to provide path to an fdo profile +var FdoProfileProvider = blueprint.NewMutatorProvider(FdoProfileInfo{}, "fdo_profile") + +// FdoProfileMutatorInterface is the interface implemented by fdo_profile module type +// module types that can depend on an fdo_profile module +type FdoProfileMutatorInterface interface { + // FdoProfileMutator eithers set or get FdoProfileProvider + FdoProfileMutator(ctx android.BottomUpMutatorContext) +} + +var _ FdoProfileMutatorInterface = (*fdoProfile)(nil) + +// GenerateAndroidBuildActions of fdo_profile does not have any build actions +func (fp *fdoProfile) GenerateAndroidBuildActions(ctx android.ModuleContext) {} + +// FdoProfileMutator sets FdoProfileProvider to fdo_profile module +// or sets afdo.Properties.FdoProfilePath to path in FdoProfileProvider of the depended fdo_profile +func (fp *fdoProfile) FdoProfileMutator(ctx android.BottomUpMutatorContext) { + if fp.properties.Profile != nil { + path := android.PathForModuleSrc(ctx, *fp.properties.Profile) + ctx.SetProvider(FdoProfileProvider, FdoProfileInfo{ + Path: path, + }) + } +} + +// fdoProfileMutator calls the generic FdoProfileMutator function of FdoProfileMutator +// which is implemented by cc and cc.FdoProfile +func fdoProfileMutator(ctx android.BottomUpMutatorContext) { + if f, ok := ctx.Module().(FdoProfileMutatorInterface); ok { + f.FdoProfileMutator(ctx) + } +} + +func fdoProfileFactory() android.Module { + m := &fdoProfile{} + m.AddProperties(&m.properties) + android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibBoth) + return m +}