1//===- unittests/Driver/MultilibTest.cpp --- Multilib tests ---------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Unit tests for Multilib and MultilibSet
10//
11//===----------------------------------------------------------------------===//
12
13#include "clang/Driver/Multilib.h"
14#include "../../lib/Driver/ToolChains/CommonArgs.h"
15#include "clang/Basic/LLVM.h"
16#include "clang/Basic/Version.h"
17#include "llvm/ADT/ArrayRef.h"
18#include "llvm/ADT/StringRef.h"
19#include "llvm/ADT/StringSwitch.h"
20#include "llvm/Support/SourceMgr.h"
21#include "gtest/gtest.h"
22
23using namespace clang::driver;
24using namespace clang;
25
26TEST(MultilibTest, OpEqReflexivity1) {
27 Multilib M;
28 ASSERT_TRUE(M == M) << "Multilib::operator==() is not reflexive";
29}
30
31TEST(MultilibTest, OpEqReflexivity2) {
32 ASSERT_TRUE(Multilib() == Multilib())
33 << "Separately constructed default multilibs are not equal";
34}
35
36TEST(MultilibTest, OpEqReflexivity3) {
37 Multilib M1({}, {}, {}, {"+foo"});
38 Multilib M2({}, {}, {}, {"+foo"});
39 ASSERT_TRUE(M1 == M2) << "Multilibs with the same flag should be the same";
40}
41
42TEST(MultilibTest, OpEqInequivalence1) {
43 Multilib M1({}, {}, {}, {"+foo"});
44 Multilib M2({}, {}, {}, {"-foo"});
45 ASSERT_FALSE(M1 == M2) << "Multilibs with conflicting flags are not the same";
46 ASSERT_FALSE(M2 == M1)
47 << "Multilibs with conflicting flags are not the same (commuted)";
48}
49
50TEST(MultilibTest, OpEqInequivalence2) {
51 Multilib M1;
52 Multilib M2({}, {}, {}, {"+foo"});
53 ASSERT_FALSE(M1 == M2) << "Flags make Multilibs different";
54}
55
56TEST(MultilibTest, OpEqEquivalence2) {
57 Multilib M1("/64");
58 Multilib M2("/64");
59 ASSERT_TRUE(M1 == M2)
60 << "Constructor argument must match Multilib::gccSuffix()";
61 ASSERT_TRUE(M2 == M1)
62 << "Constructor argument must match Multilib::gccSuffix() (commuted)";
63}
64
65TEST(MultilibTest, OpEqEquivalence3) {
66 Multilib M1("", "/32");
67 Multilib M2("", "/32");
68 ASSERT_TRUE(M1 == M2)
69 << "Constructor argument must match Multilib::osSuffix()";
70 ASSERT_TRUE(M2 == M1)
71 << "Constructor argument must match Multilib::osSuffix() (commuted)";
72}
73
74TEST(MultilibTest, OpEqEquivalence4) {
75 Multilib M1("", "", "/16");
76 Multilib M2("", "", "/16");
77 ASSERT_TRUE(M1 == M2)
78 << "Constructor argument must match Multilib::includeSuffix()";
79 ASSERT_TRUE(M2 == M1)
80 << "Constructor argument must match Multilib::includeSuffix() (commuted)";
81}
82
83TEST(MultilibTest, OpEqInequivalence3) {
84 Multilib M1("/foo");
85 Multilib M2("/bar");
86 ASSERT_FALSE(M1 == M2) << "Differing gccSuffixes should be different";
87 ASSERT_FALSE(M2 == M1)
88 << "Differing gccSuffixes should be different (commuted)";
89}
90
91TEST(MultilibTest, OpEqInequivalence4) {
92 Multilib M1("", "/foo");
93 Multilib M2("", "/bar");
94 ASSERT_FALSE(M1 == M2) << "Differing osSuffixes should be different";
95 ASSERT_FALSE(M2 == M1)
96 << "Differing osSuffixes should be different (commuted)";
97}
98
99TEST(MultilibTest, OpEqInequivalence5) {
100 Multilib M1("", "", "/foo");
101 Multilib M2("", "", "/bar");
102 ASSERT_FALSE(M1 == M2) << "Differing includeSuffixes should be different";
103 ASSERT_FALSE(M2 == M1)
104 << "Differing includeSuffixes should be different (commuted)";
105}
106
107TEST(MultilibTest, Construction1) {
108 Multilib M("/gcc64", "/os64", "/inc64");
109 ASSERT_TRUE(M.gccSuffix() == "/gcc64");
110 ASSERT_TRUE(M.osSuffix() == "/os64");
111 ASSERT_TRUE(M.includeSuffix() == "/inc64");
112}
113
114TEST(MultilibTest, Construction2) {
115 Multilib M1;
116 Multilib M2("");
117 Multilib M3("", "");
118 Multilib M4("", "", "");
119 ASSERT_TRUE(M1 == M2)
120 << "Default arguments to Multilib constructor broken (first argument)";
121 ASSERT_TRUE(M1 == M3)
122 << "Default arguments to Multilib constructor broken (second argument)";
123 ASSERT_TRUE(M1 == M4)
124 << "Default arguments to Multilib constructor broken (third argument)";
125}
126
127TEST(MultilibTest, Construction3) {
128 Multilib M({}, {}, {}, {"+f1", "+f2", "-f3"});
129 for (Multilib::flags_list::const_iterator I = M.flags().begin(),
130 E = M.flags().end();
131 I != E; ++I) {
132 ASSERT_TRUE(llvm::StringSwitch<bool>(*I)
133 .Cases("+f1", "+f2", "-f3", true)
134 .Default(false));
135 }
136}
137
138TEST(MultilibTest, SetPushback) {
139 MultilibSet MS({
140 Multilib("/one"),
141 Multilib("/two"),
142 });
143 ASSERT_TRUE(MS.size() == 2);
144 for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) {
145 ASSERT_TRUE(llvm::StringSwitch<bool>(I->gccSuffix())
146 .Cases("/one", "/two", true)
147 .Default(false));
148 }
149}
150
151TEST(MultilibTest, SetPriority) {
152 MultilibSet MS({
153 Multilib("/foo", {}, {}, {"+foo"}),
154 Multilib("/bar", {}, {}, {"+bar"}),
155 });
156 Multilib::flags_list Flags1 = {"+foo", "-bar"};
157 llvm::SmallVector<Multilib> Selection1;
158 ASSERT_TRUE(MS.select(Flags1, Selection1))
159 << "Flag set was {\"+foo\"}, but selection not found";
160 ASSERT_TRUE(Selection1.back().gccSuffix() == "/foo")
161 << "Selection picked " << Selection1.back() << " which was not expected";
162
163 Multilib::flags_list Flags2 = {"+foo", "+bar"};
164 llvm::SmallVector<Multilib> Selection2;
165 ASSERT_TRUE(MS.select(Flags2, Selection2))
166 << "Flag set was {\"+bar\"}, but selection not found";
167 ASSERT_TRUE(Selection2.back().gccSuffix() == "/bar")
168 << "Selection picked " << Selection2.back() << " which was not expected";
169}
170
171TEST(MultilibTest, SelectMultiple) {
172 MultilibSet MS({
173 Multilib("/a", {}, {}, {"x"}),
174 Multilib("/b", {}, {}, {"y"}),
175 });
176 llvm::SmallVector<Multilib> Selection;
177
178 ASSERT_TRUE(MS.select({"x"}, Selection));
179 ASSERT_EQ(1u, Selection.size());
180 EXPECT_EQ("/a", Selection[0].gccSuffix());
181
182 ASSERT_TRUE(MS.select({"y"}, Selection));
183 ASSERT_EQ(1u, Selection.size());
184 EXPECT_EQ("/b", Selection[0].gccSuffix());
185
186 ASSERT_TRUE(MS.select({"y", "x"}, Selection));
187 ASSERT_EQ(2u, Selection.size());
188 EXPECT_EQ("/a", Selection[0].gccSuffix());
189 EXPECT_EQ("/b", Selection[1].gccSuffix());
190}
191
192static void diagnosticCallback(const llvm::SMDiagnostic &D, void *Out) {
193 *reinterpret_cast<std::string *>(Out) = D.getMessage();
194}
195
196static bool parseYaml(MultilibSet &MS, std::string &Diagnostic,
197 const char *Data) {
198 auto ErrorOrMS = MultilibSet::parseYaml(llvm::MemoryBufferRef(Data, "TEST"),
199 diagnosticCallback, DiagHandlerCtxt: &Diagnostic);
200 if (ErrorOrMS.getError())
201 return false;
202 MS = std::move(ErrorOrMS.get());
203 return true;
204}
205
206static bool parseYaml(MultilibSet &MS, const char *Data) {
207 auto ErrorOrMS = MultilibSet::parseYaml(llvm::MemoryBufferRef(Data, "TEST"));
208 if (ErrorOrMS.getError())
209 return false;
210 MS = std::move(ErrorOrMS.get());
211 return true;
212}
213
214// When updating this version also update MultilibVersionCurrent in Multilib.cpp
215#define YAML_PREAMBLE "MultilibVersion: 1.0\n"
216
217TEST(MultilibTest, ParseInvalid) {
218 std::string Diagnostic;
219
220 MultilibSet MS;
221
222 EXPECT_FALSE(parseYaml(MS, Diagnostic, R"(
223Variants: []
224)"));
225 EXPECT_TRUE(
226 StringRef(Diagnostic).contains("missing required key 'MultilibVersion'"))
227 << Diagnostic;
228
229 // Reject files with a different major version
230 EXPECT_FALSE(parseYaml(MS, Diagnostic,
231 R"(
232MultilibVersion: 2.0
233Variants: []
234)"));
235 EXPECT_TRUE(
236 StringRef(Diagnostic).contains("multilib version 2.0 is unsupported"))
237 << Diagnostic;
238 EXPECT_FALSE(parseYaml(MS, Diagnostic,
239 R"(
240MultilibVersion: 0.1
241Variants: []
242)"));
243 EXPECT_TRUE(
244 StringRef(Diagnostic).contains("multilib version 0.1 is unsupported"))
245 << Diagnostic;
246
247 // Reject files with a later minor version
248 EXPECT_FALSE(parseYaml(MS, Diagnostic,
249 R"(
250MultilibVersion: 1.9
251Variants: []
252)"));
253 EXPECT_TRUE(
254 StringRef(Diagnostic).contains("multilib version 1.9 is unsupported"))
255 << Diagnostic;
256
257 // Accept files with the same major version and the same or earlier minor
258 // version
259 EXPECT_TRUE(parseYaml(MS, Diagnostic, R"(
260MultilibVersion: 1.0
261Variants: []
262)")) << Diagnostic;
263
264 EXPECT_FALSE(parseYaml(MS, Diagnostic, YAML_PREAMBLE));
265 EXPECT_TRUE(StringRef(Diagnostic).contains("missing required key 'Variants'"))
266 << Diagnostic;
267
268 EXPECT_FALSE(parseYaml(MS, Diagnostic, YAML_PREAMBLE R"(
269Variants:
270- Dir: /abc
271 Flags: []
272)"));
273 EXPECT_TRUE(StringRef(Diagnostic).contains("paths must be relative"))
274 << Diagnostic;
275
276 EXPECT_FALSE(parseYaml(MS, Diagnostic, YAML_PREAMBLE R"(
277Variants:
278- Flags: []
279)"));
280 EXPECT_TRUE(StringRef(Diagnostic).contains("missing required key 'Dir'"))
281 << Diagnostic;
282
283 EXPECT_FALSE(parseYaml(MS, Diagnostic, YAML_PREAMBLE R"(
284Variants:
285- Dir: .
286)"));
287 EXPECT_TRUE(StringRef(Diagnostic).contains("missing required key 'Flags'"))
288 << Diagnostic;
289
290 EXPECT_FALSE(parseYaml(MS, Diagnostic, YAML_PREAMBLE R"(
291Variants: []
292Mappings:
293- Match: abc
294)"));
295 EXPECT_TRUE(StringRef(Diagnostic).contains("value required for 'Flags'"))
296 << Diagnostic;
297
298 EXPECT_FALSE(parseYaml(MS, Diagnostic, YAML_PREAMBLE R"(
299Variants: []
300Mappings:
301- Dir: .
302 Match: '('
303 Flags: []
304)"));
305 EXPECT_TRUE(StringRef(Diagnostic).contains("parentheses not balanced"))
306 << Diagnostic;
307}
308
309TEST(MultilibTest, Parse) {
310 MultilibSet MS;
311 EXPECT_TRUE(parseYaml(MS, YAML_PREAMBLE R"(
312Variants:
313- Dir: .
314 Flags: []
315)"));
316 EXPECT_EQ(1U, MS.size());
317 EXPECT_EQ("", MS.begin()->gccSuffix());
318
319 EXPECT_TRUE(parseYaml(MS, YAML_PREAMBLE R"(
320Variants:
321- Dir: abc
322 Flags: []
323)"));
324 EXPECT_EQ(1U, MS.size());
325 EXPECT_EQ("/abc", MS.begin()->gccSuffix());
326
327 EXPECT_TRUE(parseYaml(MS, YAML_PREAMBLE R"(
328Variants:
329- Dir: pqr
330 Flags: [-mfloat-abi=soft]
331)"));
332 EXPECT_EQ(1U, MS.size());
333 EXPECT_EQ("/pqr", MS.begin()->gccSuffix());
334 EXPECT_EQ(std::vector<std::string>({"-mfloat-abi=soft"}),
335 MS.begin()->flags());
336
337 EXPECT_TRUE(parseYaml(MS, YAML_PREAMBLE R"(
338Variants:
339- Dir: pqr
340 Flags: [-mfloat-abi=soft, -fno-exceptions]
341)"));
342 EXPECT_EQ(1U, MS.size());
343 EXPECT_EQ(std::vector<std::string>({"-mfloat-abi=soft", "-fno-exceptions"}),
344 MS.begin()->flags());
345
346 EXPECT_TRUE(parseYaml(MS, YAML_PREAMBLE R"(
347Variants:
348- Dir: a
349 Flags: []
350- Dir: b
351 Flags: []
352)"));
353 EXPECT_EQ(2U, MS.size());
354}
355
356TEST(MultilibTest, SelectSoft) {
357 MultilibSet MS;
358 llvm::SmallVector<Multilib> Selected;
359 ASSERT_TRUE(parseYaml(MS, YAML_PREAMBLE R"(
360Variants:
361- Dir: s
362 Flags: [-mfloat-abi=soft]
363Mappings:
364- Match: -mfloat-abi=softfp
365 Flags: [-mfloat-abi=soft]
366)"));
367 EXPECT_TRUE(MS.select({"-mfloat-abi=soft"}, Selected));
368 EXPECT_TRUE(MS.select({"-mfloat-abi=softfp"}, Selected));
369 EXPECT_FALSE(MS.select({"-mfloat-abi=hard"}, Selected));
370}
371
372TEST(MultilibTest, SelectSoftFP) {
373 MultilibSet MS;
374 llvm::SmallVector<Multilib> Selected;
375 ASSERT_TRUE(parseYaml(MS, YAML_PREAMBLE R"(
376Variants:
377- Dir: f
378 Flags: [-mfloat-abi=softfp]
379)"));
380 EXPECT_FALSE(MS.select({"-mfloat-abi=soft"}, Selected));
381 EXPECT_TRUE(MS.select({"-mfloat-abi=softfp"}, Selected));
382 EXPECT_FALSE(MS.select({"-mfloat-abi=hard"}, Selected));
383}
384
385TEST(MultilibTest, SelectHard) {
386 // If hard float is all that's available then select that only if compiling
387 // with hard float.
388 MultilibSet MS;
389 llvm::SmallVector<Multilib> Selected;
390 ASSERT_TRUE(parseYaml(MS, YAML_PREAMBLE R"(
391Variants:
392- Dir: h
393 Flags: [-mfloat-abi=hard]
394)"));
395 EXPECT_FALSE(MS.select({"-mfloat-abi=soft"}, Selected));
396 EXPECT_FALSE(MS.select({"-mfloat-abi=softfp"}, Selected));
397 EXPECT_TRUE(MS.select({"-mfloat-abi=hard"}, Selected));
398}
399
400TEST(MultilibTest, SelectFloatABI) {
401 MultilibSet MS;
402 llvm::SmallVector<Multilib> Selected;
403 ASSERT_TRUE(parseYaml(MS, YAML_PREAMBLE R"(
404Variants:
405- Dir: s
406 Flags: [-mfloat-abi=soft]
407- Dir: f
408 Flags: [-mfloat-abi=softfp]
409- Dir: h
410 Flags: [-mfloat-abi=hard]
411Mappings:
412- Match: -mfloat-abi=softfp
413 Flags: [-mfloat-abi=soft]
414)"));
415 MS.select(Flags: {"-mfloat-abi=soft"}, Selected);
416 EXPECT_EQ("/s", Selected.back().gccSuffix());
417 MS.select(Flags: {"-mfloat-abi=softfp"}, Selected);
418 EXPECT_EQ("/f", Selected.back().gccSuffix());
419 MS.select(Flags: {"-mfloat-abi=hard"}, Selected);
420 EXPECT_EQ("/h", Selected.back().gccSuffix());
421}
422
423TEST(MultilibTest, SelectFloatABIReversed) {
424 // If soft is specified after softfp then softfp will never be
425 // selected because soft is compatible with softfp and last wins.
426 MultilibSet MS;
427 llvm::SmallVector<Multilib> Selected;
428 ASSERT_TRUE(parseYaml(MS, YAML_PREAMBLE R"(
429Variants:
430- Dir: h
431 Flags: [-mfloat-abi=hard]
432- Dir: f
433 Flags: [-mfloat-abi=softfp]
434- Dir: s
435 Flags: [-mfloat-abi=soft]
436Mappings:
437- Match: -mfloat-abi=softfp
438 Flags: [-mfloat-abi=soft]
439)"));
440 MS.select(Flags: {"-mfloat-abi=soft"}, Selected);
441 EXPECT_EQ("/s", Selected.back().gccSuffix());
442 MS.select(Flags: {"-mfloat-abi=softfp"}, Selected);
443 EXPECT_EQ("/s", Selected.back().gccSuffix());
444 MS.select(Flags: {"-mfloat-abi=hard"}, Selected);
445 EXPECT_EQ("/h", Selected.back().gccSuffix());
446}
447
448TEST(MultilibTest, SelectMClass) {
449 const char *MultilibSpec = YAML_PREAMBLE R"(
450Variants:
451- Dir: thumb/v6-m/nofp
452 Flags: [--target=thumbv6m-none-unknown-eabi, -mfpu=none]
453
454- Dir: thumb/v7-m/nofp
455 Flags: [--target=thumbv7m-none-unknown-eabi, -mfpu=none]
456
457- Dir: thumb/v7e-m/nofp
458 Flags: [--target=thumbv7em-none-unknown-eabi, -mfpu=none]
459
460- Dir: thumb/v8-m.main/nofp
461 Flags: [--target=thumbv8m.main-none-unknown-eabi, -mfpu=none]
462
463- Dir: thumb/v8.1-m.main/nofp/nomve
464 Flags: [--target=thumbv8.1m.main-none-unknown-eabi, -mfpu=none]
465
466- Dir: thumb/v7e-m/fpv4_sp_d16
467 Flags: [--target=thumbv7em-none-unknown-eabihf, -mfpu=fpv4-sp-d16]
468
469- Dir: thumb/v7e-m/fpv5_d16
470 Flags: [--target=thumbv7em-none-unknown-eabihf, -mfpu=fpv5-d16]
471
472- Dir: thumb/v8-m.main/fp
473 Flags: [--target=thumbv8m.main-none-unknown-eabihf]
474
475- Dir: thumb/v8.1-m.main/fp
476 Flags: [--target=thumbv8.1m.main-none-unknown-eabihf]
477
478- Dir: thumb/v8.1-m.main/nofp/mve
479 Flags: [--target=thumbv8.1m.main-none-unknown-eabihf, -march=thumbv8.1m.main+mve]
480
481Mappings:
482- Match: --target=thumbv8(\.[0-9]+)?m\.base-none-unknown-eabi
483 Flags: [--target=thumbv6m-none-unknown-eabi]
484- Match: -target=thumbv8\.[1-9]m\.main-none-unknown-eabi
485 Flags: [--target=thumbv8.1m.main-none-unknown-eabi]
486- Match: -target=thumbv8\.[1-9]m\.main-none-unknown-eabihf
487 Flags: [--target=thumbv8.1m.main-none-unknown-eabihf]
488- Match: -march=thumbv8\.[1-9]m\.main.*\+mve($|\+).*
489 Flags: [-march=thumbv8.1m.main+mve]
490)";
491
492 MultilibSet MS;
493 llvm::SmallVector<Multilib> Selected;
494 ASSERT_TRUE(parseYaml(MS, MultilibSpec));
495
496 ASSERT_TRUE(MS.select({"--target=thumbv6m-none-unknown-eabi", "-mfpu=none"},
497 Selected));
498 EXPECT_EQ("/thumb/v6-m/nofp", Selected.back().gccSuffix());
499
500 ASSERT_TRUE(MS.select({"--target=thumbv7m-none-unknown-eabi", "-mfpu=none"},
501 Selected));
502 EXPECT_EQ("/thumb/v7-m/nofp", Selected.back().gccSuffix());
503
504 ASSERT_TRUE(MS.select({"--target=thumbv7em-none-unknown-eabi", "-mfpu=none"},
505 Selected));
506 EXPECT_EQ("/thumb/v7e-m/nofp", Selected.back().gccSuffix());
507
508 ASSERT_TRUE(MS.select(
509 {"--target=thumbv8m.main-none-unknown-eabi", "-mfpu=none"}, Selected));
510 EXPECT_EQ("/thumb/v8-m.main/nofp", Selected.back().gccSuffix());
511
512 ASSERT_TRUE(MS.select(
513 {"--target=thumbv8.1m.main-none-unknown-eabi", "-mfpu=none"}, Selected));
514 EXPECT_EQ("/thumb/v8.1-m.main/nofp/nomve", Selected.back().gccSuffix());
515
516 ASSERT_TRUE(
517 MS.select({"--target=thumbv7em-none-unknown-eabihf", "-mfpu=fpv4-sp-d16"},
518 Selected));
519 EXPECT_EQ("/thumb/v7e-m/fpv4_sp_d16", Selected.back().gccSuffix());
520
521 ASSERT_TRUE(MS.select(
522 {"--target=thumbv7em-none-unknown-eabihf", "-mfpu=fpv5-d16"}, Selected));
523 EXPECT_EQ("/thumb/v7e-m/fpv5_d16", Selected.back().gccSuffix());
524
525 ASSERT_TRUE(
526 MS.select({"--target=thumbv8m.main-none-unknown-eabihf"}, Selected));
527 EXPECT_EQ("/thumb/v8-m.main/fp", Selected.back().gccSuffix());
528
529 ASSERT_TRUE(
530 MS.select({"--target=thumbv8.1m.main-none-unknown-eabihf"}, Selected));
531 EXPECT_EQ("/thumb/v8.1-m.main/fp", Selected.back().gccSuffix());
532
533 ASSERT_TRUE(MS.select({"--target=thumbv8.1m.main-none-unknown-eabihf",
534 "-mfpu=none", "-march=thumbv8.1m.main+dsp+mve"},
535 Selected));
536 EXPECT_EQ("/thumb/v8.1-m.main/nofp/mve", Selected.back().gccSuffix());
537}
538

source code of clang/unittests/Driver/MultilibTest.cpp