Index: include/llvm/ADT/StringRef.h =================================================================== --- include/llvm/ADT/StringRef.h +++ include/llvm/ADT/StringRef.h @@ -855,6 +855,10 @@ /// constexpr StringLiteral S("test"); /// class StringLiteral : public StringRef { + private: + constexpr StringLiteral(const char *Str, size_t N) : StringRef(Str, N) { + } + public: template constexpr StringLiteral(const char (&Str)[N]) @@ -867,6 +871,12 @@ #endif : StringRef(Str, N - 1) { } + + // HACK -- The Google unit tests need/want strings like "foo\0bar" to work. + template + static constexpr StringLiteral withInnerNUL(const char (&Str)[N]) { + return StringLiteral(Str, N - 1); + } }; /// @name StringRef Comparison Operators Index: include/llvm/ADT/StringSwitch.h =================================================================== --- include/llvm/ADT/StringSwitch.h +++ include/llvm/ADT/StringSwitch.h @@ -6,8 +6,8 @@ // License. See LICENSE.TXT for details. //===----------------------------------------------------------------------===/ // -// This file implements the StringSwitch template, which mimics a switch() -// statement whose cases are string literals. +// This file implements the StringSwitch template and STRING_SWITCH macros, +// which mimics a switch() statement whose cases are string literals. // //===----------------------------------------------------------------------===/ #ifndef LLVM_ADT_STRINGSWITCH_H @@ -46,12 +46,12 @@ /// \brief The pointer to the result of this switch statement, once known, /// null before that. - const T *Result; + Optional Result; public: LLVM_ATTRIBUTE_ALWAYS_INLINE explicit StringSwitch(StringRef S) - : Str(S), Result(nullptr) { } + : Str(S), Result() { } // StringSwitch is not copyable. StringSwitch(const StringSwitch &) = delete; @@ -61,187 +61,154 @@ *this = std::move(other); } StringSwitch &operator=(StringSwitch &&other) { - Str = other.Str; - Result = other.Result; + Str = std::move(other.Str); + Result = std::move(other.Result); return *this; } ~StringSwitch() = default; // Case-sensitive case matchers - template LLVM_ATTRIBUTE_ALWAYS_INLINE - StringSwitch& Case(const char (&S)[N], const T& Value) { - assert(N); - if (!Result && N-1 == Str.size() && - (N == 1 || std::memcmp(S, Str.data(), N-1) == 0)) { - Result = &Value; + StringSwitch &Case(StringLiteral S, T Value) { + if (!Result && Str == S) { + Result = std::move(Value); } return *this; } template LLVM_ATTRIBUTE_ALWAYS_INLINE - StringSwitch& EndsWith(const char (&S)[N], const T &Value) { + StringSwitch& EndsWith(const char (&S)[N], T Value) { assert(N); if (!Result && Str.size() >= N-1 && (N == 1 || std::memcmp(S, Str.data() + Str.size() + 1 - N, N-1) == 0)) { - Result = &Value; + Result = std::move(Value); } return *this; } template LLVM_ATTRIBUTE_ALWAYS_INLINE - StringSwitch& StartsWith(const char (&S)[N], const T &Value) { + StringSwitch& StartsWith(const char (&S)[N], T Value) { assert(N); if (!Result && Str.size() >= N-1 && (N == 1 || std::memcmp(S, Str.data(), N-1) == 0)) { - Result = &Value; + Result = std::move(Value); } return *this; } - template LLVM_ATTRIBUTE_ALWAYS_INLINE - StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1], - const T& Value) { + StringSwitch &Cases(StringLiteral S0, StringLiteral S1, T Value) { return Case(S0, Value).Case(S1, Value); } - template LLVM_ATTRIBUTE_ALWAYS_INLINE - StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1], - const char (&S2)[N2], const T& Value) { + StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, + T Value) { return Case(S0, Value).Cases(S1, S2, Value); } - template LLVM_ATTRIBUTE_ALWAYS_INLINE - StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1], - const char (&S2)[N2], const char (&S3)[N3], - const T& Value) { + StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, + StringLiteral S3, T Value) { return Case(S0, Value).Cases(S1, S2, S3, Value); } - template LLVM_ATTRIBUTE_ALWAYS_INLINE - StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1], - const char (&S2)[N2], const char (&S3)[N3], - const char (&S4)[N4], const T& Value) { + StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, + StringLiteral S3, StringLiteral S4, T Value) { return Case(S0, Value).Cases(S1, S2, S3, S4, Value); } - template LLVM_ATTRIBUTE_ALWAYS_INLINE - StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1], - const char (&S2)[N2], const char (&S3)[N3], - const char (&S4)[N4], const char (&S5)[N5], - const T &Value) { + StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, + StringLiteral S3, StringLiteral S4, StringLiteral S5, + T Value) { return Case(S0, Value).Cases(S1, S2, S3, S4, S5, Value); } - template LLVM_ATTRIBUTE_ALWAYS_INLINE - StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1], - const char (&S2)[N2], const char (&S3)[N3], - const char (&S4)[N4], const char (&S5)[N5], - const char (&S6)[N6], const T &Value) { + StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, + StringLiteral S3, StringLiteral S4, StringLiteral S5, + StringLiteral S6, T Value) { return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, Value); } - template LLVM_ATTRIBUTE_ALWAYS_INLINE - StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1], - const char (&S2)[N2], const char (&S3)[N3], - const char (&S4)[N4], const char (&S5)[N5], - const char (&S6)[N6], const char (&S7)[N7], - const T &Value) { + StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, + StringLiteral S3, StringLiteral S4, StringLiteral S5, + StringLiteral S6, StringLiteral S7, T Value) { return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, S7, Value); } - template LLVM_ATTRIBUTE_ALWAYS_INLINE - StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1], - const char (&S2)[N2], const char (&S3)[N3], - const char (&S4)[N4], const char (&S5)[N5], - const char (&S6)[N6], const char (&S7)[N7], - const char (&S8)[N8], const T &Value) { + StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, + StringLiteral S3, StringLiteral S4, StringLiteral S5, + StringLiteral S6, StringLiteral S7, StringLiteral S8, + T Value) { return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, S7, S8, Value); } - template LLVM_ATTRIBUTE_ALWAYS_INLINE - StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1], - const char (&S2)[N2], const char (&S3)[N3], - const char (&S4)[N4], const char (&S5)[N5], - const char (&S6)[N6], const char (&S7)[N7], - const char (&S8)[N8], const char (&S9)[N9], - const T &Value) { + StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, + StringLiteral S3, StringLiteral S4, StringLiteral S5, + StringLiteral S6, StringLiteral S7, StringLiteral S8, + StringLiteral S9, T Value) { return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, S7, S8, S9, Value); } // Case-insensitive case matchers. - template - LLVM_ATTRIBUTE_ALWAYS_INLINE StringSwitch &CaseLower(const char (&S)[N], - const T &Value) { - if (!Result && Str.equals_lower(StringRef(S, N - 1))) - Result = &Value; + LLVM_ATTRIBUTE_ALWAYS_INLINE + StringSwitch &CaseLower(StringLiteral S, T Value) { + if (!Result && Str.equals_lower(S)) + Result = std::move(Value); return *this; } - template - LLVM_ATTRIBUTE_ALWAYS_INLINE StringSwitch &EndsWithLower(const char (&S)[N], - const T &Value) { - if (!Result && Str.endswith_lower(StringRef(S, N - 1))) - Result = &Value; + LLVM_ATTRIBUTE_ALWAYS_INLINE + StringSwitch &EndsWithLower(StringLiteral S, T Value) { + if (!Result && Str.endswith_lower(S)) + Result = Value; return *this; } - template - LLVM_ATTRIBUTE_ALWAYS_INLINE StringSwitch &StartsWithLower(const char (&S)[N], - const T &Value) { - if (!Result && Str.startswith_lower(StringRef(S, N - 1))) - Result = &Value; + LLVM_ATTRIBUTE_ALWAYS_INLINE + StringSwitch &StartsWithLower(StringLiteral S, T Value) { + if (!Result && Str.startswith_lower(S)) + Result = std::move(Value); return *this; } - template - LLVM_ATTRIBUTE_ALWAYS_INLINE StringSwitch & - CasesLower(const char (&S0)[N0], const char (&S1)[N1], const T &Value) { + + LLVM_ATTRIBUTE_ALWAYS_INLINE + StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, T Value) { return CaseLower(S0, Value).CaseLower(S1, Value); } - template - LLVM_ATTRIBUTE_ALWAYS_INLINE StringSwitch & - CasesLower(const char (&S0)[N0], const char (&S1)[N1], const char (&S2)[N2], - const T &Value) { + LLVM_ATTRIBUTE_ALWAYS_INLINE + StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2, + T Value) { return CaseLower(S0, Value).CasesLower(S1, S2, Value); } - template - LLVM_ATTRIBUTE_ALWAYS_INLINE StringSwitch & - CasesLower(const char (&S0)[N0], const char (&S1)[N1], const char (&S2)[N2], - const char (&S3)[N3], const T &Value) { + LLVM_ATTRIBUTE_ALWAYS_INLINE + StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2, + StringLiteral S3, T Value) { return CaseLower(S0, Value).CasesLower(S1, S2, S3, Value); } - template - LLVM_ATTRIBUTE_ALWAYS_INLINE StringSwitch & - CasesLower(const char (&S0)[N0], const char (&S1)[N1], const char (&S2)[N2], - const char (&S3)[N3], const char (&S4)[N4], const T &Value) { + LLVM_ATTRIBUTE_ALWAYS_INLINE + StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2, + StringLiteral S3, StringLiteral S4, T Value) { return CaseLower(S0, Value).CasesLower(S1, S2, S3, S4, Value); } LLVM_ATTRIBUTE_ALWAYS_INLINE - R Default(const T &Value) const { + R Default(T Value) const { if (Result) return *Result; return Value; @@ -254,6 +221,121 @@ } }; +/// \brief A switch()-like macro system whose cases are string literals. +/// +/// The STRING_SWITCH macros are a simple form of a switch() statement that +/// determines whether the given string matches one of the given string +/// literals. The macros are designed for reasonable to great code gen +/// depending on the variability of the case string lengths. +/// +/// \code +/// Color color = STRING_SWITCH(Color, String) +/// #define COLOR_VALUE(Ret, Str, ...) \ +/// STRING_SWITCH_CASE(Str, Ret) +/// #include +/// STRING_SWITCH_DEFAULT(UnknownColor); +/// \endcode + +// This macro must be synchronized with switch statement below. +#define MAX_STRING_SWITCH_CASE_LEN 64 + +#define STRING_SWITCH(RetTy, Str) [&]() -> RetTy { \ + auto StrPtr = Str.data(); \ + auto StrSz = Str.size(); \ + auto helper = [&](unsigned ConstSz) -> RetTy { + +#define STRING_SWITCH_CASE(Str, RetVal) \ + static_assert(__builtin_strlen(Str) > 0, "Not a string literal?"); \ + static_assert(__builtin_strlen(Str) <= MAX_STRING_SWITCH_CASE_LEN, \ + "MacroStringSwitch.h needs updating"); \ + if (ConstSz == __builtin_strlen(Str) && \ + memcmp(StrPtr, Str, __builtin_strlen(Str)) == 0) \ + return RetVal; + +// This switch statement must be synchronized with MAX_STRING_SWITCH_CASE_LEN +#define STRING_SWITCH_SWITCH() \ + }; \ + switch (StrSz) { \ + case 64: return helper(64); \ + case 63: return helper(63); \ + case 62: return helper(62); \ + case 61: return helper(61); \ + case 60: return helper(60); \ + case 59: return helper(59); \ + case 58: return helper(58); \ + case 57: return helper(57); \ + case 56: return helper(56); \ + case 55: return helper(55); \ + case 54: return helper(54); \ + case 53: return helper(53); \ + case 52: return helper(52); \ + case 51: return helper(51); \ + case 50: return helper(50); \ + case 49: return helper(49); \ + case 48: return helper(48); \ + case 47: return helper(47); \ + case 46: return helper(46); \ + case 45: return helper(45); \ + case 44: return helper(44); \ + case 43: return helper(43); \ + case 42: return helper(42); \ + case 41: return helper(41); \ + case 40: return helper(40); \ + case 39: return helper(39); \ + case 38: return helper(38); \ + case 37: return helper(37); \ + case 36: return helper(36); \ + case 35: return helper(35); \ + case 34: return helper(34); \ + case 33: return helper(33); \ + case 32: return helper(32); \ + case 31: return helper(31); \ + case 30: return helper(30); \ + case 29: return helper(29); \ + case 28: return helper(28); \ + case 27: return helper(27); \ + case 26: return helper(26); \ + case 25: return helper(25); \ + case 24: return helper(24); \ + case 23: return helper(23); \ + case 22: return helper(22); \ + case 21: return helper(21); \ + case 20: return helper(20); \ + case 19: return helper(19); \ + case 18: return helper(18); \ + case 17: return helper(17); \ + case 16: return helper(16); \ + case 15: return helper(15); \ + case 14: return helper(14); \ + case 13: return helper(13); \ + case 12: return helper(12); \ + case 11: return helper(11); \ + case 10: return helper(10); \ + case 9: return helper(9); \ + case 8: return helper(8); \ + case 7: return helper(7); \ + case 6: return helper(6); \ + case 5: return helper(5); \ + case 4: return helper(4); \ + case 3: return helper(3); \ + case 2: return helper(2); \ + case 1: return helper(1); + +#define STRING_SWITCH_DEFAULT(DefVal) \ + return DefVal; \ + STRING_SWITCH_SWITCH() \ + default: return DefVal; \ + } \ + }() + +#define STRING_SWITCH_UNREACHABLE(Msg) \ + llvm_unreachable(Msg); \ + STRING_SWITCH_SWITCH() \ + default: llvm_unreachable(Msg); \ + } \ + }() + + } // end namespace llvm #endif // LLVM_ADT_STRINGSWITCH_H Index: lib/BinaryFormat/Dwarf.cpp =================================================================== --- lib/BinaryFormat/Dwarf.cpp +++ lib/BinaryFormat/Dwarf.cpp @@ -30,11 +30,11 @@ } unsigned llvm::dwarf::getTag(StringRef TagString) { - return StringSwitch(TagString) + return STRING_SWITCH(unsigned, TagString) #define HANDLE_DW_TAG(ID, NAME, VERSION, VENDOR) \ - .Case("DW_TAG_" #NAME, DW_TAG_##NAME) + STRING_SWITCH_CASE("DW_TAG_" #NAME, DW_TAG_##NAME) #include "llvm/BinaryFormat/Dwarf.def" - .Default(DW_TAG_invalid); + STRING_SWITCH_DEFAULT(DW_TAG_invalid); } unsigned llvm::dwarf::TagVersion(dwarf::Tag Tag) { @@ -149,12 +149,12 @@ } unsigned llvm::dwarf::getOperationEncoding(StringRef OperationEncodingString) { - return StringSwitch(OperationEncodingString) + return STRING_SWITCH(unsigned, OperationEncodingString) #define HANDLE_DW_OP(ID, NAME, VERSION, VENDOR) \ - .Case("DW_OP_" #NAME, DW_OP_##NAME) + STRING_SWITCH_CASE("DW_OP_" #NAME, DW_OP_##NAME) #include "llvm/BinaryFormat/Dwarf.def" - .Case("DW_OP_LLVM_fragment", DW_OP_LLVM_fragment) - .Default(0); + STRING_SWITCH_CASE("DW_OP_LLVM_fragment", DW_OP_LLVM_fragment) + STRING_SWITCH_DEFAULT(0); } unsigned llvm::dwarf::OperationVersion(dwarf::LocationAtom Op) { @@ -191,11 +191,11 @@ } unsigned llvm::dwarf::getAttributeEncoding(StringRef EncodingString) { - return StringSwitch(EncodingString) + return STRING_SWITCH(unsigned, EncodingString) #define HANDLE_DW_ATE(ID, NAME, VERSION, VENDOR) \ - .Case("DW_ATE_" #NAME, DW_ATE_##NAME) + STRING_SWITCH_CASE("DW_ATE_" #NAME, DW_ATE_##NAME) #include "llvm/BinaryFormat/Dwarf.def" - .Default(0); + STRING_SWITCH_DEFAULT(0); } unsigned llvm::dwarf::AttributeEncodingVersion(dwarf::TypeKind ATE) { @@ -289,11 +289,11 @@ } unsigned llvm::dwarf::getVirtuality(StringRef VirtualityString) { - return StringSwitch(VirtualityString) + return STRING_SWITCH(unsigned, VirtualityString) #define HANDLE_DW_VIRTUALITY(ID, NAME) \ - .Case("DW_VIRTUALITY_" #NAME, DW_VIRTUALITY_##NAME) + STRING_SWITCH_CASE("DW_VIRTUALITY_" #NAME, DW_VIRTUALITY_##NAME) #include "llvm/BinaryFormat/Dwarf.def" - .Default(DW_VIRTUALITY_invalid); + STRING_SWITCH_DEFAULT(DW_VIRTUALITY_invalid); } StringRef llvm::dwarf::LanguageString(unsigned Language) { @@ -308,11 +308,11 @@ } unsigned llvm::dwarf::getLanguage(StringRef LanguageString) { - return StringSwitch(LanguageString) + return STRING_SWITCH(unsigned, LanguageString) #define HANDLE_DW_LANG(ID, NAME, VERSION, VENDOR) \ - .Case("DW_LANG_" #NAME, DW_LANG_##NAME) + STRING_SWITCH_CASE("DW_LANG_" #NAME, DW_LANG_##NAME) #include "llvm/BinaryFormat/Dwarf.def" - .Default(0); + STRING_SWITCH_DEFAULT(0); } unsigned llvm::dwarf::LanguageVersion(dwarf::SourceLanguage Lang) { @@ -363,10 +363,10 @@ } unsigned llvm::dwarf::getCallingConvention(StringRef CCString) { - return StringSwitch(CCString) -#define HANDLE_DW_CC(ID, NAME) .Case("DW_CC_" #NAME, DW_CC_##NAME) + return STRING_SWITCH(unsigned, CCString) +#define HANDLE_DW_CC(ID, NAME) STRING_SWITCH_CASE("DW_CC_" #NAME, DW_CC_##NAME) #include "llvm/BinaryFormat/Dwarf.def" - .Default(0); + STRING_SWITCH_DEFAULT(0); } StringRef llvm::dwarf::InlineCodeString(unsigned Code) { Index: lib/Support/TargetParser.cpp =================================================================== --- lib/Support/TargetParser.cpp +++ lib/Support/TargetParser.cpp @@ -180,23 +180,23 @@ if (CPU == "generic") return ARCHNames[static_cast(AK)].DefaultFPU; - return StringSwitch(CPU) + return STRING_SWITCH(unsigned, CPU) #define ARM_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \ - .Case(NAME, DEFAULT_FPU) + STRING_SWITCH_CASE(NAME, DEFAULT_FPU) #include "llvm/Support/ARMTargetParser.def" - .Default(ARM::FK_INVALID); + STRING_SWITCH_DEFAULT(ARM::FK_INVALID); } unsigned llvm::ARM::getDefaultExtensions(StringRef CPU, ArchKind AK) { if (CPU == "generic") return ARCHNames[static_cast(AK)].ArchBaseExtensions; - return StringSwitch(CPU) + return STRING_SWITCH(unsigned, CPU) #define ARM_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \ - .Case(NAME, ARCHNames[static_cast(ARM::ArchKind::ID)]\ + STRING_SWITCH_CASE(NAME,ARCHNames[static_cast(ARM::ArchKind::ID)]\ .ArchBaseExtensions | DEFAULT_EXT) #include "llvm/Support/ARMTargetParser.def" - .Default(ARM::AEK_INVALID); + STRING_SWITCH_DEFAULT(ARM::AEK_INVALID); } bool llvm::ARM::getHWDivFeatures(unsigned HWDivKind, @@ -412,25 +412,25 @@ if (CPU == "generic") return AArch64ARCHNames[static_cast(AK)].DefaultFPU; - return StringSwitch(CPU) + return STRING_SWITCH(unsigned, CPU) #define AARCH64_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \ - .Case(NAME, DEFAULT_FPU) + STRING_SWITCH_CASE(NAME, DEFAULT_FPU) #include "llvm/Support/AArch64TargetParser.def" - .Default(ARM::FK_INVALID); + STRING_SWITCH_DEFAULT(ARM::FK_INVALID); } unsigned llvm::AArch64::getDefaultExtensions(StringRef CPU, ArchKind AK) { if (CPU == "generic") return AArch64ARCHNames[static_cast(AK)].ArchBaseExtensions; - return StringSwitch(CPU) + return STRING_SWITCH(unsigned, CPU) #define AARCH64_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \ - .Case(NAME, \ + STRING_SWITCH_CASE(NAME, \ AArch64ARCHNames[static_cast(AArch64::ArchKind::ID)] \ .ArchBaseExtensions | \ DEFAULT_EXT) #include "llvm/Support/AArch64TargetParser.def" - .Default(AArch64::AEK_INVALID); + STRING_SWITCH_DEFAULT(AArch64::AEK_INVALID); } bool llvm::AArch64::getExtensionFeatures(unsigned Extensions, Index: unittests/ADT/StringSwitchTest.cpp =================================================================== --- unittests/ADT/StringSwitchTest.cpp +++ unittests/ADT/StringSwitchTest.cpp @@ -158,7 +158,8 @@ auto Translate = [](StringRef S) { return llvm::StringSwitch(S) - .Cases("wind\0ws", "win32", "winnt", OSType::Windows) + .Cases(StringLiteral::withInnerNUL("wind\0ws"), "win32", "winnt", + OSType::Windows) .Cases("linux", "unix", "*nix", "posix", OSType::Linux) .Default(OSType::Unknown); }; @@ -184,7 +185,8 @@ auto Translate = [](StringRef S) { return llvm::StringSwitch(S) - .CasesLower("wind\0ws", "win32", "winnt", OSType::Windows) + .CasesLower(StringLiteral::withInnerNUL("wind\0ws"), "win32", "winnt", + OSType::Windows) .CasesLower("linux", "unix", "*nix", "posix", OSType::Linux) .Default(OSType::Unknown); };