//===- unittests/Driver/MultilibTest.cpp --- Multilib tests ---------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Unit tests for Multilib and MultilibSet // //===----------------------------------------------------------------------===// #include "clang/Driver/Multilib.h" #include "../../lib/Driver/ToolChains/CommonArgs.h" #include "SimpleDiagnosticConsumer.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/Version.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/SourceMgr.h" #include "gtest/gtest.h" using namespace clang::driver; using namespace clang; TEST(MultilibTest, OpEqReflexivity1) { Multilib M; ASSERT_TRUE(M == M) << "Multilib::operator==() is not reflexive"; } TEST(MultilibTest, OpEqReflexivity2) { ASSERT_TRUE(Multilib() == Multilib()) << "Separately constructed default multilibs are not equal"; } TEST(MultilibTest, OpEqReflexivity3) { Multilib M1({}, {}, {}, {"+foo"}); Multilib M2({}, {}, {}, {"+foo"}); ASSERT_TRUE(M1 == M2) << "Multilibs with the same flag should be the same"; } TEST(MultilibTest, OpEqInequivalence1) { Multilib M1({}, {}, {}, {"+foo"}); Multilib M2({}, {}, {}, {"-foo"}); ASSERT_FALSE(M1 == M2) << "Multilibs with conflicting flags are not the same"; ASSERT_FALSE(M2 == M1) << "Multilibs with conflicting flags are not the same (commuted)"; } TEST(MultilibTest, OpEqInequivalence2) { Multilib M1; Multilib M2({}, {}, {}, {"+foo"}); ASSERT_FALSE(M1 == M2) << "Flags make Multilibs different"; } TEST(MultilibTest, OpEqEquivalence2) { Multilib M1("/64"); Multilib M2("/64"); ASSERT_TRUE(M1 == M2) << "Constructor argument must match Multilib::gccSuffix()"; ASSERT_TRUE(M2 == M1) << "Constructor argument must match Multilib::gccSuffix() (commuted)"; } TEST(MultilibTest, OpEqEquivalence3) { Multilib M1("", "/32"); Multilib M2("", "/32"); ASSERT_TRUE(M1 == M2) << "Constructor argument must match Multilib::osSuffix()"; ASSERT_TRUE(M2 == M1) << "Constructor argument must match Multilib::osSuffix() (commuted)"; } TEST(MultilibTest, OpEqEquivalence4) { Multilib M1("", "", "/16"); Multilib M2("", "", "/16"); ASSERT_TRUE(M1 == M2) << "Constructor argument must match Multilib::includeSuffix()"; ASSERT_TRUE(M2 == M1) << "Constructor argument must match Multilib::includeSuffix() (commuted)"; } TEST(MultilibTest, OpEqInequivalence3) { Multilib M1("/foo"); Multilib M2("/bar"); ASSERT_FALSE(M1 == M2) << "Differing gccSuffixes should be different"; ASSERT_FALSE(M2 == M1) << "Differing gccSuffixes should be different (commuted)"; } TEST(MultilibTest, OpEqInequivalence4) { Multilib M1("", "/foo"); Multilib M2("", "/bar"); ASSERT_FALSE(M1 == M2) << "Differing osSuffixes should be different"; ASSERT_FALSE(M2 == M1) << "Differing osSuffixes should be different (commuted)"; } TEST(MultilibTest, OpEqInequivalence5) { Multilib M1("", "", "/foo"); Multilib M2("", "", "/bar"); ASSERT_FALSE(M1 == M2) << "Differing includeSuffixes should be different"; ASSERT_FALSE(M2 == M1) << "Differing includeSuffixes should be different (commuted)"; } TEST(MultilibTest, Construction1) { Multilib M("/gcc64", "/os64", "/inc64"); ASSERT_TRUE(M.gccSuffix() == "/gcc64"); ASSERT_TRUE(M.osSuffix() == "/os64"); ASSERT_TRUE(M.includeSuffix() == "/inc64"); } TEST(MultilibTest, Construction2) { Multilib M1; Multilib M2(""); Multilib M3("", ""); Multilib M4("", "", ""); ASSERT_TRUE(M1 == M2) << "Default arguments to Multilib constructor broken (first argument)"; ASSERT_TRUE(M1 == M3) << "Default arguments to Multilib constructor broken (second argument)"; ASSERT_TRUE(M1 == M4) << "Default arguments to Multilib constructor broken (third argument)"; } TEST(MultilibTest, Construction3) { Multilib M({}, {}, {}, {"+f1", "+f2", "-f3"}); for (Multilib::flags_list::const_iterator I = M.flags().begin(), E = M.flags().end(); I != E; ++I) { ASSERT_TRUE(llvm::StringSwitch(*I) .Cases("+f1", "+f2", "-f3", true) .Default(false)); } } TEST(MultilibTest, SetPushback) { MultilibSet MS({ Multilib("/one"), Multilib("/two"), }); ASSERT_TRUE(MS.size() == 2); for (MultilibSet::const_iterator I = MS.begin(), E = MS.end(); I != E; ++I) { ASSERT_TRUE(llvm::StringSwitch(I->gccSuffix()) .Cases("/one", "/two", true) .Default(false)); } } TEST(MultilibTest, SetPriority) { MultilibSet MS({ Multilib("/foo", {}, {}, {"+foo"}), Multilib("/bar", {}, {}, {"+bar"}), }); Driver TheDriver = diagnostic_test_driver(); Multilib::flags_list Flags1 = {"+foo", "-bar"}; llvm::SmallVector Selection1; ASSERT_TRUE(MS.select(TheDriver, Flags1, Selection1)) << "Flag set was {\"+foo\"}, but selection not found"; ASSERT_TRUE(Selection1.back().gccSuffix() == "/foo") << "Selection picked " << Selection1.back() << " which was not expected"; Multilib::flags_list Flags2 = {"+foo", "+bar"}; llvm::SmallVector Selection2; ASSERT_TRUE(MS.select(TheDriver, Flags2, Selection2)) << "Flag set was {\"+bar\"}, but selection not found"; ASSERT_TRUE(Selection2.back().gccSuffix() == "/bar") << "Selection picked " << Selection2.back() << " which was not expected"; } TEST(MultilibTest, SelectMultiple) { MultilibSet MS({ Multilib("/a", {}, {}, {"x"}), Multilib("/b", {}, {}, {"y"}), }); llvm::SmallVector Selection; Driver TheDriver = diagnostic_test_driver(); ASSERT_TRUE(MS.select(TheDriver, {"x"}, Selection)); ASSERT_EQ(1u, Selection.size()); EXPECT_EQ("/a", Selection[0].gccSuffix()); ASSERT_TRUE(MS.select(TheDriver, {"y"}, Selection)); ASSERT_EQ(1u, Selection.size()); EXPECT_EQ("/b", Selection[0].gccSuffix()); ASSERT_TRUE(MS.select(TheDriver, {"y", "x"}, Selection)); ASSERT_EQ(2u, Selection.size()); EXPECT_EQ("/a", Selection[0].gccSuffix()); EXPECT_EQ("/b", Selection[1].gccSuffix()); } static void diagnosticCallback(const llvm::SMDiagnostic &D, void *Out) { *reinterpret_cast(Out) = D.getMessage(); } static bool parseYaml(MultilibSet &MS, std::string &Diagnostic, const char *Data) { auto ErrorOrMS = MultilibSet::parseYaml(llvm::MemoryBufferRef(Data, "TEST"), diagnosticCallback, &Diagnostic); if (ErrorOrMS.getError()) return false; MS = std::move(ErrorOrMS.get()); return true; } static bool parseYaml(MultilibSet &MS, const char *Data) { auto ErrorOrMS = MultilibSet::parseYaml(llvm::MemoryBufferRef(Data, "TEST")); if (ErrorOrMS.getError()) return false; MS = std::move(ErrorOrMS.get()); return true; } // When updating this version also update MultilibVersionCurrent in Multilib.cpp #define YAML_PREAMBLE "MultilibVersion: 1.0\n" TEST(MultilibTest, ParseInvalid) { std::string Diagnostic; MultilibSet MS; EXPECT_FALSE(parseYaml(MS, Diagnostic, R"( Variants: [] )")); EXPECT_TRUE( StringRef(Diagnostic).contains("missing required key 'MultilibVersion'")) << Diagnostic; // Reject files with a different major version EXPECT_FALSE(parseYaml(MS, Diagnostic, R"( MultilibVersion: 2.0 Variants: [] )")); EXPECT_TRUE( StringRef(Diagnostic).contains("multilib version 2.0 is unsupported")) << Diagnostic; EXPECT_FALSE(parseYaml(MS, Diagnostic, R"( MultilibVersion: 0.1 Variants: [] )")); EXPECT_TRUE( StringRef(Diagnostic).contains("multilib version 0.1 is unsupported")) << Diagnostic; // Reject files with a later minor version EXPECT_FALSE(parseYaml(MS, Diagnostic, R"( MultilibVersion: 1.9 Variants: [] )")); EXPECT_TRUE( StringRef(Diagnostic).contains("multilib version 1.9 is unsupported")) << Diagnostic; // Accept files with the same major version and the same or earlier minor // version EXPECT_TRUE(parseYaml(MS, Diagnostic, R"( MultilibVersion: 1.0 Variants: [] )")) << Diagnostic; EXPECT_FALSE(parseYaml(MS, Diagnostic, YAML_PREAMBLE)); EXPECT_TRUE(StringRef(Diagnostic).contains("missing required key 'Variants'")) << Diagnostic; EXPECT_FALSE(parseYaml(MS, Diagnostic, YAML_PREAMBLE R"( Variants: - Dir: /abc Flags: [] )")); EXPECT_TRUE(StringRef(Diagnostic).contains("paths must be relative")) << Diagnostic; EXPECT_FALSE(parseYaml(MS, Diagnostic, YAML_PREAMBLE R"( Variants: - Flags: [] )")); EXPECT_TRUE( StringRef(Diagnostic) .contains("one of the 'Dir' and 'Error' keys must be specified")) << Diagnostic; EXPECT_FALSE(parseYaml(MS, Diagnostic, YAML_PREAMBLE R"( Variants: - Dir: . )")); EXPECT_TRUE(StringRef(Diagnostic).contains("missing required key 'Flags'")) << Diagnostic; EXPECT_FALSE(parseYaml(MS, Diagnostic, YAML_PREAMBLE R"( Variants: [] Mappings: - Match: abc )")); EXPECT_TRUE(StringRef(Diagnostic).contains("value required for 'Flags'")) << Diagnostic; EXPECT_FALSE(parseYaml(MS, Diagnostic, YAML_PREAMBLE R"( Variants: [] Mappings: - Dir: . Match: '(' Flags: [] )")); EXPECT_TRUE(StringRef(Diagnostic).contains("parentheses not balanced")) << Diagnostic; } TEST(MultilibTest, Parse) { MultilibSet MS; EXPECT_TRUE(parseYaml(MS, YAML_PREAMBLE R"( Variants: - Dir: . Flags: [] )")); EXPECT_EQ(1U, MS.size()); EXPECT_EQ("", MS.begin()->gccSuffix()); EXPECT_TRUE(parseYaml(MS, YAML_PREAMBLE R"( Variants: - Dir: abc Flags: [] )")); EXPECT_EQ(1U, MS.size()); EXPECT_EQ("/abc", MS.begin()->gccSuffix()); EXPECT_TRUE(parseYaml(MS, YAML_PREAMBLE R"( Variants: - Dir: pqr Flags: [-mfloat-abi=soft] )")); EXPECT_EQ(1U, MS.size()); EXPECT_EQ("/pqr", MS.begin()->gccSuffix()); EXPECT_EQ(std::vector({"-mfloat-abi=soft"}), MS.begin()->flags()); EXPECT_TRUE(parseYaml(MS, YAML_PREAMBLE R"( Variants: - Dir: pqr Flags: [-mfloat-abi=soft, -fno-exceptions] )")); EXPECT_EQ(1U, MS.size()); EXPECT_EQ(std::vector({"-mfloat-abi=soft", "-fno-exceptions"}), MS.begin()->flags()); EXPECT_TRUE(parseYaml(MS, YAML_PREAMBLE R"( Variants: - Dir: a Flags: [] - Dir: b Flags: [] )")); EXPECT_EQ(2U, MS.size()); } TEST(MultilibTest, SelectSoft) { MultilibSet MS; llvm::SmallVector Selected; ASSERT_TRUE(parseYaml(MS, YAML_PREAMBLE R"( Variants: - Dir: s Flags: [-mfloat-abi=soft] Mappings: - Match: -mfloat-abi=softfp Flags: [-mfloat-abi=soft] )")); Driver TheDriver = diagnostic_test_driver(); EXPECT_TRUE(MS.select(TheDriver, {"-mfloat-abi=soft"}, Selected)); EXPECT_TRUE(MS.select(TheDriver, {"-mfloat-abi=softfp"}, Selected)); EXPECT_FALSE(MS.select(TheDriver, {"-mfloat-abi=hard"}, Selected)); } TEST(MultilibTest, SelectSoftFP) { MultilibSet MS; llvm::SmallVector Selected; ASSERT_TRUE(parseYaml(MS, YAML_PREAMBLE R"( Variants: - Dir: f Flags: [-mfloat-abi=softfp] )")); Driver TheDriver = diagnostic_test_driver(); EXPECT_FALSE(MS.select(TheDriver, {"-mfloat-abi=soft"}, Selected)); EXPECT_TRUE(MS.select(TheDriver, {"-mfloat-abi=softfp"}, Selected)); EXPECT_FALSE(MS.select(TheDriver, {"-mfloat-abi=hard"}, Selected)); } TEST(MultilibTest, SelectHard) { // If hard float is all that's available then select that only if compiling // with hard float. MultilibSet MS; llvm::SmallVector Selected; ASSERT_TRUE(parseYaml(MS, YAML_PREAMBLE R"( Variants: - Dir: h Flags: [-mfloat-abi=hard] )")); Driver TheDriver = diagnostic_test_driver(); EXPECT_FALSE(MS.select(TheDriver, {"-mfloat-abi=soft"}, Selected)); EXPECT_FALSE(MS.select(TheDriver, {"-mfloat-abi=softfp"}, Selected)); EXPECT_TRUE(MS.select(TheDriver, {"-mfloat-abi=hard"}, Selected)); } TEST(MultilibTest, SelectFloatABI) { MultilibSet MS; llvm::SmallVector Selected; ASSERT_TRUE(parseYaml(MS, YAML_PREAMBLE R"( Variants: - Dir: s Flags: [-mfloat-abi=soft] - Dir: f Flags: [-mfloat-abi=softfp] - Dir: h Flags: [-mfloat-abi=hard] Mappings: - Match: -mfloat-abi=softfp Flags: [-mfloat-abi=soft] )")); Driver TheDriver = diagnostic_test_driver(); MS.select(TheDriver, {"-mfloat-abi=soft"}, Selected); EXPECT_EQ("/s", Selected.back().gccSuffix()); MS.select(TheDriver, {"-mfloat-abi=softfp"}, Selected); EXPECT_EQ("/f", Selected.back().gccSuffix()); MS.select(TheDriver, {"-mfloat-abi=hard"}, Selected); EXPECT_EQ("/h", Selected.back().gccSuffix()); } TEST(MultilibTest, SelectFloatABIReversed) { // If soft is specified after softfp then softfp will never be // selected because soft is compatible with softfp and last wins. MultilibSet MS; llvm::SmallVector Selected; ASSERT_TRUE(parseYaml(MS, YAML_PREAMBLE R"( Variants: - Dir: h Flags: [-mfloat-abi=hard] - Dir: f Flags: [-mfloat-abi=softfp] - Dir: s Flags: [-mfloat-abi=soft] Mappings: - Match: -mfloat-abi=softfp Flags: [-mfloat-abi=soft] )")); Driver TheDriver = diagnostic_test_driver(); MS.select(TheDriver, {"-mfloat-abi=soft"}, Selected); EXPECT_EQ("/s", Selected.back().gccSuffix()); MS.select(TheDriver, {"-mfloat-abi=softfp"}, Selected); EXPECT_EQ("/s", Selected.back().gccSuffix()); MS.select(TheDriver, {"-mfloat-abi=hard"}, Selected); EXPECT_EQ("/h", Selected.back().gccSuffix()); } TEST(MultilibTest, SelectMClass) { Driver TheDriver = diagnostic_test_driver(); const char *MultilibSpec = YAML_PREAMBLE R"( Variants: - Dir: thumb/v6-m/nofp Flags: [--target=thumbv6m-none-unknown-eabi, -mfpu=none] - Dir: thumb/v7-m/nofp Flags: [--target=thumbv7m-none-unknown-eabi, -mfpu=none] - Dir: thumb/v7e-m/nofp Flags: [--target=thumbv7em-none-unknown-eabi, -mfpu=none] - Dir: thumb/v8-m.main/nofp Flags: [--target=thumbv8m.main-none-unknown-eabi, -mfpu=none] - Dir: thumb/v8.1-m.main/nofp/nomve Flags: [--target=thumbv8.1m.main-none-unknown-eabi, -mfpu=none] - Dir: thumb/v7e-m/fpv4_sp_d16 Flags: [--target=thumbv7em-none-unknown-eabihf, -mfpu=fpv4-sp-d16] - Dir: thumb/v7e-m/fpv5_d16 Flags: [--target=thumbv7em-none-unknown-eabihf, -mfpu=fpv5-d16] - Dir: thumb/v8-m.main/fp Flags: [--target=thumbv8m.main-none-unknown-eabihf] - Dir: thumb/v8.1-m.main/fp Flags: [--target=thumbv8.1m.main-none-unknown-eabihf] - Dir: thumb/v8.1-m.main/nofp/mve Flags: [--target=thumbv8.1m.main-none-unknown-eabihf, -march=thumbv8.1m.main+mve] Mappings: - Match: --target=thumbv8(\.[0-9]+)?m\.base-none-unknown-eabi Flags: [--target=thumbv6m-none-unknown-eabi] - Match: -target=thumbv8\.[1-9]m\.main-none-unknown-eabi Flags: [--target=thumbv8.1m.main-none-unknown-eabi] - Match: -target=thumbv8\.[1-9]m\.main-none-unknown-eabihf Flags: [--target=thumbv8.1m.main-none-unknown-eabihf] - Match: -march=thumbv8\.[1-9]m\.main.*\+mve($|\+).* Flags: [-march=thumbv8.1m.main+mve] )"; MultilibSet MS; llvm::SmallVector Selected; ASSERT_TRUE(parseYaml(MS, MultilibSpec)); ASSERT_TRUE(MS.select(TheDriver, {"--target=thumbv6m-none-unknown-eabi", "-mfpu=none"}, Selected)); EXPECT_EQ("/thumb/v6-m/nofp", Selected.back().gccSuffix()); ASSERT_TRUE(MS.select(TheDriver, {"--target=thumbv7m-none-unknown-eabi", "-mfpu=none"}, Selected)); EXPECT_EQ("/thumb/v7-m/nofp", Selected.back().gccSuffix()); ASSERT_TRUE(MS.select(TheDriver, {"--target=thumbv7em-none-unknown-eabi", "-mfpu=none"}, Selected)); EXPECT_EQ("/thumb/v7e-m/nofp", Selected.back().gccSuffix()); ASSERT_TRUE(MS.select( TheDriver, {"--target=thumbv8m.main-none-unknown-eabi", "-mfpu=none"}, Selected)); EXPECT_EQ("/thumb/v8-m.main/nofp", Selected.back().gccSuffix()); ASSERT_TRUE(MS.select( TheDriver, {"--target=thumbv8.1m.main-none-unknown-eabi", "-mfpu=none"}, Selected)); EXPECT_EQ("/thumb/v8.1-m.main/nofp/nomve", Selected.back().gccSuffix()); ASSERT_TRUE( MS.select(TheDriver, {"--target=thumbv7em-none-unknown-eabihf", "-mfpu=fpv4-sp-d16"}, Selected)); EXPECT_EQ("/thumb/v7e-m/fpv4_sp_d16", Selected.back().gccSuffix()); ASSERT_TRUE(MS.select( TheDriver, {"--target=thumbv7em-none-unknown-eabihf", "-mfpu=fpv5-d16"}, Selected)); EXPECT_EQ("/thumb/v7e-m/fpv5_d16", Selected.back().gccSuffix()); ASSERT_TRUE(MS.select( TheDriver, {"--target=thumbv8m.main-none-unknown-eabihf"}, Selected)); EXPECT_EQ("/thumb/v8-m.main/fp", Selected.back().gccSuffix()); ASSERT_TRUE(MS.select( TheDriver, {"--target=thumbv8.1m.main-none-unknown-eabihf"}, Selected)); EXPECT_EQ("/thumb/v8.1-m.main/fp", Selected.back().gccSuffix()); ASSERT_TRUE(MS.select(TheDriver, {"--target=thumbv8.1m.main-none-unknown-eabihf", "-mfpu=none", "-march=thumbv8.1m.main+dsp+mve"}, Selected)); EXPECT_EQ("/thumb/v8.1-m.main/nofp/mve", Selected.back().gccSuffix()); }