GCC Code Coverage Report


Directory: ../
File: src/irgenerator/NameMangling.cpp
Date: 2025-09-17 23:16:16
Exec Total Coverage
Lines: 156 167 93.4%
Functions: 13 13 100.0%
Branches: 154 252 61.1%

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 26842 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 0 (3→4) not taken.
✓ Branch 1 (3→6) taken 26842 times.
26842 if (!spiceFunc.predefinedMangledName.empty())
24 return spiceFunc.predefinedMangledName;
25
26 // Check if mangling is required
27
1/2
✗ Branch 0 (6→7) not taken.
✓ Branch 1 (6→9) taken 26842 times.
26842 if (!spiceFunc.mangleFunctionName)
28 return spiceFunc.name;
29
30
1/2
✓ Branch 0 (9→10) taken 26842 times.
✗ Branch 1 (9→104) not taken.
26842 std::stringstream mangledName;
31
1/2
✓ Branch 0 (10→11) taken 26842 times.
✗ Branch 1 (10→102) not taken.
26842 mangledName << "_Z";
32
33 // This type
34
2/2
✓ Branch 0 (14→15) taken 17455 times.
✓ Branch 1 (14→17) taken 9387 times.
26842 if (spiceFunc.isMethod()) {
35
1/2
✓ Branch 0 (15→16) taken 17455 times.
✗ Branch 1 (15→102) not taken.
17455 mangledName << "N";
36
1/2
✓ Branch 0 (16→17) taken 17455 times.
✗ Branch 1 (16→102) not taken.
17455 mangleType(mangledName, spiceFunc.thisType);
37 }
38
39 // Function name
40
1/2
✓ Branch 0 (17→18) taken 26842 times.
✗ Branch 1 (17→102) not taken.
26842 const std::string name = spiceFunc.name + spiceFunc.mangleSuffix;
41
2/4
✓ Branch 0 (19→20) taken 26842 times.
✗ Branch 1 (19→100) not taken.
✓ Branch 2 (20→21) taken 26842 times.
✗ Branch 3 (20→100) not taken.
26842 mangledName << name.length() << name;
42
43 // Template types
44
1/2
✓ Branch 0 (22→23) taken 26842 times.
✗ Branch 1 (22→100) not taken.
26842 bool isSelfGeneric = !spiceFunc.templateTypes.empty();
45
2/2
✓ Branch 0 (25→26) taken 17455 times.
✓ Branch 1 (25→30) taken 9387 times.
26842 if (spiceFunc.isMethod())
46
1/2
✓ Branch 0 (27→28) taken 17455 times.
✗ Branch 1 (27→100) not taken.
17455 isSelfGeneric = spiceFunc.templateTypes.size() > spiceFunc.thisType.getTemplateTypes().size();
47
2/2
✓ Branch 0 (30→31) taken 1568 times.
✓ Branch 1 (30→57) taken 25274 times.
26842 if (isSelfGeneric) {
48
1/2
✓ Branch 0 (31→32) taken 1568 times.
✗ Branch 1 (31→100) not taken.
1568 mangledName << "I";
49 // Template types themselves
50
2/2
✓ Branch 0 (44→34) taken 1691 times.
✓ Branch 1 (44→45) taken 1568 times.
3259 for (const GenericType &genericTemplateType : spiceFunc.templateTypes) {
51
3/6
✓ Branch 0 (35→36) taken 1691 times.
✗ Branch 1 (35→93) not taken.
✓ Branch 2 (36→37) taken 1691 times.
✗ Branch 3 (36→93) not taken.
✗ Branch 4 (37→38) not taken.
✓ Branch 5 (37→39) taken 1691 times.
1691 assert(spiceFunc.typeMapping.contains(genericTemplateType.getSubType()));
52
2/4
✓ Branch 0 (39→40) taken 1691 times.
✗ Branch 1 (39→93) not taken.
✓ Branch 2 (40→41) taken 1691 times.
✗ Branch 3 (40→93) not taken.
1691 const QualType &actualType = spiceFunc.typeMapping.at(genericTemplateType.getSubType());
53
1/2
✓ Branch 0 (41→42) taken 1691 times.
✗ Branch 1 (41→93) not taken.
1691 mangleType(mangledName, actualType);
54 }
55
1/2
✓ Branch 0 (45→46) taken 1568 times.
✗ Branch 1 (45→100) not taken.
1568 mangledName << "E";
56
57 // Insert second end marker to end the nested type
58
2/2
✓ Branch 0 (49→50) taken 523 times.
✓ Branch 1 (49→51) taken 1045 times.
1568 if (spiceFunc.isMethod())
59
1/2
✓ Branch 0 (50→51) taken 523 times.
✗ Branch 1 (50→100) not taken.
523 mangledName << "E";
60
61 // Return type
62
2/2
✓ Branch 0 (54→55) taken 1039 times.
✓ Branch 1 (54→56) taken 529 times.
1568 if (spiceFunc.isFunction())
63
1/2
✓ Branch 0 (55→62) taken 1039 times.
✗ Branch 1 (55→100) not taken.
1039 mangleType(mangledName, spiceFunc.returnType);
64 else
65
1/2
✓ Branch 0 (56→62) taken 529 times.
✗ Branch 1 (56→100) not taken.
529 mangledName << "v";
66
67
2/2
✓ Branch 0 (60→61) taken 16932 times.
✓ Branch 1 (60→62) taken 8342 times.
25274 } else if (spiceFunc.isMethod()) {
68
1/2
✓ Branch 0 (61→62) taken 16932 times.
✗ Branch 1 (61→100) not taken.
16932 mangledName << "E";
69 }
70
71 // Parameter types
72
2/2
✓ Branch 0 (70→64) taken 25232 times.
✓ Branch 1 (70→71) taken 26842 times.
52074 for (const auto &[qualType, isOptional] : spiceFunc.paramList) {
73
1/2
✗ Branch 0 (65→66) not taken.
✓ Branch 1 (65→67) taken 25232 times.
25232 assert(!isOptional);
74
1/2
✓ Branch 0 (67→68) taken 25232 times.
✗ Branch 1 (67→94) not taken.
25232 mangleType(mangledName, qualType);
75 }
76
2/2
✓ Branch 0 (72→73) taken 9118 times.
✓ Branch 1 (72→74) taken 17724 times.
26842 if (spiceFunc.paramList.empty())
77
1/2
✓ Branch 0 (73→74) taken 9118 times.
✗ Branch 1 (73→100) not taken.
9118 mangledName << "v";
78
79 #ifndef NDEBUG
80 26842 const TypeMapping &typeMapping = spiceFunc.typeMapping;
81
2/4
✓ Branch 0 (74→75) taken 26842 times.
✗ Branch 1 (74→96) not taken.
✓ Branch 2 (75→76) taken 26842 times.
✗ Branch 3 (75→95) not taken.
26842 const bool returnTypeIsFctOrProc = spiceFunc.returnType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE});
82
2/4
✓ Branch 0 (2→3) taken 25223 times.
✗ Branch 1 (2→8) not taken.
✓ Branch 2 (3→4) taken 25223 times.
✗ Branch 3 (3→7) not taken.
25223 const auto paramPredicate = [](const Param &p) { return p.qualType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE}); };
83
1/2
✓ Branch 0 (76→77) taken 26842 times.
✗ Branch 1 (76→100) not taken.
26842 const bool paramTypeIsFctOrProc = std::ranges::any_of(spiceFunc.paramList, paramPredicate);
84 7407 const auto templateTypePredicate = [&](const GenericType &t) {
85
4/8
✓ Branch 0 (2→3) taken 7407 times.
✗ Branch 1 (2→10) not taken.
✓ Branch 2 (3→4) taken 7407 times.
✗ Branch 3 (3→10) not taken.
✓ Branch 4 (4→5) taken 7407 times.
✗ Branch 5 (4→10) not taken.
✓ Branch 6 (5→6) taken 7407 times.
✗ Branch 7 (5→9) not taken.
7407 return typeMapping.at(t.getSubType()).getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE});
86 26842 };
87
1/2
✓ Branch 0 (77→78) taken 26842 times.
✗ Branch 1 (77→100) not taken.
26842 const bool templateTypeIsFctOrProc = std::ranges::any_of(spiceFunc.templateTypes, templateTypePredicate);
88
6/6
✓ Branch 0 (78→79) taken 26840 times.
✓ Branch 1 (78→87) taken 2 times.
✓ Branch 2 (79→80) taken 26799 times.
✓ Branch 3 (79→87) taken 41 times.
✓ Branch 4 (80→81) taken 26778 times.
✓ Branch 5 (80→87) taken 21 times.
26842 if (!returnTypeIsFctOrProc && !paramTypeIsFctOrProc && !templateTypeIsFctOrProc)
89
3/6
✓ Branch 0 (81→82) taken 26778 times.
✗ Branch 1 (81→99) not taken.
✓ Branch 2 (82→83) taken 26778 times.
✗ Branch 3 (82→97) not taken.
✗ Branch 4 (83→84) not taken.
✓ Branch 5 (83→85) taken 26778 times.
26778 assert(CommonUtil::isValidMangledName(mangledName.str()));
90 #endif
91
92
1/2
✓ Branch 0 (87→88) taken 26842 times.
✗ Branch 1 (87→100) not taken.
26842 return mangledName.str();
93 26842 }
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 1677 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 238 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 29444 void NameMangling::mangleName(std::stringstream &out, const std::string &name, bool &nestedType) {
122 29444 std::vector<std::string> fragments;
123
1/2
✓ Branch 0 (2→3) taken 29444 times.
✗ Branch 1 (2→51) not taken.
29444 std::istringstream ss(name);
124 29444 std::string token;
125
126
4/6
✓ Branch 0 (16→17) taken 58888 times.
✗ Branch 1 (16→47) not taken.
✓ Branch 2 (17→18) taken 58888 times.
✗ Branch 3 (17→47) not taken.
✓ Branch 4 (18→5) taken 29444 times.
✓ Branch 5 (18→19) taken 29444 times.
58888 while (std::getline(ss, token, ':')) {
127
1/2
✓ Branch 0 (5→6) taken 29444 times.
✗ Branch 1 (5→42) not taken.
29444 std::istringstream subStream(token);
128 29444 std::string subToken;
129
4/6
✓ Branch 0 (10→11) taken 58888 times.
✗ Branch 1 (10→38) not taken.
✓ Branch 2 (11→12) taken 58888 times.
✗ Branch 3 (11→38) not taken.
✓ Branch 4 (12→8) taken 29444 times.
✓ Branch 5 (12→13) taken 29444 times.
58888 while (std::getline(subStream, subToken, '/')) {
130
1/2
✓ Branch 0 (8→9) taken 29444 times.
✗ Branch 1 (8→38) not taken.
29444 fragments.push_back(subToken);
131 }
132 29444 }
133
134 // Start a nested type if needed. The caller needs to emit the end marker.
135
1/2
✗ Branch 0 (20→21) not taken.
✓ Branch 1 (20→23) taken 29444 times.
29444 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 0 (33→25) taken 29444 times.
✓ Branch 1 (33→34) taken 29444 times.
58888 for (const std::string &fragment : fragments) {
142 29444 int fragmentLength = static_cast<int>(fragment.length());
143
2/4
✓ Branch 0 (28→29) taken 29444 times.
✗ Branch 1 (28→43) not taken.
✓ Branch 2 (29→30) taken 29444 times.
✗ Branch 3 (29→43) not taken.
29444 out << std::to_string(fragmentLength) << fragment;
144 }
145 29444 }
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 59439 void NameMangling::mangleType(std::stringstream &out, QualType qualType) { // NOLINT(*-no-recursion)
156 59439 const Type *type = qualType.getType();
157
1/2
✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 59439 times.
59439 assert(!qualType.hasAnyGenericParts());
158
159 // Unwrap qualType chain
160
1/2
✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→9) taken 59439 times.
59439 assert(!type->typeChain.empty());
161
2/2
✓ Branch 0 (14→11) taken 8122 times.
✓ Branch 1 (14→15) taken 59439 times.
67561 for (size_t i = type->typeChain.size() - 1; i >= 1; i--)
162 8122 mangleTypeChainElement(out, type->typeChain.at(i), false);
163
164 // Qualifiers
165
1/2
✗ Branch 0 (17→18) not taken.
✓ Branch 1 (17→19) taken 59439 times.
59439 assert(qualType.getQualifiers().isSigned == !qualType.getQualifiers().isUnsigned);
166 59439 const bool signedness = qualType.getQualifiers().isSigned;
167
6/6
✓ Branch 0 (21→22) taken 5811 times.
✓ Branch 1 (21→25) taken 53628 times.
✓ Branch 2 (23→24) taken 4503 times.
✓ Branch 3 (23→25) taken 1308 times.
✓ Branch 4 (26→27) taken 4503 times.
✓ Branch 5 (26→28) taken 54936 times.
59439 if (qualType.getQualifiers().isConst && type->typeChain.size() > 1)
168 4503 out << "K";
169
170 // Base chain element
171 59439 mangleTypeChainElement(out, type->typeChain.front(), signedness);
172 59439 }
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 67561 void NameMangling::mangleTypeChainElement(std::stringstream &out, const TypeChainElement &chainElement, bool signedness) {
183
14/16
✓ Branch 0 (2→3) taken 1969 times.
✓ Branch 1 (2→4) taken 120 times.
✓ Branch 2 (2→10) taken 6033 times.
✓ Branch 3 (2→12) taken 420 times.
✓ Branch 4 (2→16) taken 6301 times.
✓ Branch 5 (2→21) taken 326 times.
✓ Branch 6 (2→26) taken 4589 times.
✓ Branch 7 (2→31) taken 1787 times.
✓ Branch 8 (2→36) taken 5887 times.
✓ Branch 9 (2→38) taken 9162 times.
✓ Branch 10 (2→40) taken 1452 times.
✓ Branch 11 (2→44) taken 29444 times.
✗ Branch 12 (2→60) not taken.
✓ Branch 13 (2→65) taken 10 times.
✓ Branch 14 (2→78) taken 61 times.
✗ Branch 15 (2→90) not taken.
67561 switch (chainElement.superType) {
184 1969 case TY_PTR:
185 1969 out << "P";
186 1969 break;
187 120 case TY_ARRAY:
188
2/2
✓ Branch 0 (4→5) taken 72 times.
✓ Branch 1 (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 6033 case TY_REF:
194 6033 out << "R";
195 6033 break;
196 420 case TY_DOUBLE:
197
1/2
✗ Branch 0 (12→13) not taken.
✓ Branch 1 (12→14) taken 420 times.
420 assert(signedness && "Unsigned double types are forbidden");
198 420 out << "d";
199 420 break;
200 6301 case TY_INT:
201
2/2
✓ Branch 0 (16→17) taken 5809 times.
✓ Branch 1 (16→18) taken 492 times.
6301 out << (signedness ? "i" : "j");
202 6301 break;
203 326 case TY_SHORT:
204
2/2
✓ Branch 0 (21→22) taken 289 times.
✓ Branch 1 (21→23) taken 37 times.
326 out << (signedness ? "s" : "t");
205 326 break;
206 4589 case TY_LONG:
207
2/2
✓ Branch 0 (26→27) taken 1115 times.
✓ Branch 1 (26→28) taken 3474 times.
4589 out << (signedness ? "l" : "m");
208 4589 break;
209 1787 case TY_BYTE:
210
1/2
✗ Branch 0 (31→32) not taken.
✓ Branch 1 (31→33) taken 1787 times.
1787 out << (signedness ? "a" : "h");
211 1787 break;
212 5887 case TY_CHAR:
213 5887 out << "c";
214 5887 break;
215 9162 case TY_STRING:
216 9162 out << "PKc";
217 9162 break;
218 1452 case TY_BOOL:
219
1/2
✗ Branch 0 (40→41) not taken.
✓ Branch 1 (40→42) taken 1452 times.
1452 assert(!signedness && "Signed bool types are forbidden");
220 1452 out << "b";
221 1452 break;
222 29444 case TY_STRUCT: // fall-through
223 case TY_INTERFACE: {
224 29444 bool nestedType = false;
225
1/2
✓ Branch 0 (44→45) taken 29444 times.
✗ Branch 1 (44→102) not taken.
29444 mangleName(out, chainElement.subType, nestedType);
226
2/2
✓ Branch 0 (46→47) taken 9593 times.
✓ Branch 1 (46→56) taken 19851 times.
29444 if (!chainElement.templateTypes.empty()) {
227
1/2
✓ Branch 0 (47→48) taken 9593 times.
✗ Branch 1 (47→102) not taken.
9593 out << "I";
228
2/2
✓ Branch 0 (54→50) taken 12419 times.
✓ Branch 1 (54→55) taken 9593 times.
22012 for (const QualType &templateType : chainElement.templateTypes)
229
1/2
✓ Branch 0 (51→52) taken 12419 times.
✗ Branch 1 (51→101) not taken.
12419 mangleType(out, templateType);
230
1/2
✓ Branch 0 (55→56) taken 9593 times.
✗ Branch 1 (55→102) not taken.
9593 out << "E";
231 }
232
1/2
✗ Branch 0 (56→57) not taken.
✓ Branch 1 (56→58) taken 29444 times.
29444 if (nestedType)
233 out << "E";
234 29444 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 10 case TY_FUNCTION: {
244
2/2
✓ Branch 0 (65→66) taken 2 times.
✓ Branch 1 (65→67) taken 8 times.
10 out << (chainElement.data.hasCaptures ? "PFC" : "PF");
245
2/2
✓ Branch 0 (75→71) taken 22 times.
✓ Branch 1 (75→76) taken 10 times.
32 for (const QualType &paramType : chainElement.paramTypes)
246
1/2
✓ Branch 0 (72→73) taken 22 times.
✗ Branch 1 (72→104) not taken.
22 mangleType(out, paramType);
247 10 out << "E";
248 10 break;
249 }
250 61 case TY_PROCEDURE: {
251
2/2
✓ Branch 0 (78→79) taken 4 times.
✓ Branch 1 (78→80) taken 57 times.
61 out << (chainElement.data.hasCaptures ? "PFCv" : "PFv");
252
2/2
✓ Branch 0 (87→83) taken 11 times.
✓ Branch 1 (87→88) taken 61 times.
72 for (size_t i = 1; i < chainElement.paramTypes.size(); i++)
253 11 mangleType(out, chainElement.paramTypes.at(i));
254 61 out << "E";
255 61 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 67561 }
261
262 355 std::string NameMangling::mangleTypeInfoName(const StructBase *structBase) {
263
1/2
✓ Branch 0 (2→3) taken 355 times.
✗ Branch 1 (2→13) not taken.
355 std::stringstream out;
264
1/2
✓ Branch 0 (3→4) taken 355 times.
✗ Branch 1 (3→11) not taken.
355 out << "_ZTS";
265
2/4
✓ Branch 0 (4→5) taken 355 times.
✗ Branch 1 (4→11) not taken.
✓ Branch 2 (5→6) taken 355 times.
✗ Branch 3 (5→11) not taken.
355 mangleType(out, structBase->entry->getQualType());
266
1/2
✓ Branch 0 (6→7) taken 355 times.
✗ Branch 1 (6→11) not taken.
710 return out.str();
267 355 }
268
269
2/4
✓ Branch 0 (3→4) taken 355 times.
✗ Branch 1 (3→11) not taken.
✓ Branch 2 (4→5) taken 355 times.
✗ Branch 3 (4→9) not taken.
355 std::string NameMangling::mangleTypeInfoValue(const std::string &value) { return std::to_string(value.size()) + value; }
270
271 505 std::string NameMangling::mangleTypeInfo(const StructBase *structBase) {
272
1/2
✓ Branch 0 (2→3) taken 505 times.
✗ Branch 1 (2→13) not taken.
505 std::stringstream out;
273
1/2
✓ Branch 0 (3→4) taken 505 times.
✗ Branch 1 (3→11) not taken.
505 out << "_ZTI";
274
2/4
✓ Branch 0 (4→5) taken 505 times.
✗ Branch 1 (4→11) not taken.
✓ Branch 2 (5→6) taken 505 times.
✗ Branch 3 (5→11) not taken.
505 mangleType(out, structBase->entry->getQualType());
275
1/2
✓ Branch 0 (6→7) taken 505 times.
✗ Branch 1 (6→11) not taken.
1010 return out.str();
276 505 }
277
278 710 std::string NameMangling::mangleVTable(const StructBase *structBase) {
279
1/2
✓ Branch 0 (2→3) taken 710 times.
✗ Branch 1 (2→13) not taken.
710 std::stringstream out;
280
1/2
✓ Branch 0 (3→4) taken 710 times.
✗ Branch 1 (3→11) not taken.
710 out << "_ZTV";
281
2/4
✓ Branch 0 (4→5) taken 710 times.
✗ Branch 1 (4→11) not taken.
✓ Branch 2 (5→6) taken 710 times.
✗ Branch 3 (5→11) not taken.
710 mangleType(out, structBase->entry->getQualType());
282
1/2
✓ Branch 0 (6→7) taken 710 times.
✗ Branch 1 (6→11) not taken.
1420 return out.str();
283 710 }
284
285 312 std::string NameMangling::mangleVTable(const std::string &typeName) {
286
3/6
✓ Branch 0 (3→4) taken 312 times.
✗ Branch 1 (3→15) not taken.
✓ Branch 2 (4→5) taken 312 times.
✗ Branch 3 (4→13) not taken.
✓ Branch 4 (5→6) taken 312 times.
✗ Branch 5 (5→11) not taken.
312 return "_ZTV" + std::to_string(typeName.size()) + typeName;
287 }
288
289 } // namespace spice::compiler
290