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