//===- subzero/src/IceMangling.cpp - Cross test name mangling --*- C++ -*-===// // // The Subzero Code Generator // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief Defines utility functions for name mangling for cross tests. /// //===----------------------------------------------------------------------===// #include "IceDefs.h" #include "IceGlobalContext.h" #include "IceMangling.h" #include <cctype> // isdigit(), isupper() #include <locale> // locale namespace Ice { using ManglerVector = llvm::SmallVector<char, 32>; namespace { // Scan a string for S[0-9A-Z]*_ patterns and replace them with // S<num>_ where <num> is the next base-36 value. If a type name // legitimately contains that pattern, then the substitution will be // made in error and most likely the link will fail. In this case, // the test classes can be rewritten not to use that pattern, which is // much simpler and more reliable than implementing a full demangling // parser. Another substitution-in-error may occur if a type // identifier ends with the pattern S[0-9A-Z]*, because an immediately // following substitution string like "S1_" or "PS1_" may be combined // with the previous type. void incrementSubstitutions(ManglerVector &OldName) { const std::locale CLocale("C"); // Provide extra space in case the length of <num> increases. ManglerVector NewName(OldName.size() * 2); size_t OldPos = 0; size_t NewPos = 0; const size_t OldLen = OldName.size(); for (; OldPos < OldLen; ++OldPos, ++NewPos) { if (OldName[OldPos] == '\0') break; if (OldName[OldPos] == 'S') { // Search forward until we find _ or invalid character (including \0). bool AllZs = true; bool Found = false; size_t Last; for (Last = OldPos + 1; Last < OldLen; ++Last) { char Ch = OldName[Last]; if (Ch == '_') { Found = true; break; } else if (std::isdigit(Ch) || std::isupper(Ch, CLocale)) { if (Ch != 'Z') AllZs = false; } else { // Invalid character, stop searching. break; } } if (Found) { NewName[NewPos++] = OldName[OldPos++]; // 'S' size_t Length = Last - OldPos; // NewPos and OldPos point just past the 'S'. assert(NewName[NewPos - 1] == 'S'); assert(OldName[OldPos - 1] == 'S'); assert(OldName[OldPos + Length] == '_'); if (AllZs) { // Replace N 'Z' characters with a '0' (if N=0) or '1' (if N>0) // followed by N '0' characters. NewName[NewPos++] = (Length ? '1' : '0'); for (size_t i = 0; i < Length; ++i) { NewName[NewPos++] = '0'; } } else { // Iterate right-to-left and increment the base-36 number. bool Carry = true; for (size_t i = 0; i < Length; ++i) { size_t Offset = Length - 1 - i; char Ch = OldName[OldPos + Offset]; if (Carry) { Carry = false; switch (Ch) { case '9': Ch = 'A'; break; case 'Z': Ch = '0'; Carry = true; break; default: ++Ch; break; } } NewName[NewPos + Offset] = Ch; } NewPos += Length; } OldPos = Last; // Fall through and let the '_' be copied across. } } NewName[NewPos] = OldName[OldPos]; } assert(NewName[NewPos] == '\0'); OldName = NewName; } } // end of anonymous namespace // In this context, name mangling means to rewrite a symbol using a given // prefix. For a C++ symbol, nest the original symbol inside the "prefix" // namespace. For other symbols, just prepend the prefix. std::string mangleName(const std::string &Name) { // An already-nested name like foo::bar() gets pushed down one level, making // it equivalent to Prefix::foo::bar(). // _ZN3foo3barExyz ==> _ZN6Prefix3foo3barExyz // A non-nested but mangled name like bar() gets nested, making it equivalent // to Prefix::bar(). // _Z3barxyz ==> ZN6Prefix3barExyz // An unmangled, extern "C" style name, gets a simple prefix: // bar ==> Prefixbar if (!BuildDefs::dump() || getFlags().getTestPrefix().empty()) return Name; const std::string TestPrefix = getFlags().getTestPrefix(); unsigned PrefixLength = TestPrefix.length(); ManglerVector NameBase(1 + Name.length()); const size_t BufLen = 30 + Name.length() + PrefixLength; ManglerVector NewName(BufLen); uint32_t BaseLength = 0; // using uint32_t due to sscanf format string int ItemsParsed = sscanf(Name.c_str(), "_ZN%s", NameBase.data()); if (ItemsParsed == 1) { // Transform _ZN3foo3barExyz ==> _ZN6Prefix3foo3barExyz // (splice in "6Prefix") ^^^^^^^ snprintf(NewName.data(), BufLen, "_ZN%u%s%s", PrefixLength, TestPrefix.c_str(), NameBase.data()); // We ignore the snprintf return value (here and below). If we somehow // miscalculated the output buffer length, the output will be truncated, // but it will be truncated consistently for all mangleName() calls on the // same input string. incrementSubstitutions(NewName); return NewName.data(); } // Artificially limit BaseLength to 9 digits (less than 1 billion) because // sscanf behavior is undefined on integer overflow. If there are more than 9 // digits (which we test by looking at the beginning of NameBase), then we // consider this a failure to parse a namespace mangling, and fall back to // the simple prefixing. ItemsParsed = sscanf(Name.c_str(), "_Z%9u%s", &BaseLength, NameBase.data()); if (ItemsParsed == 2 && BaseLength <= strlen(NameBase.data()) && !isdigit(NameBase[0])) { // Transform _Z3barxyz ==> _ZN6Prefix3barExyz // ^^^^^^^^ ^ // (splice in "N6Prefix", and insert "E" after "3bar") But an "I" after the // identifier indicates a template argument list terminated with "E"; // insert the new "E" before/after the old "E". E.g.: // Transform _Z3barIabcExyz ==> _ZN6Prefix3barIabcEExyz // ^^^^^^^^ ^ // (splice in "N6Prefix", and insert "E" after "3barIabcE") ManglerVector OrigName(Name.length()); ManglerVector OrigSuffix(Name.length()); uint32_t ActualBaseLength = BaseLength; if (NameBase[ActualBaseLength] == 'I') { ++ActualBaseLength; while (NameBase[ActualBaseLength] != 'E' && NameBase[ActualBaseLength] != '\0') ++ActualBaseLength; } strncpy(OrigName.data(), NameBase.data(), ActualBaseLength); OrigName[ActualBaseLength] = '\0'; strcpy(OrigSuffix.data(), NameBase.data() + ActualBaseLength); snprintf(NewName.data(), BufLen, "_ZN%u%s%u%sE%s", PrefixLength, TestPrefix.c_str(), BaseLength, OrigName.data(), OrigSuffix.data()); incrementSubstitutions(NewName); return NewName.data(); } // Transform bar ==> Prefixbar // ^^^^^^ return TestPrefix + Name; } } // end of namespace Ice