GCC Code Coverage Report


Directory: ../
File: src/irgenerator/NameMangling.cpp
Date: 2025-11-22 22:49:11
Coverage Exec Excl Total
Lines: 93.4% 156 2 169
Functions: 100.0% 13 0 13
Branches: 61.1% 154 8 260

Line Branch Exec Source
1 // Copyright (c) 2021-2025 ChilliBits. All rights reserved.
2
3 #include "NameMangling.h"
4
5 #include <exception/CompilerError.h>
6 #include <model/Function.h>
7 #include <model/Interface.h>
8 #include <model/Struct.h>
9 #include <symboltablebuilder/SymbolTableEntry.h>
10 #include <util/CommonUtil.h>
11
12 namespace spice::compiler {
13
14 /**
15 * Mangle a function or procedure.
16 * This should be mostly compatible with the C++ Itanium ABI name mangling scheme.
17 *
18 * @param spiceFunc Input function
19 * @return Mangled name
20 */
21 30515 std::string NameMangling::mangleFunction(const Function &spiceFunc) {
22 // Check if we have a predefined mangled name, specified by e.g. a function attribute
23
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 30515 times.
30515 if (!spiceFunc.predefinedMangledName.empty())
24 return spiceFunc.predefinedMangledName;
25
26 // Check if mangling is required
27
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 9 taken 30515 times.
30515 if (!spiceFunc.mangleFunctionName)
28 return spiceFunc.name;
29
30
1/2
✓ Branch 9 → 10 taken 30515 times.
✗ Branch 9 → 104 not taken.
30515 std::stringstream mangledName;
31
1/2
✓ Branch 10 → 11 taken 30515 times.
✗ Branch 10 → 102 not taken.
30515 mangledName << "_Z";
32
33 // This type
34
2/2
✓ Branch 14 → 15 taken 18509 times.
✓ Branch 14 → 17 taken 12006 times.
30515 if (spiceFunc.isMethod()) {
35
1/2
✓ Branch 15 → 16 taken 18509 times.
✗ Branch 15 → 102 not taken.
18509 mangledName << "N";
36
1/2
✓ Branch 16 → 17 taken 18509 times.
✗ Branch 16 → 102 not taken.
18509 mangleType(mangledName, spiceFunc.thisType);
37 }
38
39 // Function name
40
1/2
✓ Branch 17 → 18 taken 30515 times.
✗ Branch 17 → 102 not taken.
30515 const std::string name = spiceFunc.name + spiceFunc.mangleSuffix;
41
2/4
✓ Branch 19 → 20 taken 30515 times.
✗ Branch 19 → 100 not taken.
✓ Branch 20 → 21 taken 30515 times.
✗ Branch 20 → 100 not taken.
30515 mangledName << name.length() << name;
42
43 // Template types
44
1/2
✓ Branch 22 → 23 taken 30515 times.
✗ Branch 22 → 100 not taken.
30515 bool isSelfGeneric = !spiceFunc.templateTypes.empty();
45
2/2
✓ Branch 25 → 26 taken 18509 times.
✓ Branch 25 → 30 taken 12006 times.
30515 if (spiceFunc.isMethod())
46
1/2
✓ Branch 27 → 28 taken 18509 times.
✗ Branch 27 → 100 not taken.
18509 isSelfGeneric = spiceFunc.templateTypes.size() > spiceFunc.thisType.getTemplateTypes().size();
47
2/2
✓ Branch 30 → 31 taken 3628 times.
✓ Branch 30 → 57 taken 26887 times.
30515 if (isSelfGeneric) {
48
1/2
✓ Branch 31 → 32 taken 3628 times.
✗ Branch 31 → 100 not taken.
3628 mangledName << "I";
49 // Template types themselves
50
2/2
✓ Branch 44 → 34 taken 6196 times.
✓ Branch 44 → 45 taken 3628 times.
9824 for (const GenericType &genericTemplateType : spiceFunc.templateTypes) {
51
3/6
✓ Branch 35 → 36 taken 6196 times.
✗ Branch 35 → 93 not taken.
✓ Branch 36 → 37 taken 6196 times.
✗ Branch 36 → 93 not taken.
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 6196 times.
6196 assert(spiceFunc.typeMapping.contains(genericTemplateType.getSubType()));
52
2/4
✓ Branch 39 → 40 taken 6196 times.
✗ Branch 39 → 93 not taken.
✓ Branch 40 → 41 taken 6196 times.
✗ Branch 40 → 93 not taken.
6196 const QualType &actualType = spiceFunc.typeMapping.at(genericTemplateType.getSubType());
53
1/2
✓ Branch 41 → 42 taken 6196 times.
✗ Branch 41 → 93 not taken.
6196 mangleType(mangledName, actualType);
54 }
55
1/2
✓ Branch 45 → 46 taken 3628 times.
✗ Branch 45 → 100 not taken.
3628 mangledName << "E";
56
57 // Insert second end marker to end the nested type
58
2/2
✓ Branch 49 → 50 taken 549 times.
✓ Branch 49 → 51 taken 3079 times.
3628 if (spiceFunc.isMethod())
59
1/2
✓ Branch 50 → 51 taken 549 times.
✗ Branch 50 → 100 not taken.
549 mangledName << "E";
60
61 // Return type
62
2/2
✓ Branch 54 → 55 taken 1137 times.
✓ Branch 54 → 56 taken 2491 times.
3628 if (spiceFunc.isFunction())
63
1/2
✓ Branch 55 → 62 taken 1137 times.
✗ Branch 55 → 100 not taken.
1137 mangleType(mangledName, spiceFunc.returnType);
64 else
65
1/2
✓ Branch 56 → 62 taken 2491 times.
✗ Branch 56 → 100 not taken.
2491 mangledName << "v";
66
67
2/2
✓ Branch 60 → 61 taken 17960 times.
✓ Branch 60 → 62 taken 8927 times.
26887 } else if (spiceFunc.isMethod()) {
68
1/2
✓ Branch 61 → 62 taken 17960 times.
✗ Branch 61 → 100 not taken.
17960 mangledName << "E";
69 }
70
71 // Parameter types
72
2/2
✓ Branch 70 → 64 taken 33849 times.
✓ Branch 70 → 71 taken 30515 times.
64364 for (const auto &[qualType, isOptional] : spiceFunc.paramList) {
73
1/2
✗ Branch 65 → 66 not taken.
✓ Branch 65 → 67 taken 33849 times.
33849 assert(!isOptional);
74
1/2
✓ Branch 67 → 68 taken 33849 times.
✗ Branch 67 → 94 not taken.
33849 mangleType(mangledName, qualType);
75 }
76
2/2
✓ Branch 72 → 73 taken 9838 times.
✓ Branch 72 → 74 taken 20677 times.
30515 if (spiceFunc.paramList.empty())
77
1/2
✓ Branch 73 → 74 taken 9838 times.
✗ Branch 73 → 100 not taken.
9838 mangledName << "v";
78
79 #ifndef NDEBUG
80 30515 const TypeMapping &typeMapping = spiceFunc.typeMapping;
81
2/4
✓ Branch 74 → 75 taken 30515 times.
✗ Branch 74 → 96 not taken.
✓ Branch 75 → 76 taken 30515 times.
✗ Branch 75 → 95 not taken.
30515 const bool returnTypeIsFctOrProc = spiceFunc.returnType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE});
82
2/4
✓ Branch 2 → 3 taken 33816 times.
✗ Branch 2 → 8 not taken.
✓ Branch 3 → 4 taken 33816 times.
✗ Branch 3 → 7 not taken.
33816 const auto paramPredicate = [](const Param &p) { return p.qualType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE}); };
83
1/2
✓ Branch 76 → 77 taken 30515 times.
✗ Branch 76 → 100 not taken.
30515 const bool paramTypeIsFctOrProc = std::ranges::any_of(spiceFunc.paramList, paramPredicate);
84 12168 const auto templateTypePredicate = [&](const GenericType &t) {
85
4/8
✓ Branch 2 → 3 taken 12168 times.
✗ Branch 2 → 10 not taken.
✓ Branch 3 → 4 taken 12168 times.
✗ Branch 3 → 10 not taken.
✓ Branch 4 → 5 taken 12168 times.
✗ Branch 4 → 10 not taken.
✓ Branch 5 → 6 taken 12168 times.
✗ Branch 5 → 9 not taken.
12168 return typeMapping.at(t.getSubType()).getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE});
86 30515 };
87
1/2
✓ Branch 77 → 78 taken 30515 times.
✗ Branch 77 → 100 not taken.
30515 const bool templateTypeIsFctOrProc = std::ranges::any_of(spiceFunc.templateTypes, templateTypePredicate);
88
6/6
✓ Branch 78 → 79 taken 30513 times.
✓ Branch 78 → 87 taken 2 times.
✓ Branch 79 → 80 taken 30453 times.
✓ Branch 79 → 87 taken 60 times.
✓ Branch 80 → 81 taken 30432 times.
✓ Branch 80 → 87 taken 21 times.
30515 if (!returnTypeIsFctOrProc && !paramTypeIsFctOrProc && !templateTypeIsFctOrProc)
89
3/6
✓ Branch 81 → 82 taken 30432 times.
✗ Branch 81 → 99 not taken.
✓ Branch 82 → 83 taken 30432 times.
✗ Branch 82 → 97 not taken.
✗ Branch 83 → 84 not taken.
✓ Branch 83 → 85 taken 30432 times.
30432 assert(CommonUtil::isValidMangledName(mangledName.str()));
90 #endif
91
92
1/2
✓ Branch 87 → 88 taken 30515 times.
✗ Branch 87 → 100 not taken.
30515 return mangledName.str();
93 30515 }
94
95 /**
96 * Mangle a struct
97 * This should be mostly compatible with the C++ Itanium ABI name mangling scheme.
98 *
99 * @param spiceStruct Input struct
100 * @return Mangled name
101 */
102 1816 std::string NameMangling::mangleStruct(const Struct &spiceStruct) { return "struct." + spiceStruct.name; }
103
104 /**
105 * Mangle an interface
106 *
107 * @param spiceInterface Input interface
108 * @return Mangled name
109 */
110 256 std::string NameMangling::mangleInterface(const Interface &spiceInterface) { return "interface." + spiceInterface.name; }
111
112 /**
113 * Mangle a fully qualified name like e.g. test::s1::calledMethod to 4test2s112calledMethod
114 * This should be mostly compatible with the C++ Itanium ABI name mangling scheme.
115 *
116 * @param out Output string stream
117 * @param name Input name
118 * @param nestedType True if the name is a nested type
119 * @return Mangled name
120 */
121 31247 void NameMangling::mangleName(std::stringstream &out, const std::string &name, bool &nestedType) {
122 31247 std::vector<std::string> fragments;
123
1/2
✓ Branch 2 → 3 taken 31247 times.
✗ Branch 2 → 51 not taken.
31247 std::istringstream ss(name);
124 31247 std::string token;
125
126
4/6
✓ Branch 16 → 17 taken 62494 times.
✗ Branch 16 → 47 not taken.
✓ Branch 17 → 18 taken 62494 times.
✗ Branch 17 → 47 not taken.
✓ Branch 18 → 5 taken 31247 times.
✓ Branch 18 → 19 taken 31247 times.
62494 while (std::getline(ss, token, ':')) {
127
1/2
✓ Branch 5 → 6 taken 31247 times.
✗ Branch 5 → 42 not taken.
31247 std::istringstream subStream(token);
128 31247 std::string subToken;
129
4/6
✓ Branch 10 → 11 taken 62494 times.
✗ Branch 10 → 38 not taken.
✓ Branch 11 → 12 taken 62494 times.
✗ Branch 11 → 38 not taken.
✓ Branch 12 → 8 taken 31247 times.
✓ Branch 12 → 13 taken 31247 times.
62494 while (std::getline(subStream, subToken, '/')) {
130
1/2
✓ Branch 8 → 9 taken 31247 times.
✗ Branch 8 → 38 not taken.
31247 fragments.push_back(subToken);
131 }
132 31247 }
133
134 // Start a nested type if needed. The caller needs to emit the end marker.
135
1/2
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 23 taken 31247 times.
31247 if (fragments.size() > 1) {
136 out << "N";
137 nestedType = true;
138 }
139
140 // Process each fragment and append it to the result
141
2/2
✓ Branch 33 → 25 taken 31247 times.
✓ Branch 33 → 34 taken 31247 times.
62494 for (const std::string &fragment : fragments) {
142 31247 int fragmentLength = static_cast<int>(fragment.length());
143
2/4
✓ Branch 28 → 29 taken 31247 times.
✗ Branch 28 → 43 not taken.
✓ Branch 29 → 30 taken 31247 times.
✗ Branch 29 → 43 not taken.
31247 out << std::to_string(fragmentLength) << fragment;
144 }
145 31247 }
146
147 /**
148 * Mangle a symbol qualType
149 * This should be mostly compatible with the C++ Itanium ABI name mangling scheme.
150 *
151 * @param out Output string stream
152 * @param qualType Input symbol qualType
153 * @return Mangled name
154 */
155 74417 void NameMangling::mangleType(std::stringstream &out, QualType qualType) { // NOLINT(*-no-recursion)
156 74417 const Type *type = qualType.getType();
157
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 74417 times.
74417 assert(!qualType.hasAnyGenericParts());
158
159 // Unwrap qualType chain
160
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 74417 times.
74417 assert(!type->typeChain.empty());
161
2/2
✓ Branch 14 → 11 taken 8778 times.
✓ Branch 14 → 15 taken 74417 times.
83195 for (size_t i = type->typeChain.size() - 1; i >= 1; i--)
162 8778 mangleTypeChainElement(out, type->typeChain.at(i), false);
163
164 // Qualifiers
165
1/2
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 19 taken 74417 times.
74417 assert(qualType.getQualifiers().isSigned == !qualType.getQualifiers().isUnsigned);
166 74417 const bool signedness = qualType.getQualifiers().isSigned;
167
6/6
✓ Branch 21 → 22 taken 12809 times.
✓ Branch 21 → 25 taken 61608 times.
✓ Branch 23 → 24 taken 4822 times.
✓ Branch 23 → 25 taken 7987 times.
✓ Branch 26 → 27 taken 4822 times.
✓ Branch 26 → 28 taken 69595 times.
74417 if (qualType.getQualifiers().isConst && type->typeChain.size() > 1)
168 4822 out << "K";
169
170 // Base chain element
171 74417 mangleTypeChainElement(out, type->typeChain.front(), signedness);
172 74417 }
173
174 /**
175 * Mangle a type chain element
176 *
177 * @param out Output string stream
178 * @param chainElement Input type chain element
179 * @param signedness Signedness of the type
180 * @return Mangled name
181 */
182 83195 void NameMangling::mangleTypeChainElement(std::stringstream &out, const TypeChainElement &chainElement, bool signedness) {
183
14/16
✓ Branch 2 → 3 taken 2284 times.
✓ Branch 2 → 4 taken 120 times.
✓ Branch 2 → 10 taken 6374 times.
✓ Branch 2 → 12 taken 2325 times.
✓ Branch 2 → 16 taken 9035 times.
✓ Branch 2 → 21 taken 2653 times.
✓ Branch 2 → 26 taken 7587 times.
✓ Branch 2 → 31 taken 2313 times.
✓ Branch 2 → 36 taken 6392 times.
✓ Branch 2 → 38 taken 9792 times.
✓ Branch 2 → 40 taken 2947 times.
✓ Branch 2 → 44 taken 31247 times.
✗ Branch 2 → 60 not taken.
✓ Branch 2 → 65 taken 34 times.
✓ Branch 2 → 78 taken 92 times.
✗ Branch 2 → 90 not taken.
83195 switch (chainElement.superType) {
184 2284 case TY_PTR:
185 2284 out << "P";
186 2284 break;
187 120 case TY_ARRAY:
188
2/2
✓ Branch 4 → 5 taken 72 times.
✓ Branch 4 → 6 taken 48 times.
120 if (chainElement.data.arraySize == ARRAY_SIZE_UNKNOWN)
189 72 out << "P";
190 else
191 48 out << "A" << chainElement.data.arraySize << "_";
192 120 break;
193 6374 case TY_REF:
194 6374 out << "R";
195 6374 break;
196 2325 case TY_DOUBLE:
197
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 2325 times.
2325 assert(signedness && "Unsigned double types are forbidden");
198 2325 out << "d";
199 2325 break;
200 9035 case TY_INT:
201
2/2
✓ Branch 16 → 17 taken 8482 times.
✓ Branch 16 → 18 taken 553 times.
9035 out << (signedness ? "i" : "j");
202 9035 break;
203 2653 case TY_SHORT:
204
2/2
✓ Branch 21 → 22 taken 2616 times.
✓ Branch 21 → 23 taken 37 times.
2653 out << (signedness ? "s" : "t");
205 2653 break;
206 7587 case TY_LONG:
207
2/2
✓ Branch 26 → 27 taken 3880 times.
✓ Branch 26 → 28 taken 3707 times.
7587 out << (signedness ? "l" : "m");
208 7587 break;
209 2313 case TY_BYTE:
210
1/2
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 33 taken 2313 times.
2313 out << (signedness ? "a" : "h");
211 2313 break;
212 6392 case TY_CHAR:
213 6392 out << "c";
214 6392 break;
215 9792 case TY_STRING:
216 9792 out << "PKc";
217 9792 break;
218 2947 case TY_BOOL:
219
1/2
✗ Branch 40 → 41 not taken.
✓ Branch 40 → 42 taken 2947 times.
2947 assert(!signedness && "Signed bool types are forbidden");
220 2947 out << "b";
221 2947 break;
222 31247 case TY_STRUCT: // fall-through
223 case TY_INTERFACE: {
224 31247 bool nestedType = false;
225
1/2
✓ Branch 44 → 45 taken 31247 times.
✗ Branch 44 → 102 not taken.
31247 mangleName(out, chainElement.subType, nestedType);
226
2/2
✓ Branch 46 → 47 taken 10041 times.
✓ Branch 46 → 56 taken 21206 times.
31247 if (!chainElement.templateTypes.empty()) {
227
1/2
✓ Branch 47 → 48 taken 10041 times.
✗ Branch 47 → 102 not taken.
10041 out << "I";
228
2/2
✓ Branch 54 → 50 taken 12879 times.
✓ Branch 54 → 55 taken 10041 times.
22920 for (const QualType &templateType : chainElement.templateTypes)
229
1/2
✓ Branch 51 → 52 taken 12879 times.
✗ Branch 51 → 101 not taken.
12879 mangleType(out, templateType);
230
1/2
✓ Branch 55 → 56 taken 10041 times.
✗ Branch 55 → 102 not taken.
10041 out << "E";
231 }
232
1/2
✗ Branch 56 → 57 not taken.
✓ Branch 56 → 58 taken 31247 times.
31247 if (nestedType)
233 out << "E";
234 31247 break;
235 }
236 case TY_ENUM: {
237 bool nestedType = false;
238 mangleName(out, chainElement.subType, nestedType);
239 if (nestedType)
240 out << "E";
241 break;
242 }
243 34 case TY_FUNCTION: {
244
2/2
✓ Branch 65 → 66 taken 2 times.
✓ Branch 65 → 67 taken 32 times.
34 out << (chainElement.data.hasCaptures ? "PFC" : "PF");
245
2/2
✓ Branch 75 → 71 taken 94 times.
✓ Branch 75 → 76 taken 34 times.
128 for (const QualType &paramType : chainElement.paramTypes)
246
1/2
✓ Branch 72 → 73 taken 94 times.
✗ Branch 72 → 104 not taken.
94 mangleType(out, paramType);
247 34 out << "E";
248 34 break;
249 }
250 92 case TY_PROCEDURE: {
251
2/2
✓ Branch 78 → 79 taken 4 times.
✓ Branch 78 → 80 taken 88 times.
92 out << (chainElement.data.hasCaptures ? "PFCv" : "PFv");
252
2/2
✓ Branch 87 → 83 taken 59 times.
✓ Branch 87 → 88 taken 92 times.
151 for (size_t i = 1; i < chainElement.paramTypes.size(); i++)
253 59 mangleType(out, chainElement.paramTypes.at(i));
254 92 out << "E";
255 92 break;
256 }
257 default: // GCOV_EXCL_LINE
258 throw CompilerError(INTERNAL_ERROR, "Type " + chainElement.getName(false, true) + " cannot be mangled"); // GCOV_EXCL_LINE
259 }
260 83195 }
261
262 383 std::string NameMangling::mangleTypeInfoName(const StructBase *structBase) {
263
1/2
✓ Branch 2 → 3 taken 383 times.
✗ Branch 2 → 13 not taken.
383 std::stringstream out;
264
1/2
✓ Branch 3 → 4 taken 383 times.
✗ Branch 3 → 11 not taken.
383 out << "_ZTS";
265
2/4
✓ Branch 4 → 5 taken 383 times.
✗ Branch 4 → 11 not taken.
✓ Branch 5 → 6 taken 383 times.
✗ Branch 5 → 11 not taken.
383 mangleType(out, structBase->entry->getQualType());
266
1/2
✓ Branch 6 → 7 taken 383 times.
✗ Branch 6 → 11 not taken.
766 return out.str();
267 383 }
268
269
2/4
✓ Branch 3 → 4 taken 383 times.
✗ Branch 3 → 11 not taken.
✓ Branch 4 → 5 taken 383 times.
✗ Branch 4 → 9 not taken.
383 std::string NameMangling::mangleTypeInfoValue(const std::string &value) { return std::to_string(value.size()) + value; }
270
271 545 std::string NameMangling::mangleTypeInfo(const StructBase *structBase) {
272
1/2
✓ Branch 2 → 3 taken 545 times.
✗ Branch 2 → 13 not taken.
545 std::stringstream out;
273
1/2
✓ Branch 3 → 4 taken 545 times.
✗ Branch 3 → 11 not taken.
545 out << "_ZTI";
274
2/4
✓ Branch 4 → 5 taken 545 times.
✗ Branch 4 → 11 not taken.
✓ Branch 5 → 6 taken 545 times.
✗ Branch 5 → 11 not taken.
545 mangleType(out, structBase->entry->getQualType());
275
1/2
✓ Branch 6 → 7 taken 545 times.
✗ Branch 6 → 11 not taken.
1090 return out.str();
276 545 }
277
278 766 std::string NameMangling::mangleVTable(const StructBase *structBase) {
279
1/2
✓ Branch 2 → 3 taken 766 times.
✗ Branch 2 → 13 not taken.
766 std::stringstream out;
280
1/2
✓ Branch 3 → 4 taken 766 times.
✗ Branch 3 → 11 not taken.
766 out << "_ZTV";
281
2/4
✓ Branch 4 → 5 taken 766 times.
✗ Branch 4 → 11 not taken.
✓ Branch 5 → 6 taken 766 times.
✗ Branch 5 → 11 not taken.
766 mangleType(out, structBase->entry->getQualType());
282
1/2
✓ Branch 6 → 7 taken 766 times.
✗ Branch 6 → 11 not taken.
1532 return out.str();
283 766 }
284
285 336 std::string NameMangling::mangleVTable(const std::string &typeName) {
286
3/6
✓ Branch 3 → 4 taken 336 times.
✗ Branch 3 → 15 not taken.
✓ Branch 4 → 5 taken 336 times.
✗ Branch 4 → 13 not taken.
✓ Branch 5 → 6 taken 336 times.
✗ Branch 5 → 11 not taken.
336 return "_ZTV" + std::to_string(typeName.size()) + typeName;
287 }
288
289 } // namespace spice::compiler
290