| //===- 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 |