Add PGO support to soong

Bug: http://b/63768402
Bug: http://b/65598278

Add support for the 'pgo' property to specify how a module is processed
under PGO.  A sample property is below:

pgo: {
    instrumentation: true, // could be "sampling: true" when supported
    profile_file: "pgo_simple.profdata",
    benchmarks: ["pgo_simple"],
}

1. Runtime profiles can be gathered using "sampling" or
"instrumentation".  Sampling is not supported initially.

2. If 'toolchain/pgo-profiles' project is found,
'toolchain/pgo-profiles/${profile_file}' is passed to the compiler and
linker when building this module.

3. If ANDROID_PGO_INSTRUMENT environment variable is set, and includes a
benchmark in the 'benchmarks' list, appropriate flags (for e.g.
-fprofile-generate for instrumentation) are passed to the compiler and
linker when building this module.

Test: Add example modules that specify the pgo property and verify
appropriate flags and dependencies in the Ninja file.  Some
tests/examples are in https://android-review.googlesource.com/474805

Change-Id: I6242e0c904497a115e367dea6927ba1c4b906355
diff --git a/cc/binary.go b/cc/binary.go
index 4b10070..b2405b6 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -325,6 +325,7 @@
 	linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
 	linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
 	linkerDeps = append(linkerDeps, objs.tidyFiles...)
+	linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
 
 	TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs,
 		deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true,
diff --git a/cc/cc.go b/cc/cc.go
index af58f9d..b4b70ed 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -134,7 +134,8 @@
 	RequiredInstructionSet string
 	DynamicLinker          string
 
-	CFlagsDeps android.Paths // Files depended on by compiler flags
+	CFlagsDeps  android.Paths // Files depended on by compiler flags
+	LdFlagsDeps android.Paths // Files depended on by linker flags
 
 	GroupStaticLibs bool
 }
@@ -308,6 +309,7 @@
 	sabi      *sabi
 	vndkdep   *vndkdep
 	lto       *lto
+	pgo       *pgo
 
 	androidMkSharedLibDeps []string
 
@@ -350,6 +352,9 @@
 	if c.lto != nil {
 		c.AddProperties(c.lto.props()...)
 	}
+	if c.pgo != nil {
+		c.AddProperties(c.pgo.props()...)
+	}
 	for _, feature := range c.features {
 		c.AddProperties(feature.props()...)
 	}
@@ -506,6 +511,7 @@
 	module.sabi = &sabi{}
 	module.vndkdep = &vndkdep{}
 	module.lto = &lto{}
+	module.pgo = &pgo{}
 	return module
 }
 
@@ -557,6 +563,9 @@
 	if c.lto != nil {
 		flags = c.lto.flags(ctx, flags)
 	}
+	if c.pgo != nil {
+		flags = c.pgo.flags(ctx, flags)
+	}
 	for _, feature := range c.features {
 		flags = feature.flags(ctx, flags)
 	}
@@ -643,6 +652,9 @@
 	if c.lto != nil {
 		c.lto.begin(ctx)
 	}
+	if c.pgo != nil {
+		c.pgo.begin(ctx)
+	}
 	for _, feature := range c.features {
 		feature.begin(ctx)
 	}
@@ -1250,6 +1262,7 @@
 		&SAbiProperties{},
 		&VndkProperties{},
 		&LTOProperties{},
+		&PgoProperties{},
 	)
 
 	android.InitDefaultsModule(module)
diff --git a/cc/library.go b/cc/library.go
index 2a866dc..f1681db 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -484,6 +484,7 @@
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 
 	var linkerDeps android.Paths
+	linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
 
 	versionScript := android.OptionalPathForModuleSrc(ctx, library.Properties.Version_script)
 	unexportedSymbols := android.OptionalPathForModuleSrc(ctx, library.Properties.Unexported_symbols_list)
@@ -628,7 +629,6 @@
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 
 	objs = objs.Append(deps.Objs)
