1 //===-------------- ItaniumManglingCanonicalizerTest.cpp ------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "llvm/ProfileData/ItaniumManglingCanonicalizer.h"
10 #include "llvm/ADT/ArrayRef.h"
11 #include "llvm/ADT/StringRef.h"
12 #include "gtest/gtest.h"
22 using EquivalenceError
= llvm::ItaniumManglingCanonicalizer::EquivalenceError
;
23 using FragmentKind
= llvm::ItaniumManglingCanonicalizer::FragmentKind
;
27 llvm::StringRef First
;
28 llvm::StringRef Second
;
31 // A set of manglings that should all be considered equivalent.
32 using EquivalenceClass
= std::vector
<llvm::StringRef
>;
35 // A set of equivalences to register.
36 std::vector
<Equivalence
> Equivalences
;
37 // A set of distinct equivalence classes created by registering the
39 std::vector
<EquivalenceClass
> Classes
;
42 // A function that returns a set of test cases.
43 static std::vector
<Testcase
> getTestcases() {
45 // Three different manglings for std::string (old libstdc++, new libstdc++,
49 {FragmentKind::Type
, "Ss",
50 "NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE"},
51 {FragmentKind::Type
, "Ss",
52 "NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},
57 "_Z1fNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE",
58 "_Z1fNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},
60 "_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE4sizeEv",
61 "_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4sizeEv"},
65 // Check that substitutions are properly handled.
68 // ::X <-> ::N::X<int>
69 {FragmentKind::Type
, "1X", "N1N1XIiEE"},
70 // ::T<T<int, int>, T<int, int>> <-> T<int>
71 {FragmentKind::Type
, "1TIS_IiiES0_E", "1TIiE"},
72 // A::B::foo <-> AB::foo
73 {FragmentKind::Name
, "N1A1B3fooE", "N2AB3fooE"},
76 {"_Z1f1XPS_RS_", "_Z1fN1N1XIiEEPS1_RS1_"},
77 {"_ZN1A1B3fooE1TIS1_IiiES2_EPS3_RS3_", "_ZN2AB3fooE1TIiEPS1_RS1_"},
81 // Check that nested equivalences are properly handled.
84 // std::__1::char_traits == std::__cxx11::char_traits
85 // (Note that this is unused and should make no difference,
86 // but it should not cause us to fail to match up the cases
89 "NSt3__111char_traitsE",
90 "NSt7__cxx1111char_traitsE"},
91 // std::__1::allocator == std::allocator
94 "Sa"}, // "Sa" is not strictly a <name> but we accept it as one.
95 // std::__1::vector == std::vector
99 // std::__1::basic_string<
101 // std::__1::char_traits<char>,
102 // std::__1::allocator<char>> ==
103 // std::__cxx11::basic_string<
105 // std::char_traits<char>,
106 // std::allocator<char>>
108 "NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE",
109 "NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},
111 {FragmentKind::Type
, "1XI1AE", "1XI1BE"},
113 {FragmentKind::Name
, "1X", "1Y"},
117 {"_Z1fNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE",
118 "_Z1fNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},
119 // f(std::vector<int>)
120 {"_Z1fSt6vectorIiSaIiEE", "_Z1fNSt3__16vectorIiNS_9allocatorIiEEEE"},
121 // f(X<A>), f(X<B>), f(Y<A>), f(Y<B>)
122 {"_Z1f1XI1AE", "_Z1f1XI1BE", "_Z1f1YI1AE", "_Z1f1YI1BE"},
124 {"_Z1f1XI1CE", "_Z1f1YI1CE"},
128 // Check namespace equivalences.
131 // std::__1 == std::__cxx11
132 {FragmentKind::Name
, "St3__1", "St7__cxx11"},
133 // std::__1::allocator == std::allocator
134 {FragmentKind::Name
, "NSt3__19allocatorE", "Sa"},
135 // std::vector == std::__1::vector
136 {FragmentKind::Name
, "St6vector", "NSt3__16vectorE"},
137 // std::__cxx11::char_traits == std::char_traits
138 // (This indirectly means that std::__1::char_traits == std::char_traits,
139 // due to the std::__cxx11 == std::__1 equivalence, which is what we rely
141 {FragmentKind::Name
, "NSt7__cxx1111char_traitsE", "St11char_traits"},
145 {"_Z1fNSt7__cxx113fooE",
148 {"_Z1fNSt7__cxx1111char_traitsIcEE",
149 "_Z1fNSt3__111char_traitsIcEE",
150 "_Z1fSt11char_traitsIcE"},
152 {"_Z1fNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE",
153 "_Z1fNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},
154 // f(std::vector<int>)
155 {"_Z1fSt6vectorIiSaIiEE", "_Z1fNSt3__16vectorIiNS_9allocatorIiEEEE"},
159 // Check namespace equivalences for namespace 'std'. We support using 'St'
160 // for this, despite it not technically being a <name>.
164 {FragmentKind::Name
, "St3__1", "St"},
165 // std::__1 == std::__cxx11
166 {FragmentKind::Name
, "St3__1", "St7__cxx11"},
167 // FIXME: Should a 'std' equivalence also cover the predefined
169 // std::__1::allocator == std::allocator
170 {FragmentKind::Name
, "NSt3__19allocatorE", "Sa"},
173 {"_Z1fSt3foo", "_Z1fNSt3__13fooE", "_Z1fNSt7__cxx113fooE"},
174 {"_Z1fNSt3bar3bazE", "_Z1fNSt3__13bar3bazE"},
176 {"_Z1fNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE",
177 "_Z1fNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},
178 // f(std::vector<int>)
179 {"_Z1fSt6vectorIiSaIiEE", "_Z1fNSt3__16vectorIiNS_9allocatorIiEEEE"},
183 // Check mutually-recursive equivalences.
186 {FragmentKind::Type
, "1A", "1B"},
187 {FragmentKind::Type
, "1A", "1C"},
188 {FragmentKind::Type
, "1D", "1B"},
189 {FragmentKind::Type
, "1C", "1E"},
192 {"_Z1f1A", "_Z1f1B", "_Z1f1C", "_Z1f1D", "_Z1f1E"},
197 // Check <encoding>s.
200 {FragmentKind::Encoding
, "1fv", "1gv"},
203 // f(void) -> g(void)
205 // static local 'n' in f(void) -> static local 'n' in g(void)
206 {"_ZZ1fvE1n", "_ZZ1gvE1n"},
210 // Corner case: the substitution can appear within its own expansion.
214 {FragmentKind::Type
, "1X", "1YI1XE"},
216 {FragmentKind::Type
, "1AI1BE", "1B"},
219 // f(X) == f(Y<X>) == f(Y<Y<X>>) == f(Y<Y<Y<X>>>)
220 {"_Z1f1X", "_Z1f1YI1XE", "_Z1f1YIS_I1XEE", "_Z1f1YIS_IS_I1XEEE"},
221 // f(B) == f(A<B>) == f(A<A<B>>) == f(A<A<A<B>>>)
222 {"_Z1f1B", "_Z1f1AI1BE", "_Z1f1AIS_I1BEE", "_Z1f1AIS_IS_I1BEEE"},
226 // Redundant equivalences are accepted (and have no effect).
229 {FragmentKind::Name
, "3std", "St"},
230 {FragmentKind::Name
, "1X", "1Y"},
231 {FragmentKind::Name
, "N1X1ZE", "N1Y1ZE"},
236 // Check that ctor and dtor variants are considered distinct.
239 {{"_ZN1XC1Ev"}, {"_ZN1XC2Ev"}, {"_ZN1XD1Ev"}, {"_ZN1XD2Ev"}}
242 // Ensure array types with and without bounds are handled properly.
245 {FragmentKind::Type
, "A_i", "A1_f"},
248 {"_Z1fRA_i", "_Z1fRA_i", "_Z1fRA1_f"},
249 {"_Z1fRA1_i"}, {"_Z1fRA_f"},
253 // Unmangled names can be remapped as complete encodings.
256 {FragmentKind::Encoding
, "3foo", "3bar"},
261 // void f<foo>() == void f<bar>()
262 {"_Z1fIL_Z3fooEEvv", "_Z1fIL_Z3barEEvv"},
268 // A function to get a set of test cases for forward template references.
269 static std::vector
<Testcase
> getForwardTemplateReferenceTestcases() {
271 // ForwardTemplateReference does not support canonicalization.
272 // FIXME: We should consider ways of fixing this, perhaps by eliminating
273 // the ForwardTemplateReference node with a tree transformation.
276 // X::operator T() <with T = A> == Y::operator T() <with T = A>
277 {FragmentKind::Encoding
, "N1XcvT_I1AEEv", "N1YcvT_I1AEEv"},
279 {FragmentKind::Name
, "1A", "1B"},
282 // All combinations result in unique equivalence classes.
287 // Even giving the same string twice gives a new class.
294 template<bool CanonicalizeFirst
>
295 static void testTestcases(ArrayRef
<Testcase
> Testcases
) {
296 for (const auto &Testcase
: Testcases
) {
297 llvm::ItaniumManglingCanonicalizer Canonicalizer
;
298 for (const auto &Equiv
: Testcase
.Equivalences
) {
300 Canonicalizer
.addEquivalence(Equiv
.Kind
, Equiv
.First
, Equiv
.Second
);
301 EXPECT_EQ(Result
, EquivalenceError::Success
)
302 << "couldn't add equivalence between " << Equiv
.First
<< " and "
306 using CanonKey
= llvm::ItaniumManglingCanonicalizer::Key
;
308 std::map
<const EquivalenceClass
*, CanonKey
> Keys
;
309 if (CanonicalizeFirst
)
310 for (const auto &Class
: Testcase
.Classes
)
311 Keys
.insert({&Class
, Canonicalizer
.canonicalize(*Class
.begin())});
313 std::map
<CanonKey
, llvm::StringRef
> Found
;
314 for (const auto &Class
: Testcase
.Classes
) {
315 CanonKey ClassKey
= Keys
[&Class
];
316 for (llvm::StringRef Str
: Class
) {
317 // Force a copy to be made when calling lookup to test that it doesn't
318 // retain any part of the provided string.
319 CanonKey ThisKey
= CanonicalizeFirst
320 ? Canonicalizer
.lookup(std::string(Str
))
321 : Canonicalizer
.canonicalize(Str
);
322 EXPECT_NE(ThisKey
, CanonKey()) << "couldn't canonicalize " << Str
;
324 EXPECT_EQ(ThisKey
, ClassKey
)
325 << Str
<< " not in the same class as " << *Class
.begin();
330 EXPECT_TRUE(Found
.insert({ClassKey
, *Class
.begin()}).second
)
331 << *Class
.begin() << " is in the same class as " << Found
[ClassKey
];
336 TEST(ItaniumManglingCanonicalizerTest
, TestCanonicalize
) {
337 testTestcases
<false>(getTestcases());
340 TEST(ItaniumManglingCanonicalizerTest
, TestLookup
) {
341 testTestcases
<true>(getTestcases());
344 TEST(ItaniumManglingCanonicalizerTest
, TestForwardTemplateReference
) {
345 // lookup(...) after canonicalization (intentionally) returns different
346 // values for this testcase.
347 testTestcases
<false>(getForwardTemplateReferenceTestcases());
351 TEST(ItaniumManglingCanonicalizerTest
, TestInvalidManglings
) {
352 llvm::ItaniumManglingCanonicalizer Canonicalizer
;
353 EXPECT_EQ(Canonicalizer
.addEquivalence(FragmentKind::Type
, "", "1X"),
354 EquivalenceError::InvalidFirstMangling
);
355 EXPECT_EQ(Canonicalizer
.addEquivalence(FragmentKind::Type
, "1X", "1ab"),
356 EquivalenceError::InvalidSecondMangling
);
357 EXPECT_EQ(Canonicalizer
.canonicalize("_Z3fooE"),
358 llvm::ItaniumManglingCanonicalizer::Key());
359 EXPECT_EQ(Canonicalizer
.canonicalize("_Zfoo"),
360 llvm::ItaniumManglingCanonicalizer::Key());
362 // A reference to a template parameter ('T_' etc) cannot appear in a <name>,
363 // because we don't have template arguments to bind to it. (The arguments in
364 // an 'I ... E' construct in the <name> aren't registered as
365 // backreferenceable arguments in this sense, because they're not part of
366 // the template argument list of an <encoding>.
367 EXPECT_EQ(Canonicalizer
.addEquivalence(FragmentKind::Name
, "N1XcvT_I1AEE",
369 EquivalenceError::InvalidFirstMangling
);
372 TEST(ItaniumManglingCanonicalizerTest
, TestBadEquivalenceOrder
) {
373 llvm::ItaniumManglingCanonicalizer Canonicalizer
;
374 EXPECT_EQ(Canonicalizer
.addEquivalence(FragmentKind::Type
, "N1P1XE", "N1Q1XE"),
375 EquivalenceError::Success
);
376 EXPECT_EQ(Canonicalizer
.addEquivalence(FragmentKind::Type
, "1P", "1Q"),
377 EquivalenceError::ManglingAlreadyUsed
);
379 EXPECT_EQ(Canonicalizer
.addEquivalence(FragmentKind::Type
, "N1C1XE", "N1A1YE"),
380 EquivalenceError::Success
);
381 EXPECT_EQ(Canonicalizer
.addEquivalence(FragmentKind::Type
, "1A", "1B"),
382 EquivalenceError::Success
);
383 EXPECT_EQ(Canonicalizer
.addEquivalence(FragmentKind::Type
, "1C", "1D"),
384 EquivalenceError::Success
);
385 EXPECT_EQ(Canonicalizer
.addEquivalence(FragmentKind::Type
, "1B", "1D"),
386 EquivalenceError::ManglingAlreadyUsed
);
389 } // end anonymous namespace