• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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{}{&lto.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