-
 	var out android.Path
 	if library.static() || library.header() {
 		out = library.linkStatic(ctx, flags, deps, objs)
diff --git a/cc/pgo.go b/cc/pgo.go
new file mode 100644
index 0000000..ccddece
--- /dev/null
+++ b/cc/pgo.go
@@ -0,0 +1,190 @@
+// Copyright 2017 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 (
+	"fmt"
+	"strings"
+
+	"android/soong/android"
+)
+
+var (
+	// Add flags to ignore warnings that profiles are old or missing for
+	// some functions
+	profileUseOtherFlags = []string{}
+)
+
+const pgoProfileProject = "toolchain/pgo-profiles"
+
+const profileInstrumentFlag = "-fprofile-generate=/data/local/tmp"
+const profileSamplingFlag = "-gline-tables-only"
+const profileUseInstrumentFormat = "-fprofile-use=%s"
+const profileUseSamplingFormat = "-fprofile-sample-use=%s"
+
+type PgoProperties struct {
+	Pgo struct {
+		Instrumentation *bool
+		Sampling        *bool
+		Profile_file    *string `android:"arch_variant"`
+		Benchmarks      []string
+	} `android:"arch_variant"`
+
+	PgoPresent          bool `blueprint:"mutated"`
+	ShouldProfileModule bool `blueprint:"mutated"`
+}
+
+type pgo struct {
+	Properties PgoProperties
+}
+
+func (pgo *pgo) props() []interface{} {
+	return []interface{}{&pgo.Properties}
+}
+
+func (pgo *pgo) profileGatherFlags(ctx ModuleContext) string {
+	if *pgo.Properties.Pgo.Instrumentation {
+		return profileInstrumentFlag
+	}
+	if *pgo.Properties.Pgo.Sampling {
+		return profileSamplingFlag
+	}
+	return ""
+}
+
+func (pgo *pgo) profileUseFlag(ctx ModuleContext, file string) string {
+	if *pgo.Properties.Pgo.Instrumentation {
+		return fmt.Sprintf(profileUseInstrumentFormat, file)
+	}
+	if *pgo.Properties.Pgo.Sampling {
+		return fmt.Sprintf(profileUseSamplingFormat, file)
+	}
+	return ""
+}
+
+func (pgo *pgo) profileUseFlags(ctx ModuleContext, file string) []string {
+	flags := []string{pgo.profileUseFlag(ctx, file)}
+	flags = append(flags, profileUseOtherFlags...)
+	return flags
+}
+
+func (props *PgoProperties) isPGO(ctx BaseModuleContext) bool {
+	isInstrumentation := props.Pgo.Instrumentation != nil
+	isSampling := props.Pgo.Sampling != nil
+
+	profileKindPresent := isInstrumentation || isSampling
+	filePresent := props.Pgo.Profile_file != nil
+	benchmarksPresent := len(props.Pgo.Benchmarks) > 0
+
+	// If all three properties are absent, PGO is OFF for this module
+	if !profileKindPresent && !filePresent && !benchmarksPresent {
+		return false
+	}
+
+	// If at least one property exists, validate that all properties exist
+	if !profileKindPresent || !filePresent || !benchmarksPresent {
+		var missing []string
+		if !profileKindPresent {
+			missing = append(missing, "profile kind (either \"instrumentation\" or \"sampling\" property)")
+		}
+		if !filePresent {
+			missing = append(missing, "profile_file property")
+		}
+		if !benchmarksPresent {
+			missing = append(missing, "non-empty benchmarks property")
+		}
+		missingProps := strings.Join(missing, ", ")
+		ctx.ModuleErrorf("PGO specification is missing properties: " + missingProps)
+	}
+
+	// Sampling not supported yet
+	//
+	// TODO When sampling support is turned on, check that instrumentation and
+	// sampling are not simultaneously specified
+	if isSampling {
+		ctx.PropertyErrorf("pgo.sampling", "\"sampling\" is not supported yet)")
+	}
+
+	return true
+}
+
+func getPgoProfilesDir(ctx ModuleContext) android.OptionalPath {
+	return android.ExistentPathForSource(ctx, "", pgoProfileProject)
+}
+
+func (pgo *pgo) begin(ctx BaseModuleContext) {
+	// TODO Evaluate if we need to support PGO for host modules
+	if ctx.Host() {
+		return
+	}
+
+	// Check if PGO is needed for this module
+	pgo.Properties.PgoPresent = pgo.Properties.isPGO(ctx)
+
+	if !pgo.Properties.PgoPresent {
+		return
+	}
+
+	// This module should be instrumented if ANDROID_PGO_INSTRUMENT is set
+	// and includes a benchmark listed for this module
+	//
+	// TODO Validate that each benchmark instruments at least one module
+	pgo.Properties.ShouldProfileModule = false
+	pgoBenchmarks := ctx.AConfig().Getenv("ANDROID_PGO_INSTRUMENT")
+	pgoBenchmarksMap := make(map[string]bool)
+	for _, b := range strings.Split(pgoBenchmarks, ",") {
+		pgoBenchmarksMap[b] = true
+	}
+
+	for _, b := range pgo.Properties.Pgo.Benchmarks {
+		if pgoBenchmarksMap[b] == true {
+			pgo.Properties.ShouldProfileModule = true
+			break
+		}
+	}
+}
+
+func (pgo *pgo) flags(ctx ModuleContext, flags Flags) Flags {
+	if ctx.Host() {
+		return flags
+	}
+
+	props := pgo.Properties
+
+	// Add flags to profile this module based on its profile_kind
+	if props.ShouldProfileModule {
+		profileGatherFlags := pgo.profileGatherFlags(ctx)
+		flags.LdFlags = append(flags.LdFlags, profileGatherFlags)
+		flags.CFlags = append(flags.CFlags, profileGatherFlags)
+		return flags
+	}
+
+	// If the PGO profiles project is found, and this module has PGO
+	// enabled, add flags to use the profile
+	if profilesDir := getPgoProfilesDir(ctx); props.PgoPresent && profilesDir.Valid() {
+		profileFile := android.PathForSource(ctx, profilesDir.String(), *(props.Pgo.Profile_file))
+		profileUseFlags := pgo.profileUseFlags(ctx, profileFile.String())
+
+		flags.CFlags = append(flags.CFlags, profileUseFlags...)
+		flags.LdFlags = append(flags.LdFlags, profileUseFlags...)
+
+		// Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
+		// if profileFile gets updated
+		flags.CFlagsDeps = append(flags.CFlagsDeps, profileFile)
+		flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFile)
+	}
+
+	return flags
+}