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 57type lto struct { 58 Properties LTOProperties 59} 60 61func (lto *lto) props() []interface{} { 62 return []interface{}{<o.Properties} 63} 64 65func (lto *lto) begin(ctx BaseModuleContext) { 66 if ctx.Config().IsEnvTrue("DISABLE_LTO") { 67 lto.Properties.Lto.Never = boolPtr(true) 68 } 69} 70 71func (lto *lto) deps(ctx BaseModuleContext, deps Deps) Deps { 72 return deps 73} 74 75func (lto *lto) useClangLld(ctx BaseModuleContext) bool { 76 if lto.Properties.Use_clang_lld != nil { 77 return Bool(lto.Properties.Use_clang_lld) 78 } 79 return true 80} 81 82func (lto *lto) flags(ctx BaseModuleContext, flags Flags) Flags { 83 // TODO(b/131771163): Disable LTO when using explicit fuzzing configurations. 84 // LTO breaks fuzzer builds. 85 if inList("-fsanitize=fuzzer-no-link", flags.Local.CFlags) { 86 return flags 87 } 88 89 if lto.LTO() { 90 var ltoFlag string 91 if Bool(lto.Properties.Lto.Thin) { 92 ltoFlag = "-flto=thin -fsplit-lto-unit" 93 } else { 94 ltoFlag = "-flto" 95 } 96 97 flags.Local.CFlags = append(flags.Local.CFlags, ltoFlag) 98 flags.Local.LdFlags = append(flags.Local.LdFlags, ltoFlag) 99 100 if ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") && Bool(lto.Properties.Lto.Thin) && lto.useClangLld(ctx) { 101 // Set appropriate ThinLTO cache policy 102 cacheDirFormat := "-Wl,--thinlto-cache-dir=" 103 cacheDir := android.PathForOutput(ctx, "thinlto-cache").String() 104 flags.Local.LdFlags = append(flags.Local.LdFlags, cacheDirFormat+cacheDir) 105 106 // Limit the size of the ThinLTO cache to the lesser of 10% of available 107 // disk space and 10GB. 108 cachePolicyFormat := "-Wl,--thinlto-cache-policy=" 109 policy := "cache_size=10%:cache_size_bytes=10g" 110 flags.Local.LdFlags = append(flags.Local.LdFlags, cachePolicyFormat+policy) 111 } 112 113 // If the module does not have a profile, be conservative and do not inline 114 // or unroll loops during LTO, in order to prevent significant size bloat. 115 if !ctx.isPgoCompile() { 116 flags.Local.LdFlags = append(flags.Local.LdFlags, 117 "-Wl,-plugin-opt,-inline-threshold=0", 118 "-Wl,-plugin-opt,-unroll-threshold=0") 119 } 120 } 121 return flags 122} 123 124// Can be called with a null receiver 125func (lto *lto) LTO() bool { 126 if lto == nil || lto.Disabled() { 127 return false 128 } 129 130 full := Bool(lto.Properties.Lto.Full) 131 thin := Bool(lto.Properties.Lto.Thin) 132 return full || thin 133} 134 135// Is lto.never explicitly set to true? 136func (lto *lto) Disabled() bool { 137 return lto.Properties.Lto.Never != nil && *lto.Properties.Lto.Never 138} 139 140// Propagate lto requirements down from binaries 141func ltoDepsMutator(mctx android.TopDownMutatorContext) { 142 if m, ok := mctx.Module().(*Module); ok && m.lto.LTO() { 143 full := Bool(m.lto.Properties.Lto.Full) 144 thin := Bool(m.lto.Properties.Lto.Thin) 145 if full && thin { 146 mctx.PropertyErrorf("LTO", "FullLTO and ThinLTO are mutually exclusive") 147 } 148 149 mctx.WalkDeps(func(dep android.Module, parent android.Module) bool { 150 tag := mctx.OtherModuleDependencyTag(dep) 151 switch tag { 152 case StaticDepTag, staticExportDepTag, lateStaticDepTag, wholeStaticDepTag, objDepTag, reuseObjTag: 153 if dep, ok := dep.(*Module); ok && dep.lto != nil && 154 !dep.lto.Disabled() { 155 if full && !Bool(dep.lto.Properties.Lto.Full) { 156 dep.lto.Properties.FullDep = true 157 } 158 if thin && !Bool(dep.lto.Properties.Lto.Thin) { 159 dep.lto.Properties.ThinDep = true 160 } 161 } 162 163 // Recursively walk static dependencies 164 return true 165 } 166 167 // Do not recurse down non-static dependencies 168 return false 169 }) 170 } 171} 172 173// Create lto variants for modules that need them 174func ltoMutator(mctx android.BottomUpMutatorContext) { 175 if m, ok := mctx.Module().(*Module); ok && m.lto != nil { 176 // Create variations for LTO types required as static 177 // dependencies 178 variationNames := []string{""} 179 if m.lto.Properties.FullDep && !Bool(m.lto.Properties.Lto.Full) { 180 variationNames = append(variationNames, "lto-full") 181 } 182 if m.lto.Properties.ThinDep && !Bool(m.lto.Properties.Lto.Thin) { 183 variationNames = append(variationNames, "lto-thin") 184 } 185 186 // Use correct dependencies if LTO property is explicitly set 187 // (mutually exclusive) 188 if Bool(m.lto.Properties.Lto.Full) { 189 mctx.SetDependencyVariation("lto-full") 190 } 191 if Bool(m.lto.Properties.Lto.Thin) { 192 mctx.SetDependencyVariation("lto-thin") 193 } 194 195 if len(variationNames) > 1 { 196 modules := mctx.CreateVariations(variationNames...) 197 for i, name := range variationNames { 198 variation := modules[i].(*Module) 199 // Default module which will be 200 // installed. Variation set above according to 201 // explicit LTO properties 202 if name == "" { 203 continue 204 } 205 206 // LTO properties for dependencies 207 if name == "lto-full" { 208 variation.lto.Properties.Lto.Full = boolPtr(true) 209 variation.lto.Properties.Lto.Thin = boolPtr(false) 210 } 211 if name == "lto-thin" { 212 variation.lto.Properties.Lto.Full = boolPtr(false) 213 variation.lto.Properties.Lto.Thin = boolPtr(true) 214 } 215 variation.Properties.PreventInstall = true 216 variation.Properties.HideFromMake = true 217 variation.lto.Properties.FullDep = false 218 variation.lto.Properties.ThinDep = false 219 } 220 } 221 } 222} 223