1// Copyright 2017 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 20 "github.com/google/blueprint" 21 "github.com/google/blueprint/proptools" 22 23 "android/soong/android" 24) 25 26// LTO (link-time optimization) allows the compiler to optimize and generate 27// code for the entire module at link time, rather than per-compilation 28// unit. LTO is required for Clang CFI and other whole-program optimization 29// techniques. LTO also allows cross-compilation unit optimizations that should 30// result in faster and smaller code, at the expense of additional compilation 31// time. 32// 33// To properly build a module with LTO, the module and all recursive static 34// dependencies should be compiled with -flto which directs the compiler to emit 35// bitcode rather than native object files. These bitcode files are then passed 36// by the linker to the LLVM plugin for compilation at link time. Static 37// dependencies not built as bitcode will still function correctly but cannot be 38// optimized at link time and may not be compatible with features that require 39// LTO, such as CFI. 40// 41// This file adds support to soong to automatically propagate LTO options to a 42// new variant of all static dependencies for each module with LTO enabled. 43 44type LTOProperties struct { 45 // Lto must violate capitalization style for acronyms so that it can be 46 // referred to in blueprint files as "lto" 47 Lto struct { 48 Never *bool `android:"arch_variant"` 49 Thin *bool `android:"arch_variant"` 50 } `android:"arch_variant"` 51 52 LtoEnabled bool `blueprint:"mutated"` 53 LtoDefault bool `blueprint:"mutated"` 54 55 // Use -fwhole-program-vtables cflag. 56 Whole_program_vtables *bool 57 58 // Use --lto-O0 flag. 59 Lto_O0 *bool 60} 61 62type lto struct { 63 Properties LTOProperties 64} 65 66func (lto *lto) props() []interface{} { 67 return []interface{}{<o.Properties} 68} 69 70func (lto *lto) begin(ctx BaseModuleContext) { 71 // First, determine the module independent default LTO mode. 72 ltoDefault := true 73 if ctx.Config().IsEnvTrue("DISABLE_LTO") { 74 ltoDefault = false 75 } else if lto.Never() { 76 ltoDefault = false 77 } else if ctx.Host() { 78 // Performance and binary size are less important for host binaries. 79 ltoDefault = false 80 } else if ctx.Arch().ArchType.Multilib == "lib32" { 81 // LP32 has many subtle issues and less test coverage. 82 ltoDefault = false 83 } 84 85 // Then, determine the actual LTO mode to use. If different from `ltoDefault`, a variant needs 86 // to be created. 87 ltoEnabled := ltoDefault 88 if lto.Never() { 89 ltoEnabled = false 90 } else if lto.ThinLTO() { 91 // Module explicitly requests for LTO. 92 ltoEnabled = true 93 } else if ctx.testBinary() || ctx.testLibrary() { 94 // Do not enable LTO for tests for better debugging. 95 ltoEnabled = false 96 } 97 98 lto.Properties.LtoDefault = ltoDefault 99 lto.Properties.LtoEnabled = ltoEnabled 100} 101 102func (lto *lto) flags(ctx ModuleContext, flags Flags) Flags { 103 // TODO(b/131771163): CFI and Fuzzer controls LTO flags by themselves. 104 // This has be checked late because these properties can be mutated. 105 if ctx.isCfi() || ctx.isFuzzer() { 106 return flags 107 } 108 if lto.Properties.LtoEnabled { 109 ltoCFlags := []string{"-flto=thin", "-fsplit-lto-unit"} 110 var ltoLdFlags []string 111 112 // Do not perform costly LTO optimizations for Eng builds. 113 if Bool(lto.Properties.Lto_O0) || ctx.Config().Eng() { 114 ltoLdFlags = append(ltoLdFlags, "-Wl,--lto-O0") 115 } 116 117 if Bool(lto.Properties.Whole_program_vtables) { 118 ltoCFlags = append(ltoCFlags, "-fwhole-program-vtables") 119 } 120 121 if ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") { 122 // Set appropriate ThinLTO cache policy 123 cacheDirFormat := "-Wl,--thinlto-cache-dir=" 124 cacheDir := android.PathForOutput(ctx, "thinlto-cache").String() 125 ltoLdFlags = append(ltoLdFlags, cacheDirFormat+cacheDir) 126 127 // Limit the size of the ThinLTO cache to the lesser of 10% of available 128 // disk space and 10GB. 129 cachePolicyFormat := "-Wl,--thinlto-cache-policy=" 130 policy := "cache_size=10%:cache_size_bytes=10g" 131 ltoLdFlags = append(ltoLdFlags, cachePolicyFormat+policy) 132 } 133 134 // Reduce the inlining threshold for a better balance of binary size and 135 // performance. 136 if !ctx.Darwin() { 137 if ctx.isAfdoCompile(ctx) { 138 ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=40") 139 } else { 140 ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=5") 141 } 142 } 143 144 if !ctx.Config().IsEnvFalse("THINLTO_USE_MLGO") { 145 // Register allocation MLGO flags for ARM64. 146 if ctx.Arch().ArchType == android.Arm64 && !ctx.optimizeForSize() { 147 ltoLdFlags = append(ltoLdFlags, "-Wl,-mllvm,-regalloc-enable-advisor=release") 148 } 149 // Flags for training MLGO model. 150 if ctx.Config().IsEnvTrue("THINLTO_EMIT_INDEXES_AND_IMPORTS") { 151 ltoLdFlags = append(ltoLdFlags, "-Wl,--save-temps=import") 152 ltoLdFlags = append(ltoLdFlags, "-Wl,--thinlto-emit-index-files") 153 } 154 } 155 156 flags.Local.CFlags = append(flags.Local.CFlags, ltoCFlags...) 157 flags.Local.AsFlags = append(flags.Local.AsFlags, ltoCFlags...) 158 flags.Local.LdFlags = append(flags.Local.LdFlags, ltoCFlags...) 159 flags.Local.LdFlags = append(flags.Local.LdFlags, ltoLdFlags...) 160 } 161 return flags 162} 163 164func (lto *lto) ThinLTO() bool { 165 return lto != nil && proptools.Bool(lto.Properties.Lto.Thin) 166} 167 168func (lto *lto) Never() bool { 169 return lto != nil && proptools.Bool(lto.Properties.Lto.Never) 170} 171 172func ltoPropagateViaDepTag(tag blueprint.DependencyTag) bool { 173 libTag, isLibTag := tag.(libraryDependencyTag) 174 // Do not recurse down non-static dependencies 175 if isLibTag { 176 return libTag.static() 177 } else { 178 return tag == objDepTag || tag == reuseObjTag || tag == staticVariantTag 179 } 180} 181 182// ltoTransitionMutator creates LTO variants of cc modules. Variant "" is the default variant, which may 183// or may not have LTO enabled depending on the config and the module's type and properties. "lto-thin" or 184// "lto-none" variants are created when a module needs to compile in the non-default state for that module. 185type ltoTransitionMutator struct{} 186 187const LTO_NONE_VARIATION = "lto-none" 188const LTO_THIN_VARIATION = "lto-thin" 189 190func (l *ltoTransitionMutator) Split(ctx android.BaseModuleContext) []string { 191 return []string{""} 192} 193 194func (l *ltoTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string { 195 if m, ok := ctx.Module().(*Module); ok && m.lto != nil { 196 if !ltoPropagateViaDepTag(ctx.DepTag()) { 197 return "" 198 } 199 200 if sourceVariation != "" { 201 return sourceVariation 202 } 203 204 // Always request an explicit variation, IncomingTransition will rewrite it back to the default variation 205 // if necessary. 206 if m.lto.Properties.LtoEnabled { 207 return LTO_THIN_VARIATION 208 } else { 209 return LTO_NONE_VARIATION 210 } 211 } 212 return "" 213} 214 215func (l *ltoTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string { 216 if m, ok := ctx.Module().(*Module); ok && m.lto != nil { 217 if m.lto.Never() { 218 return "" 219 } 220 // Rewrite explicit variations back to the default variation if the default variation matches. 221 if incomingVariation == LTO_THIN_VARIATION && m.lto.Properties.LtoDefault { 222 return "" 223 } else if incomingVariation == LTO_NONE_VARIATION && !m.lto.Properties.LtoDefault { 224 return "" 225 } 226 return incomingVariation 227 } 228 return "" 229} 230 231func (l *ltoTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) { 232 // Default module which will be installed. Variation set above according to explicit LTO properties. 233 if variation == "" { 234 return 235 } 236 237 if m, ok := ctx.Module().(*Module); ok && m.lto != nil { 238 // Non-default variation, set the LTO properties to match the variation. 239 switch variation { 240 case LTO_THIN_VARIATION: 241 m.lto.Properties.LtoEnabled = true 242 case LTO_NONE_VARIATION: 243 m.lto.Properties.LtoEnabled = false 244 default: 245 panic(fmt.Errorf("unknown variation %s", variation)) 246 } 247 // Non-default variations are never installed. 248 m.Properties.PreventInstall = true 249 m.Properties.HideFromMake = true 250 } 251} 252