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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

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