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