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 "github.com/google/blueprint/proptools" 19 20 "android/soong/android" 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 FullDep bool `blueprint:"mutated"` 53 ThinDep bool `blueprint:"mutated"` 54 NoLtoDep bool `blueprint:"mutated"` 55 56 // Use clang lld instead of gnu ld. 57 Use_clang_lld *bool 58 59 // Use -fwhole-program-vtables cflag. 60 Whole_program_vtables *bool 61} 62 63type lto struct { 64 Properties LTOProperties 65} 66 67func (lto *lto) props() []interface{} { 68 return []interface{}{<o.Properties} 69} 70 71func (lto *lto) begin(ctx BaseModuleContext) { 72 if ctx.Config().IsEnvTrue("DISABLE_LTO") { 73 lto.Properties.Lto.Never = proptools.BoolPtr(true) 74 } 75} 76 77func (lto *lto) useClangLld(ctx BaseModuleContext) bool { 78 if lto.Properties.Use_clang_lld != nil { 79 return Bool(lto.Properties.Use_clang_lld) 80 } 81 return true 82} 83 84func (lto *lto) flags(ctx BaseModuleContext, flags Flags) Flags { 85 // TODO(b/131771163): Disable LTO when using explicit fuzzing configurations. 86 // LTO breaks fuzzer builds. 87 if inList("-fsanitize=fuzzer-no-link", flags.Local.CFlags) { 88 return flags 89 } 90 91 if lto.LTO(ctx) { 92 var ltoCFlag string 93 var ltoLdFlag string 94 if lto.ThinLTO() { 95 ltoCFlag = "-flto=thin -fsplit-lto-unit" 96 } else if lto.FullLTO() { 97 ltoCFlag = "-flto" 98 } else { 99 ltoCFlag = "-flto=thin -fsplit-lto-unit" 100 ltoLdFlag = "-Wl,--lto-O0" 101 } 102 103 flags.Local.CFlags = append(flags.Local.CFlags, ltoCFlag) 104 flags.Local.LdFlags = append(flags.Local.LdFlags, ltoCFlag) 105 flags.Local.LdFlags = append(flags.Local.LdFlags, ltoLdFlag) 106 107 if Bool(lto.Properties.Whole_program_vtables) { 108 flags.Local.CFlags = append(flags.Local.CFlags, "-fwhole-program-vtables") 109 } 110 111 if (lto.DefaultThinLTO(ctx) || lto.ThinLTO()) && ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") && lto.useClangLld(ctx) { 112 // Set appropriate ThinLTO cache policy 113 cacheDirFormat := "-Wl,--thinlto-cache-dir=" 114 cacheDir := android.PathForOutput(ctx, "thinlto-cache").String() 115 flags.Local.LdFlags = append(flags.Local.LdFlags, cacheDirFormat+cacheDir) 116 117 // Limit the size of the ThinLTO cache to the lesser of 10% of available 118 // disk space and 10GB. 119 cachePolicyFormat := "-Wl,--thinlto-cache-policy=" 120 policy := "cache_size=10%:cache_size_bytes=10g" 121 flags.Local.LdFlags = append(flags.Local.LdFlags, cachePolicyFormat+policy) 122 } 123 124 // If the module does not have a profile, be conservative and limit cross TU inline 125 // limit to 5 LLVM IR instructions, to balance binary size increase and performance. 126 if !ctx.isPgoCompile() && !ctx.isAfdoCompile() { 127 flags.Local.LdFlags = append(flags.Local.LdFlags, 128 "-Wl,-plugin-opt,-import-instr-limit=5") 129 } 130 } 131 return flags 132} 133 134func (lto *lto) LTO(ctx BaseModuleContext) bool { 135 return lto.ThinLTO() || lto.FullLTO() || lto.DefaultThinLTO(ctx) 136} 137 138func (lto *lto) DefaultThinLTO(ctx BaseModuleContext) bool { 139 host := ctx.Host() 140 vndk := ctx.isVndk() // b/169217596 141 return GlobalThinLTO(ctx) && !lto.Never() && !host && !vndk 142} 143 144func (lto *lto) FullLTO() bool { 145 return lto != nil && Bool(lto.Properties.Lto.Full) 146} 147 148func (lto *lto) ThinLTO() bool { 149 return lto != nil && Bool(lto.Properties.Lto.Thin) 150} 151 152func (lto *lto) Never() bool { 153 return lto != nil && Bool(lto.Properties.Lto.Never) 154} 155 156func GlobalThinLTO(ctx android.BaseModuleContext) bool { 157 return ctx.Config().IsEnvTrue("GLOBAL_THINLTO") 158} 159 160// Propagate lto requirements down from binaries 161func ltoDepsMutator(mctx android.TopDownMutatorContext) { 162 globalThinLTO := GlobalThinLTO(mctx) 163 164 if m, ok := mctx.Module().(*Module); ok { 165 full := m.lto.FullLTO() 166 thin := m.lto.ThinLTO() 167 never := m.lto.Never() 168 if full && thin { 169 mctx.PropertyErrorf("LTO", "FullLTO and ThinLTO are mutually exclusive") 170 } 171 172 mctx.WalkDeps(func(dep android.Module, parent android.Module) bool { 173 tag := mctx.OtherModuleDependencyTag(dep) 174 libTag, isLibTag := tag.(libraryDependencyTag) 175 176 // Do not recurse down non-static dependencies 177 if isLibTag { 178 if !libTag.static() { 179 return false 180 } 181 } else { 182 if tag != objDepTag && tag != reuseObjTag { 183 return false 184 } 185 } 186 187 if dep, ok := dep.(*Module); ok { 188 if full && !dep.lto.FullLTO() { 189 dep.lto.Properties.FullDep = true 190 } 191 if !globalThinLTO && thin && !dep.lto.ThinLTO() { 192 dep.lto.Properties.ThinDep = true 193 } 194 if globalThinLTO && never && !dep.lto.Never() { 195 dep.lto.Properties.NoLtoDep = true 196 } 197 } 198 199 // Recursively walk static dependencies 200 return true 201 }) 202 } 203} 204 205// Create lto variants for modules that need them 206func ltoMutator(mctx android.BottomUpMutatorContext) { 207 globalThinLTO := GlobalThinLTO(mctx) 208 209 if m, ok := mctx.Module().(*Module); ok && m.lto != nil { 210 // Create variations for LTO types required as static 211 // dependencies 212 variationNames := []string{""} 213 if m.lto.Properties.FullDep && !m.lto.FullLTO() { 214 variationNames = append(variationNames, "lto-full") 215 } 216 if !globalThinLTO && m.lto.Properties.ThinDep && !m.lto.ThinLTO() { 217 variationNames = append(variationNames, "lto-thin") 218 } 219 if globalThinLTO && m.lto.Properties.NoLtoDep && !m.lto.Never() { 220 variationNames = append(variationNames, "lto-none") 221 } 222 223 // Use correct dependencies if LTO property is explicitly set 224 // (mutually exclusive) 225 if m.lto.FullLTO() { 226 mctx.SetDependencyVariation("lto-full") 227 } 228 if !globalThinLTO && m.lto.ThinLTO() { 229 mctx.SetDependencyVariation("lto-thin") 230 } 231 // Never must be the last, it overrides Thin or Full. 232 if globalThinLTO && m.lto.Never() { 233 mctx.SetDependencyVariation("lto-none") 234 } 235 236 if len(variationNames) > 1 { 237 modules := mctx.CreateVariations(variationNames...) 238 for i, name := range variationNames { 239 variation := modules[i].(*Module) 240 // Default module which will be 241 // installed. Variation set above according to 242 // explicit LTO properties 243 if name == "" { 244 continue 245 } 246 247 // LTO properties for dependencies 248 if name == "lto-full" { 249 variation.lto.Properties.Lto.Full = proptools.BoolPtr(true) 250 } 251 if name == "lto-thin" { 252 variation.lto.Properties.Lto.Thin = proptools.BoolPtr(true) 253 } 254 if name == "lto-none" { 255 variation.lto.Properties.Lto.Never = proptools.BoolPtr(true) 256 } 257 variation.Properties.PreventInstall = true 258 variation.Properties.HideFromMake = true 259 variation.lto.Properties.FullDep = false 260 variation.lto.Properties.ThinDep = false 261 variation.lto.Properties.NoLtoDep = false 262 } 263 } 264 } 265} 266