• 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
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{}{&lto.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