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