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	"fmt"
19
20	"github.com/google/blueprint"
21	"github.com/google/blueprint/proptools"
22
23	"android/soong/android"
24)
25
26// LTO (link-time optimization) allows the compiler to optimize and generate
27// code for the entire module at link time, rather than per-compilation
28// unit. LTO is required for Clang CFI and other whole-program optimization
29// techniques. LTO also allows cross-compilation unit optimizations that should
30// result in faster and smaller code, at the expense of additional compilation
31// time.
32//
33// To properly build a module with LTO, the module and all recursive static
34// dependencies should be compiled with -flto which directs the compiler to emit
35// bitcode rather than native object files. These bitcode files are then passed
36// by the linker to the LLVM plugin for compilation at link time. Static
37// dependencies not built as bitcode will still function correctly but cannot be
38// optimized at link time and may not be compatible with features that require
39// LTO, such as CFI.
40//
41// This file adds support to soong to automatically propagate LTO options to a
42// new variant of all static dependencies for each module with LTO enabled.
43
44type LTOProperties struct {
45	// Lto must violate capitalization style for acronyms so that it can be
46	// referred to in blueprint files as "lto"
47	Lto struct {
48		Never *bool `android:"arch_variant"`
49		Thin  *bool `android:"arch_variant"`
50	} `android:"arch_variant"`
51
52	LtoEnabled bool `blueprint:"mutated"`
53	LtoDefault bool `blueprint:"mutated"`
54
55	// Use -fwhole-program-vtables cflag.
56	Whole_program_vtables *bool
57
58	// Use --lto-O0 flag.
59	Lto_O0 *bool
60}
61
62type lto struct {
63	Properties LTOProperties
64}
65
66func (lto *lto) props() []interface{} {
67	return []interface{}{&lto.Properties}
68}
69
70func (lto *lto) begin(ctx BaseModuleContext) {
71	// First, determine the module independent default LTO mode.
72	ltoDefault := true
73	if ctx.Config().IsEnvTrue("DISABLE_LTO") {
74		ltoDefault = false
75	} else if lto.Never() {
76		ltoDefault = false
77	} else if ctx.Host() {
78		// Performance and binary size are less important for host binaries.
79		ltoDefault = false
80	} else if ctx.Arch().ArchType.Multilib == "lib32" {
81		// LP32 has many subtle issues and less test coverage.
82		ltoDefault = false
83	}
84
85	// Then, determine the actual LTO mode to use. If different from `ltoDefault`, a variant needs
86	// to be created.
87	ltoEnabled := ltoDefault
88	if lto.Never() {
89		ltoEnabled = false
90	} else if lto.ThinLTO() {
91		// Module explicitly requests for LTO.
92		ltoEnabled = true
93	} else if ctx.testBinary() || ctx.testLibrary() {
94		// Do not enable LTO for tests for better debugging.
95		ltoEnabled = false
96	}
97
98	lto.Properties.LtoDefault = ltoDefault
99	lto.Properties.LtoEnabled = ltoEnabled
100}
101
102func (lto *lto) flags(ctx ModuleContext, flags Flags) Flags {
103	// TODO(b/131771163): CFI and Fuzzer controls LTO flags by themselves.
104	// This has be checked late because these properties can be mutated.
105	if ctx.isCfi() || ctx.isFuzzer() {
106		return flags
107	}
108	if lto.Properties.LtoEnabled {
109		ltoCFlags := []string{"-flto=thin", "-fsplit-lto-unit"}
110		var ltoLdFlags []string
111
112		// Do not perform costly LTO optimizations for Eng builds.
113		if Bool(lto.Properties.Lto_O0) || ctx.Config().Eng() {
114			ltoLdFlags = append(ltoLdFlags, "-Wl,--lto-O0")
115		}
116
117		if Bool(lto.Properties.Whole_program_vtables) {
118			ltoCFlags = append(ltoCFlags, "-fwhole-program-vtables")
119		}
120
121		if ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") {
122			// Set appropriate ThinLTO cache policy
123			cacheDirFormat := "-Wl,--thinlto-cache-dir="
124			cacheDir := android.PathForOutput(ctx, "thinlto-cache").String()
125			ltoLdFlags = append(ltoLdFlags, cacheDirFormat+cacheDir)
126
127			// Limit the size of the ThinLTO cache to the lesser of 10% of available
128			// disk space and 10GB.
129			cachePolicyFormat := "-Wl,--thinlto-cache-policy="
130			policy := "cache_size=10%:cache_size_bytes=10g"
131			ltoLdFlags = append(ltoLdFlags, cachePolicyFormat+policy)
132		}
133
134		// Reduce the inlining threshold for a better balance of binary size and
135		// performance.
136		if !ctx.Darwin() {
137			if ctx.isAfdoCompile(ctx) {
138				ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=40")
139			} else {
140				ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=5")
141			}
142		}
143
144		if !ctx.Config().IsEnvFalse("THINLTO_USE_MLGO") {
145			// Register allocation MLGO flags for ARM64.
146			if ctx.Arch().ArchType == android.Arm64 && !ctx.optimizeForSize() {
147				ltoLdFlags = append(ltoLdFlags, "-Wl,-mllvm,-regalloc-enable-advisor=release")
148			}
149			// Flags for training MLGO model.
150			if ctx.Config().IsEnvTrue("THINLTO_EMIT_INDEXES_AND_IMPORTS") {
151				ltoLdFlags = append(ltoLdFlags, "-Wl,--save-temps=import")
152				ltoLdFlags = append(ltoLdFlags, "-Wl,--thinlto-emit-index-files")
153			}
154		}
155
156		flags.Local.CFlags = append(flags.Local.CFlags, ltoCFlags...)
157		flags.Local.AsFlags = append(flags.Local.AsFlags, ltoCFlags...)
158		flags.Local.LdFlags = append(flags.Local.LdFlags, ltoCFlags...)
159		flags.Local.LdFlags = append(flags.Local.LdFlags, ltoLdFlags...)
160	}
161	return flags
162}
163
164func (lto *lto) ThinLTO() bool {
165	return lto != nil && proptools.Bool(lto.Properties.Lto.Thin)
166}
167
168func (lto *lto) Never() bool {
169	return lto != nil && proptools.Bool(lto.Properties.Lto.Never)
170}
171
172func ltoPropagateViaDepTag(tag blueprint.DependencyTag) bool {
173	libTag, isLibTag := tag.(libraryDependencyTag)
174	// Do not recurse down non-static dependencies
175	if isLibTag {
176		return libTag.static()
177	} else {
178		return tag == objDepTag || tag == reuseObjTag || tag == staticVariantTag
179	}
180}
181
182// ltoTransitionMutator creates LTO variants of cc modules.  Variant "" is the default variant, which may
183// or may not have LTO enabled depending on the config and the module's type and properties.  "lto-thin" or
184// "lto-none" variants are created when a module needs to compile in the non-default state for that module.
185type ltoTransitionMutator struct{}
186
187const LTO_NONE_VARIATION = "lto-none"
188const LTO_THIN_VARIATION = "lto-thin"
189
190func (l *ltoTransitionMutator) Split(ctx android.BaseModuleContext) []string {
191	return []string{""}
192}
193
194func (l *ltoTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
195	if m, ok := ctx.Module().(*Module); ok && m.lto != nil {
196		if !ltoPropagateViaDepTag(ctx.DepTag()) {
197			return ""
198		}
199
200		if sourceVariation != "" {
201			return sourceVariation
202		}
203
204		// Always request an explicit variation, IncomingTransition will rewrite it back to the default variation
205		// if necessary.
206		if m.lto.Properties.LtoEnabled {
207			return LTO_THIN_VARIATION
208		} else {
209			return LTO_NONE_VARIATION
210		}
211	}
212	return ""
213}
214
215func (l *ltoTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
216	if m, ok := ctx.Module().(*Module); ok && m.lto != nil {
217		if m.lto.Never() {
218			return ""
219		}
220		// Rewrite explicit variations back to the default variation if the default variation matches.
221		if incomingVariation == LTO_THIN_VARIATION && m.lto.Properties.LtoDefault {
222			return ""
223		} else if incomingVariation == LTO_NONE_VARIATION && !m.lto.Properties.LtoDefault {
224			return ""
225		}
226		return incomingVariation
227	}
228	return ""
229}
230
231func (l *ltoTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
232	// Default module which will be installed. Variation set above according to explicit LTO properties.
233	if variation == "" {
234		return
235	}
236
237	if m, ok := ctx.Module().(*Module); ok && m.lto != nil {
238		// Non-default variation, set the LTO properties to match the variation.
239		switch variation {
240		case LTO_THIN_VARIATION:
241			m.lto.Properties.LtoEnabled = true
242		case LTO_NONE_VARIATION:
243			m.lto.Properties.LtoEnabled = false
244		default:
245			panic(fmt.Errorf("unknown variation %s", variation))
246		}
247		// Non-default variations are never installed.
248		m.Properties.PreventInstall = true
249		m.Properties.HideFromMake = true
250	}
251}
252