|  | // 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 ( | 
|  | "android/soong/android" | 
|  |  | 
|  | "github.com/google/blueprint/proptools" | 
|  | ) | 
|  |  | 
|  | // LTO (link-time optimization) allows the compiler to optimize and generate | 
|  | // code for the entire module at link time, rather than per-compilation | 
|  | // unit. LTO is required for Clang CFI and other whole-program optimization | 
|  | // techniques. LTO also allows cross-compilation unit optimizations that should | 
|  | // result in faster and smaller code, at the expense of additional compilation | 
|  | // time. | 
|  | // | 
|  | // To properly build a module with LTO, the module and all recursive static | 
|  | // dependencies should be compiled with -flto which directs the compiler to emit | 
|  | // bitcode rather than native object files. These bitcode files are then passed | 
|  | // by the linker to the LLVM plugin for compilation at link time. Static | 
|  | // dependencies not built as bitcode will still function correctly but cannot be | 
|  | // optimized at link time and may not be compatible with features that require | 
|  | // LTO, such as CFI. | 
|  | // | 
|  | // This file adds support to soong to automatically propogate LTO options to a | 
|  | // new variant of all static dependencies for each module with LTO enabled. | 
|  |  | 
|  | type LTOProperties struct { | 
|  | // Lto must violate capitialization style for acronyms so that it can be | 
|  | // referred to in blueprint files as "lto" | 
|  | Lto struct { | 
|  | Never *bool `android:"arch_variant"` | 
|  | Thin  *bool `android:"arch_variant"` | 
|  | } `android:"arch_variant"` | 
|  |  | 
|  | LtoEnabled bool `blueprint:"mutated"` | 
|  | LtoDefault bool `blueprint:"mutated"` | 
|  |  | 
|  | // Dep properties indicate that this module needs to be built with LTO | 
|  | // since it is an object dependency of an LTO module. | 
|  | LtoDep   bool `blueprint:"mutated"` | 
|  | NoLtoDep bool `blueprint:"mutated"` | 
|  |  | 
|  | // Use -fwhole-program-vtables cflag. | 
|  | Whole_program_vtables *bool | 
|  | } | 
|  |  | 
|  | type lto struct { | 
|  | Properties LTOProperties | 
|  | } | 
|  |  | 
|  | func (lto *lto) props() []interface{} { | 
|  | return []interface{}{<o.Properties} | 
|  | } | 
|  |  | 
|  | func (lto *lto) begin(ctx BaseModuleContext) { | 
|  | // First, determine the module indepedent default LTO mode. | 
|  | ltoDefault := GlobalThinLTO(ctx) | 
|  | if ctx.Config().IsEnvTrue("DISABLE_LTO") { | 
|  | ltoDefault = false | 
|  | } else if ctx.Host() { | 
|  | // Performance and binary size are less important for host binaries. | 
|  | ltoDefault = false | 
|  | } else if ctx.Arch().ArchType.Multilib == "lib32" { | 
|  | // LP32 has many subtle issues and less test coverage. | 
|  | ltoDefault = false | 
|  | } | 
|  |  | 
|  | // Then, determine the actual LTO mode to use. If different from `ltoDefault`, a variant needs | 
|  | // to be created. | 
|  | ltoEnabled := ltoDefault | 
|  | if lto.Never() { | 
|  | ltoEnabled = false | 
|  | } else if lto.ThinLTO() { | 
|  | // Module explicitly requests for LTO. | 
|  | ltoEnabled = true | 
|  | } else if ctx.testBinary() || ctx.testLibrary() { | 
|  | // Do not enable LTO for tests for better debugging. | 
|  | ltoEnabled = false | 
|  | } else if ctx.isVndk() { | 
|  | // FIXME: ThinLTO for VNDK produces different output. | 
|  | // b/169217596 | 
|  | ltoEnabled = false | 
|  | } | 
|  |  | 
|  | lto.Properties.LtoDefault = ltoDefault | 
|  | lto.Properties.LtoEnabled = ltoEnabled | 
|  | } | 
|  |  | 
|  | func (lto *lto) flags(ctx BaseModuleContext, flags Flags) Flags { | 
|  | // TODO(b/131771163): CFI and Fuzzer controls LTO flags by themselves. | 
|  | // This has be checked late because these properties can be mutated. | 
|  | if ctx.isCfi() || ctx.isFuzzer() { | 
|  | return flags | 
|  | } | 
|  | if lto.Properties.LtoEnabled { | 
|  | ltoCFlags := []string{"-flto=thin", "-fsplit-lto-unit"} | 
|  | var ltoLdFlags []string | 
|  |  | 
|  | // The module did not explicitly turn on LTO. Only leverage LTO's | 
|  | // better dead code elimination and CFG simplification, but do | 
|  | // not perform costly optimizations for a balance between compile | 
|  | // time, binary size and performance. | 
|  | if !lto.ThinLTO() { | 
|  | ltoLdFlags = append(ltoLdFlags, "-Wl,--lto-O0") | 
|  | } | 
|  |  | 
|  | if Bool(lto.Properties.Whole_program_vtables) { | 
|  | ltoCFlags = append(ltoCFlags, "-fwhole-program-vtables") | 
|  | } | 
|  |  | 
|  | if ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") { | 
|  | // Set appropriate ThinLTO cache policy | 
|  | cacheDirFormat := "-Wl,--thinlto-cache-dir=" | 
|  | cacheDir := android.PathForOutput(ctx, "thinlto-cache").String() | 
|  | ltoLdFlags = append(ltoLdFlags, cacheDirFormat+cacheDir) | 
|  |  | 
|  | // Limit the size of the ThinLTO cache to the lesser of 10% of available | 
|  | // disk space and 10GB. | 
|  | cachePolicyFormat := "-Wl,--thinlto-cache-policy=" | 
|  | policy := "cache_size=10%:cache_size_bytes=10g" | 
|  | ltoLdFlags = append(ltoLdFlags, cachePolicyFormat+policy) | 
|  | } | 
|  |  | 
|  | // Reduce the inlining threshold for a better balance of binary size and | 
|  | // performance. | 
|  | if !ctx.Darwin() { | 
|  | if ctx.isPgoCompile() || ctx.isAfdoCompile() { | 
|  | ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=40") | 
|  | } else { | 
|  | ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=5") | 
|  | } | 
|  | } | 
|  |  | 
|  | flags.Local.CFlags = append(flags.Local.CFlags, ltoCFlags...) | 
|  | flags.Local.AsFlags = append(flags.Local.AsFlags, ltoCFlags...) | 
|  | flags.Local.LdFlags = append(flags.Local.LdFlags, ltoCFlags...) | 
|  | flags.Local.LdFlags = append(flags.Local.LdFlags, ltoLdFlags...) | 
|  | } | 
|  | return flags | 
|  | } | 
|  |  | 
|  | func (lto *lto) ThinLTO() bool { | 
|  | return lto != nil && proptools.Bool(lto.Properties.Lto.Thin) | 
|  | } | 
|  |  | 
|  | func (lto *lto) Never() bool { | 
|  | return lto != nil && proptools.Bool(lto.Properties.Lto.Never) | 
|  | } | 
|  |  | 
|  | func GlobalThinLTO(ctx android.BaseModuleContext) bool { | 
|  | return !ctx.Config().IsEnvFalse("GLOBAL_THINLTO") | 
|  | } | 
|  |  | 
|  | // Propagate lto requirements down from binaries | 
|  | func ltoDepsMutator(mctx android.TopDownMutatorContext) { | 
|  | if m, ok := mctx.Module().(*Module); ok { | 
|  | if m.lto == nil || m.lto.Properties.LtoEnabled == m.lto.Properties.LtoDefault { | 
|  | return | 
|  | } | 
|  |  | 
|  | 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 | 
|  | } | 
|  | } else { | 
|  | if tag != objDepTag && tag != reuseObjTag { | 
|  | return false | 
|  | } | 
|  | } | 
|  |  | 
|  | if dep, ok := dep.(*Module); ok { | 
|  | if m.lto.Properties.LtoEnabled { | 
|  | dep.lto.Properties.LtoDep = true | 
|  | } else { | 
|  | dep.lto.Properties.NoLtoDep = true | 
|  | } | 
|  | } | 
|  |  | 
|  | // Recursively walk static dependencies | 
|  | return true | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | // Create lto variants for modules that need them | 
|  | func ltoMutator(mctx android.BottomUpMutatorContext) { | 
|  | globalThinLTO := GlobalThinLTO(mctx) | 
|  |  | 
|  | if m, ok := mctx.Module().(*Module); ok && m.lto != nil { | 
|  | // Create variations for LTO types required as static | 
|  | // dependencies | 
|  | variationNames := []string{""} | 
|  | if m.lto.Properties.LtoDep { | 
|  | variationNames = append(variationNames, "lto-thin") | 
|  | } | 
|  | if m.lto.Properties.NoLtoDep { | 
|  | variationNames = append(variationNames, "lto-none") | 
|  | } | 
|  |  | 
|  | if globalThinLTO && !m.lto.Properties.LtoEnabled { | 
|  | mctx.SetDependencyVariation("lto-none") | 
|  | } | 
|  | if !globalThinLTO && m.lto.Properties.LtoEnabled { | 
|  | mctx.SetDependencyVariation("lto-thin") | 
|  | } | 
|  |  | 
|  | if len(variationNames) > 1 { | 
|  | modules := mctx.CreateVariations(variationNames...) | 
|  | for i, name := range variationNames { | 
|  | variation := modules[i].(*Module) | 
|  | // Default module which will be | 
|  | // installed. Variation set above according to | 
|  | // explicit LTO properties | 
|  | if name == "" { | 
|  | continue | 
|  | } | 
|  |  | 
|  | // LTO properties for dependencies | 
|  | if name == "lto-thin" { | 
|  | variation.lto.Properties.LtoEnabled = true | 
|  | } | 
|  | if name == "lto-none" { | 
|  | variation.lto.Properties.LtoEnabled = false | 
|  | } | 
|  | variation.Properties.PreventInstall = true | 
|  | variation.Properties.HideFromMake = true | 
|  | variation.lto.Properties.LtoDefault = m.lto.Properties.LtoDefault | 
|  | variation.lto.Properties.LtoDep = false | 
|  | variation.lto.Properties.NoLtoDep = false | 
|  | } | 
|  | } | 
|  | } | 
|  | } |