1// Copyright 2021 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	"strings"
19	"testing"
20
21	"android/soong/android"
22
23	"github.com/google/blueprint"
24)
25
26var LTOPreparer = android.GroupFixturePreparers(
27	prepareForCcTest,
28)
29
30func hasDep(result *android.TestResult, m android.Module, wantDep android.Module) bool {
31	var found bool
32	result.VisitDirectDeps(m, func(dep blueprint.Module) {
33		if dep == wantDep {
34			found = true
35		}
36	})
37	return found
38}
39
40func TestThinLtoDeps(t *testing.T) {
41	t.Parallel()
42	bp := `
43	cc_library_shared {
44		name: "lto_enabled",
45		srcs: ["src.c"],
46		static_libs: ["foo", "lib_never_lto"],
47		shared_libs: ["bar"],
48	}
49	cc_library_static {
50		name: "foo",
51		static_libs: ["baz"],
52	}
53	cc_library_shared {
54		name: "bar",
55		static_libs: ["qux"],
56	}
57	cc_library_static {
58		name: "baz",
59	}
60	cc_library_static {
61		name: "qux",
62	}
63	cc_library_static {
64		name: "lib_never_lto",
65		lto: {
66			never: true,
67		},
68	}
69`
70
71	result := LTOPreparer.RunTestWithBp(t, bp)
72
73	libLto := result.ModuleForTests("lto_enabled", "android_arm64_armv8-a_shared").Module()
74
75	libFoo := result.ModuleForTests("foo", "android_arm64_armv8-a_static").Module()
76	if !hasDep(result, libLto, libFoo) {
77		t.Errorf("'lto_enabled' missing dependency on the default variant of 'foo'")
78	}
79
80	libBaz := result.ModuleForTests("baz", "android_arm64_armv8-a_static").Module()
81	if !hasDep(result, libFoo, libBaz) {
82		t.Errorf("'foo' missing dependency on the default variant of transitive dep 'baz'")
83	}
84
85	libNeverLto := result.ModuleForTests("lib_never_lto", "android_arm64_armv8-a_static").Module()
86	if !hasDep(result, libLto, libNeverLto) {
87		t.Errorf("'lto_enabled' missing dependency on the default variant of 'lib_never_lto'")
88	}
89
90	libBar := result.ModuleForTests("bar", "android_arm64_armv8-a_shared").Module()
91	if !hasDep(result, libLto, libBar) {
92		t.Errorf("'lto_enabled' missing dependency on the default variant of 'bar'")
93	}
94
95	barVariants := result.ModuleVariantsForTests("bar")
96	for _, v := range barVariants {
97		if strings.Contains(v, "lto-none") {
98			t.Errorf("Expected variants for 'bar' to not contain 'lto-none', but found %q", v)
99		}
100	}
101	quxVariants := result.ModuleVariantsForTests("qux")
102	for _, v := range quxVariants {
103		if strings.Contains(v, "lto-none") {
104			t.Errorf("Expected variants for 'qux' to not contain 'lto-none', but found %q", v)
105		}
106	}
107}
108
109func TestThinLtoOnlyOnStaticDep(t *testing.T) {
110	t.Parallel()
111	bp := `
112	cc_library_shared {
113		name: "root",
114		srcs: ["src.c"],
115		static_libs: ["foo"],
116	}
117	cc_library_shared {
118		name: "root_no_lto",
119		srcs: ["src.c"],
120		static_libs: ["foo"],
121		lto: {
122			never: true,
123		}
124	}
125	cc_library_static {
126		name: "foo",
127		srcs: ["foo.c"],
128		static_libs: ["baz"],
129		lto: {
130			thin: true,
131		}
132	}
133	cc_library_static {
134		name: "baz",
135		srcs: ["baz.c"],
136	}
137`
138
139	result := LTOPreparer.RunTestWithBp(t, bp)
140
141	libRoot := result.ModuleForTests("root", "android_arm64_armv8-a_shared").Module()
142	libRootLtoNever := result.ModuleForTests("root_no_lto", "android_arm64_armv8-a_shared").Module()
143
144	libFoo := result.ModuleForTests("foo", "android_arm64_armv8-a_static")
145	if !hasDep(result, libRoot, libFoo.Module()) {
146		t.Errorf("'root' missing dependency on the default variant of 'foo'")
147	}
148
149	libFooNoLto := result.ModuleForTests("foo", "android_arm64_armv8-a_static_lto-none")
150	if !hasDep(result, libRootLtoNever, libFooNoLto.Module()) {
151		t.Errorf("'root_no_lto' missing dependency on the lto_none variant of 'foo'")
152	}
153
154	libFooCFlags := libFoo.Rule("cc").Args["cFlags"]
155	if w := "-flto=thin -fsplit-lto-unit"; !strings.Contains(libFooCFlags, w) {
156		t.Errorf("'foo' expected to have flags %q, but got %q", w, libFooCFlags)
157	}
158
159	libBaz := result.ModuleForTests("baz", "android_arm64_armv8-a_static")
160	if !hasDep(result, libFoo.Module(), libBaz.Module()) {
161		t.Errorf("'foo' missing dependency on the default variant of transitive dep 'baz'")
162	}
163
164	libBazCFlags := libFoo.Rule("cc").Args["cFlags"]
165	if w := "-flto=thin -fsplit-lto-unit"; !strings.Contains(libBazCFlags, w) {
166		t.Errorf("'baz' expected to have flags %q, but got %q", w, libFooCFlags)
167	}
168}
169
170func TestLtoDisabledButEnabledForArch(t *testing.T) {
171	t.Parallel()
172	bp := `
173	cc_library {
174		name: "libfoo",
175		srcs: ["foo.c"],
176		lto: {
177			never: true,
178		},
179		target: {
180			android_arm: {
181				lto: {
182					never: false,
183					thin: true,
184				},
185			},
186		},
187	}`
188	result := LTOPreparer.RunTestWithBp(t, bp)
189
190	libFooWithLto := result.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
191	libFooWithoutLto := result.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("ld")
192
193	android.AssertStringDoesContain(t, "missing flag for LTO in variant that expects it",
194		libFooWithLto.Args["ldFlags"], "-flto=thin")
195	android.AssertStringDoesNotContain(t, "got flag for LTO in variant that doesn't expect it",
196		libFooWithoutLto.Args["ldFlags"], "-flto=thin")
197}
198
199func TestLtoDoesNotPropagateToRuntimeLibs(t *testing.T) {
200	t.Parallel()
201	bp := `
202	cc_library {
203		name: "runtime_libbar",
204		srcs: ["bar.c"],
205	}
206
207	cc_library {
208		name: "libfoo",
209		srcs: ["foo.c"],
210		runtime_libs: ["runtime_libbar"],
211		lto: {
212			thin: true,
213		},
214	}`
215
216	result := LTOPreparer.RunTestWithBp(t, bp)
217
218	libFoo := result.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
219	libBar := result.ModuleForTests("runtime_libbar", "android_arm_armv7-a-neon_shared").Rule("ld")
220
221	android.AssertStringDoesContain(t, "missing flag for LTO in LTO enabled library",
222		libFoo.Args["ldFlags"], "-flto=thin")
223	android.AssertStringDoesNotContain(t, "got flag for LTO in runtime_lib",
224		libBar.Args["ldFlags"], "-flto=thin")
225}
226