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 "android/soong/android" 19 20 "github.com/google/blueprint/proptools" 21) 22 23// LTO (link-time optimization) allows the compiler to optimize and generate 24// code for the entire module at link time, rather than per-compilation 25// unit. LTO is required for Clang CFI and other whole-program optimization 26// techniques. LTO also allows cross-compilation unit optimizations that should 27// result in faster and smaller code, at the expense of additional compilation 28// time. 29// 30// To properly build a module with LTO, the module and all recursive static 31// dependencies should be compiled with -flto which directs the compiler to emit 32// bitcode rather than native object files. These bitcode files are then passed 33// by the linker to the LLVM plugin for compilation at link time. Static 34// dependencies not built as bitcode will still function correctly but cannot be 35// optimized at link time and may not be compatible with features that require 36// LTO, such as CFI. 37// 38// This file adds support to soong to automatically propogate LTO options to a 39// new variant of all static dependencies for each module with LTO enabled. 40 41type LTOProperties struct { 42 // Lto must violate capitialization style for acronyms so that it can be 43 // referred to in blueprint files as "lto" 44 Lto struct { 45 Never *bool `android:"arch_variant"` 46 Full *bool `android:"arch_variant"` 47 Thin *bool `android:"arch_variant"` 48 } `android:"arch_variant"` 49 50 // Dep properties indicate that this module needs to be built with LTO 51 // since it is an object dependency of an LTO module. 52 FullEnabled bool `blueprint:"mutated"` 53 ThinEnabled bool `blueprint:"mutated"` 54 NoLtoEnabled bool `blueprint:"mutated"` 55 FullDep bool `blueprint:"mutated"` 56 ThinDep bool `blueprint:"mutated"` 57 NoLtoDep bool `blueprint:"mutated"` 58 59 // Use clang lld instead of gnu ld. 60 Use_clang_lld *bool 61 62 // Use -fwhole-program-vtables cflag. 63 Whole_program_vtables *bool 64} 65 66type lto struct { 67 Properties LTOProperties 68} 69 70func (lto *lto) props() []interface{} { 71 return []interface{}{<o.Properties} 72} 73 74func (lto *lto) begin(ctx BaseModuleContext) { 75 if ctx.Config().IsEnvTrue("DISABLE_LTO") { 76 lto.Properties.NoLtoEnabled = true 77 } 78} 79 80func (lto *lto) useClangLld(ctx BaseModuleContext) bool { 81 if lto.Properties.Use_clang_lld != nil { 82 return Bool(lto.Properties.Use_clang_lld) 83 } 84 return true 85} 86 87func (lto *lto) flags(ctx BaseModuleContext, flags Flags) Flags { 88 // TODO(b/131771163): Disable LTO when using explicit fuzzing configurations. 89 // LTO breaks fuzzer builds. 90 if inList("-fsanitize=fuzzer-no-link", flags.Local.CFlags) { 91 return flags 92 } 93 94 // TODO(b/254713216): LTO doesn't work on riscv64 yet. 95 if ctx.Arch().ArchType == android.Riscv64 { 96 return flags 97 } 98 99 if lto.LTO(ctx) { 100 var ltoCFlag string 101 var ltoLdFlag string 102 if lto.ThinLTO() { 103 ltoCFlag = "-flto=thin -fsplit-lto-unit" 104 } else if lto.FullLTO() { 105 ltoCFlag = "-flto" 106 } else { 107 ltoCFlag = "-flto=thin -fsplit-lto-unit" 108 ltoLdFlag = "-Wl,--lto-O0" 109 } 110 111 flags.Local.CFlags = append(flags.Local.CFlags, ltoCFlag) 112 flags.Local.AsFlags = append(flags.Local.AsFlags, ltoCFlag) 113 flags.Local.LdFlags = append(flags.Local.LdFlags, ltoCFlag) 114 flags.Local.LdFlags = append(flags.Local.LdFlags, ltoLdFlag) 115 116 if Bool(lto.Properties.Whole_program_vtables) { 117 flags.Local.CFlags = append(flags.Local.CFlags, "-fwhole-program-vtables") 118 } 119 120 if (lto.DefaultThinLTO(ctx) || lto.ThinLTO()) && ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") && lto.useClangLld(ctx) { 121 // Set appropriate ThinLTO cache policy 122 cacheDirFormat := "-Wl,--thinlto-cache-dir=" 123 cacheDir := android.PathForOutput(ctx, "thinlto-cache").String() 124 flags.Local.LdFlags = append(flags.Local.LdFlags, cacheDirFormat+cacheDir) 125 126 // Limit the size of the ThinLTO cache to the lesser of 10% of available 127 // disk space and 10GB. 128 cachePolicyFormat := "-Wl,--thinlto-cache-policy=" 129 policy := "cache_size=10%:cache_size_bytes=10g" 130 flags.Local.LdFlags = append(flags.Local.LdFlags, cachePolicyFormat+policy) 131 } 132 133 // If the module does not have a profile, be conservative and limit cross TU inline 134 // limit to 5 LLVM IR instructions, to balance binary size increase and performance. 135 if !ctx.isPgoCompile() && !ctx.isAfdoCompile() { 136 flags.Local.LdFlags = append(flags.Local.LdFlags, 137 "-Wl,-plugin-opt,-import-instr-limit=5") 138 } 139 } 140 return flags 141} 142 143func (lto *lto) LTO(ctx BaseModuleContext) bool { 144 return lto.ThinLTO() || lto.FullLTO() || lto.DefaultThinLTO(ctx) 145} 146 147func (lto *lto) DefaultThinLTO(ctx BaseModuleContext) bool { 148 // LP32 has many subtle issues and less test coverage. 149 lib32 := ctx.Arch().ArchType.Multilib == "lib32" 150 // CFI enables full LTO. 151 cfi := ctx.isCfi() 152 // Performance and binary size are less important for host binaries and tests. 153 host := ctx.Host() 154 test := ctx.testBinary() || ctx.testLibrary() 155 // FIXME: ThinLTO for VNDK produces different output. 156 // b/169217596 157 vndk := ctx.isVndk() 158 return GlobalThinLTO(ctx) && !lto.Never() && !lib32 && !cfi && !host && !test && !vndk 159} 160 161func (lto *lto) FullLTO() bool { 162 return lto != nil && (proptools.Bool(lto.Properties.Lto.Full) || lto.Properties.FullEnabled) 163} 164 165func (lto *lto) ThinLTO() bool { 166 return lto != nil && (proptools.Bool(lto.Properties.Lto.Thin) || lto.Properties.ThinEnabled) 167} 168 169func (lto *lto) Never() bool { 170 return lto != nil && (proptools.Bool(lto.Properties.Lto.Never) || lto.Properties.NoLtoEnabled) 171} 172 173func GlobalThinLTO(ctx android.BaseModuleContext) bool { 174 return ctx.Config().IsEnvTrue("GLOBAL_THINLTO") 175} 176 177// Propagate lto requirements down from binaries 178func ltoDepsMutator(mctx android.TopDownMutatorContext) { 179 globalThinLTO := GlobalThinLTO(mctx) 180 181 if m, ok := mctx.Module().(*Module); ok { 182 full := m.lto.FullLTO() 183 thin := m.lto.ThinLTO() 184 never := m.lto.Never() 185 if full && thin { 186 mctx.PropertyErrorf("LTO", "FullLTO and ThinLTO are mutually exclusive") 187 } 188 189 mctx.WalkDeps(func(dep android.Module, parent android.Module) bool { 190 tag := mctx.OtherModuleDependencyTag(dep) 191 libTag, isLibTag := tag.(libraryDependencyTag) 192 193 // Do not recurse down non-static dependencies 194 if isLibTag { 195 if !libTag.static() { 196 return false 197 } 198 } else { 199 if tag != objDepTag && tag != reuseObjTag { 200 return false 201 } 202 } 203 204 if dep, ok := dep.(*Module); ok { 205 if full && !dep.lto.FullLTO() { 206 dep.lto.Properties.FullDep = true 207 } 208 if !globalThinLTO && thin && !dep.lto.ThinLTO() { 209 dep.lto.Properties.ThinDep = true 210 } 211 if globalThinLTO && never && !dep.lto.Never() { 212 dep.lto.Properties.NoLtoDep = true 213 } 214 } 215 216 // Recursively walk static dependencies 217 return true 218 }) 219 } 220} 221 222// Create lto variants for modules that need them 223func ltoMutator(mctx android.BottomUpMutatorContext) { 224 globalThinLTO := GlobalThinLTO(mctx) 225 226 if m, ok := mctx.Module().(*Module); ok && m.lto != nil { 227 // Create variations for LTO types required as static 228 // dependencies 229 variationNames := []string{""} 230 if m.lto.Properties.FullDep && !m.lto.FullLTO() { 231 variationNames = append(variationNames, "lto-full") 232 } 233 if !globalThinLTO && m.lto.Properties.ThinDep && !m.lto.ThinLTO() { 234 variationNames = append(variationNames, "lto-thin") 235 } 236 if globalThinLTO && m.lto.Properties.NoLtoDep && !m.lto.Never() { 237 variationNames = append(variationNames, "lto-none") 238 } 239 240 // Use correct dependencies if LTO property is explicitly set 241 // (mutually exclusive) 242 if m.lto.FullLTO() { 243 mctx.SetDependencyVariation("lto-full") 244 } 245 if !globalThinLTO && m.lto.ThinLTO() { 246 mctx.SetDependencyVariation("lto-thin") 247 } 248 // Never must be the last, it overrides Thin or Full. 249 if globalThinLTO && m.lto.Never() { 250 mctx.SetDependencyVariation("lto-none") 251 } 252 253 if len(variationNames) > 1 { 254 modules := mctx.CreateVariations(variationNames...) 255 for i, name := range variationNames { 256 variation := modules[i].(*Module) 257 // Default module which will be 258 // installed. Variation set above according to 259 // explicit LTO properties 260 if name == "" { 261 continue 262 } 263 264 // LTO properties for dependencies 265 if name == "lto-full" { 266 variation.lto.Properties.FullEnabled = true 267 } 268 if name == "lto-thin" { 269 variation.lto.Properties.ThinEnabled = true 270 } 271 if name == "lto-none" { 272 variation.lto.Properties.NoLtoEnabled = true 273 } 274 variation.Properties.PreventInstall = true 275 variation.Properties.HideFromMake = true 276 variation.lto.Properties.FullDep = false 277 variation.lto.Properties.ThinDep = false 278 variation.lto.Properties.NoLtoDep = false 279 } 280 } 281 } 282} 283