GCC Code Coverage Report


src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
180 / 199
90.5%
Branches:
64 / 74, 14 excl
86.5%
Functions:
13 / 13
100.0%

irgenerator/name-mangling.spice
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 // Std imports
4 import "std/data/vector";
5 import "std/text/stringstream";
6 import "std/type/type-conversion";
7
8 // Own imports
9 import "@/global/type-name-disambiguator";
10 import "@/model/function";
11 import "@/model/generic-type";
12 import "@/model/interface";
13 import "@/model/struct";
14 import "@/model/struct-base";
15 import "@/model/union";
16 import "@/symboltablebuilder/qual-type";
17 import "@/symboltablebuilder/type-qualifiers";
18 import "@/symboltablebuilder/super-type";
19 import "@/symboltablebuilder/type";
20 import "@/symboltablebuilder/type-chain";
21
22 /**
23 * Helper for name mangling.
24 * This should be mostly compatible with the C++ Itanium ABI name mangling scheme.
25 *
26 * Spice has no static methods, so the helpers are free functions, prefixed with 'mangle'.
27 *
28 * Names:
29 * - P: pointer
30 * - R: reference
31 * - A: array
32 * - d: double
33 * - i: int
34 * - j: unsigned int
35 * - s: short
36 * - t: unsigned short
37 * - l: long
38 * - m: unsigned long
39 * - a: byte
40 * - h: unsigned byte / unsigned char
41 * - c: char
42 * - PKc: string (const char*)
43 * - b: bool
44 * - v: void
45 * - K: const
46 * - C: capturing
47 *
48 * Reserved:
49 * - I
50 * - E
51 */
52
53 /**
54 * Mangle a function or procedure.
55 * This should be mostly compatible with the C++ Itanium ABI name mangling scheme.
56 *
57 * @param spiceFunc Input function
58 * @return Mangled name
59 */
60 174404 public f<String> mangleFunction(const Function& spiceFunc) {
61 // Check if we have a predefined mangled name, specified by e.g. a function attribute
62
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 174404 times.
174404 if !spiceFunc.predefinedMangledName.isEmpty() {
63 ✗ return String(spiceFunc.predefinedMangledName);
64 }
65
66 // Check if mangling is required
67
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 174404 times.
174404 if !spiceFunc.mangleFunctionName {
68 ✗ return String(spiceFunc.name);
69 }
70
71 174404 StringStream mangledName;
72 174404 mangledName << "_Z";
73
74 // This type
75
2/2
✓ Branch 0 taken 61402 times.
✓ Branch 1 taken 113002 times.
174404 if spiceFunc.isMethod() {
76 113002 mangledName << "N";
77 113002 mangleType(mangledName, spiceFunc.thisType);
78 113002 }
79
80 // Function name
81 174404 const String name = spiceFunc.name + spiceFunc.mangleSuffix;
82 174404 mangledName << toString(name.getLength()) << name;
83
84 // Template types
85 174404 bool isSelfGeneric = !spiceFunc.templateTypes.isEmpty();
86
2/2
✓ Branch 0 taken 61402 times.
✓ Branch 1 taken 113002 times.
174404 if spiceFunc.isMethod() {
87 113002 const QualTypeList thisTemplateTypes = spiceFunc.thisType.getTemplateTypes();
88 113002 isSelfGeneric = spiceFunc.templateTypes.getSize() > thisTemplateTypes.getSize();
89 113002 }
90
2/2
✓ Branch 0 taken 12764 times.
✓ Branch 1 taken 161640 times.
174404 if isSelfGeneric {
91 12764 mangledName << "I";
92 // Template types themselves
93
2/2
✓ Branch 0 taken 15950 times.
✓ Branch 1 taken 12764 times.
28714 foreach const GenericType& genericTemplateType : spiceFunc.templateTypes {
94 15950 const String subType = genericTemplateType.qualType.getSubType();
95 15950 assert spiceFunc.typeMapping.contains(subType);
96 15950 const QualType& actualType = spiceFunc.typeMapping.get(subType);
97 15950 mangleType(mangledName, actualType);
98 15950 }
99 12764 mangledName << "E";
100
101 // Insert second end marker to end the nested type
102
2/2
✓ Branch 0 taken 10259 times.
✓ Branch 1 taken 2505 times.
12764 if spiceFunc.isMethod() {
103 2505 mangledName << "E";
104 2505 }
105
106 // Return type
107
2/2
✓ Branch 0 taken 6586 times.
✓ Branch 1 taken 6178 times.
12764 if spiceFunc.isFunction() {
108 6586 mangleType(mangledName, spiceFunc.returnType);
109 6586 } else {
110 6178 mangledName << "v";
111 6178 }
112
2/2
✓ Branch 0 taken 51143 times.
✓ Branch 1 taken 110497 times.
174404 } else if spiceFunc.isMethod() {
113 110497 mangledName << "E";
114 161640 }
115
116 // Parameter types
117
2/2
✓ Branch 0 taken 163032 times.
✓ Branch 1 taken 174404 times.
337436 foreach const Param& param : spiceFunc.paramList {
118 163032 assert !param.isOptional;
119 163032 mangleType(mangledName, param.qualType);
120 163032 }
121
2/2
✓ Branch 0 taken 107344 times.
✓ Branch 1 taken 67060 times.
174404 if spiceFunc.paramList.isEmpty() {
122 67060 mangledName << "v";
123 67060 }
124
125 348808 return String(mangledName.str());
126 174404 }
127
128 /**
129 * Mangle a struct
130 * This should be mostly compatible with the C++ Itanium ABI name mangling scheme.
131 *
132 * @param spiceStruct Input struct
133 * @return Mangled name
134 */
135 9114 public f<String> mangleStruct(const Struct& spiceStruct) {
136 18228 return String("struct.") + spiceStruct.name;
137 9114 }
138
139 /**
140 * Mangle an interface
141 *
142 * @param spiceInterface Input interface
143 * @return Mangled name
144 */
145 823 public f<String> mangleInterface(const Interface& spiceInterface) {
146 1646 return String("interface.") + spiceInterface.name;
147 823 }
148
149 /**
150 * Mangle a union
151 *
152 * @param spiceUnion Input union
153 * @return Mangled name
154 */
155 27 public f<String> mangleUnion(const Union& spiceUnion) {
156 54 return String("union.") + spiceUnion.name;
157 27 }
158
159 /**
160 * Mangle a fully qualified name like e.g. test::s1::calledMethod to 4test2s112calledMethod
161 * This should be mostly compatible with the C++ Itanium ABI name mangling scheme.
162 *
163 * @param out Output string stream
164 * @param name Input name
165 * @param nestedType True if the name is a nested type
166 */
167 195837 p mangleName(StringStream& out, const String& name, bool& nestedType) {
168 195837 Vector<String> fragments;
169 195837 const Vector<String> tokens = splitLikeGetline(name, ':');
170
2/2
✓ Branch 0 taken 195837 times.
✓ Branch 1 taken 195837 times.
391674 foreach const String& token : tokens {
171 195837 const Vector<String> subTokens = splitLikeGetline(token, '/');
172
2/2
✓ Branch 0 taken 195837 times.
✓ Branch 1 taken 195837 times.
391674 foreach const String& subToken : subTokens {
173 195837 fragments.pushBack(subToken);
174 195837 }
175 195837 }
176
177 // Start a nested type if needed. The caller needs to emit the end marker.
178
1/2
✓ Branch 0 taken 195837 times.
✗ Branch 1 not taken.
195837 if fragments.getSize() > 1l {
179 ✗ out << "N";
180 ✗ nestedType = true;
181 ✗ }
182
183 // Process each fragment and append it to the result
184
2/2
✓ Branch 0 taken 195837 times.
✓ Branch 1 taken 195837 times.
391674 foreach const String& currentFragment : fragments {
185 195837 out << toString(currentFragment.getLength()) << currentFragment;
186 195837 }
187 195837 }
188
189 /**
190 * Split a string at the given delimiter with the semantics of repeated std::getline calls: empty tokens between two
191 * delimiters are kept, but there is no trailing empty token (and none at all for an empty input).
192 *
193 * @param input Input string
194 * @param delimiter Delimiter char
195 * @return Tokens
196 */
197 391674 f<Vector<String>> splitLikeGetline(const String& input, char delimiter) {
198 391674 Vector<String> tokens;
199 391674 String token;
200 391674 const unsigned long length = input.getLength();
201
2/2
✓ Branch 0 taken 391674 times.
✓ Branch 1 taken 2755054 times.
3146728 for unsigned long i = 0l; i < length; i++ {
202 2755054 const char c = input[i];
203
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2755054 times.
2755054 if c == delimiter {
204 ✗ tokens.pushBack(token);
205 ✗ token.clear();
206 ✗ } else {
207 2755054 token += c;
208 2755054 }
209 2755054 }
210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 391674 times.
391674 if !token.isEmpty() {
211 391674 tokens.pushBack(token);
212 391674 }
213 783348 return tokens;
214 391674 }
215
216 /**
217 * Mangle a symbol qualType
218 * This should be mostly compatible with the C++ Itanium ABI name mangling scheme.
219 *
220 * @param out Output string stream
221 * @param qualType Input symbol qualType
222 */
223 381954 p mangleType(StringStream& out, const QualType& qualType) {
224 381954 const Type* rawType = qualType.getType();
225 381954 assert !qualType.hasAnyGenericParts();
226
227 // Unwrap qualType chain
228 381954 assert !rawType.typeChain.isEmpty();
229
2/2
✓ Branch 0 taken 73553 times.
✓ Branch 1 taken 381954 times.
455507 for unsigned long i = rawType.typeChain.getSize() - 1l; i >= 1l; i-- {
230 73553 mangleTypeChainElement(out, rawType.typeChain.get(i), false);
231 73553 }
232
233 // Qualifiers
234 381954 const TypeQualifiers& qualifiers = qualType.qualifiers;
235 381954 assert qualifiers.isSigned == !qualifiers.isUnsigned;
236 381954 const bool signedness = qualifiers.isSigned;
237 // The 'heap' qualifier has no equivalent in the C++ Itanium ABI, but it changes the semantics of the type (ownership), so
238 // it must be reflected in the mangled name. Otherwise e.g. 'byte*' and 'heap byte*' manifestations of the same generic
239 // function would collide on the same mangled name. Piggyback on the 'volatile' cv-qualifier slot ('V', ordered before
240 // 'K' per the Itanium <CV-qualifiers> grammar) so demangling still succeeds.
241
2/2
✓ Branch 0 taken 366865 times.
✓ Branch 1 taken 15089 times.
381954 if qualifiers.isHeap {
242 15089 out << "V";
243 15089 }
244
4/4
✓ Branch 0 taken 336625 times.
✓ Branch 1 taken 45329 times.
✓ Branch 2 taken 346677 times.
✓ Branch 3 taken 35277 times.
381954 if qualifiers.isConst && rawType.typeChain.getSize() > 1l {
245 35277 out << "K";
246 35277 }
247
248 // Base chain element
249 381954 mangleTypeChainElement(out, rawType.typeChain.front(), signedness);
250 381954 }
251
252 /**
253 * Mangle a type chain element
254 *
255 * @param out Output string stream
256 * @param chainElement Input type chain element
257 * @param signedness Signedness of the type
258 */
259 455507 p mangleTypeChainElement(StringStream& out, const TypeChainElement& chainElement, bool signedness) {
260
14/16
✓ Branch 0 taken 23385 times.
✓ Branch 1 taken 49608 times.
✓ Branch 2 taken 46609 times.
✓ Branch 3 taken 3605 times.
✓ Branch 4 taken 29831 times.
✓ Branch 5 taken 18926 times.
✓ Branch 6 taken 25795 times.
✓ Branch 7 taken 46103 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 184 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 195837 times.
✓ Branch 12 taken 560 times.
✓ Branch 13 taken 3413 times.
✓ Branch 14 taken 8537 times.
✓ Branch 15 taken 3114 times.
455507 switch chainElement.superType {
261 case SuperType::TY_PTR: {
262 23385 out << "P";
263 23385 }
264 case SuperType::TY_ARRAY: {
265
2/2
✓ Branch 0 taken 457 times.
✓ Branch 1 taken 103 times.
560 if cast<unsigned long>(chainElement.data.arraySize) == ARRAY_SIZE_UNKNOWN {
266 457 out << "P";
267 457 } else {
268 103 out << "A" << toString(chainElement.data.arraySize) << "_";
269 103 }
270 560 }
271 case SuperType::TY_REF: {
272 49608 out << "R";
273 49608 }
274 case SuperType::TY_DOUBLE: {
275 3413 assert signedness; // Unsigned double types are forbidden
276 3413 out << "d";
277 3413 }
278 case SuperType::TY_INT: {
279 46609 out << (signedness ? "i" : "j");
280 46609 }
281 case SuperType::TY_SHORT: {
282 3605 out << (signedness ? "s" : "t");
283 3605 }
284 case SuperType::TY_LONG: {
285 29831 out << (signedness ? "l" : "m");
286 29831 }
287 case SuperType::TY_BYTE: {
288 18926 out << (signedness ? "a" : "h");
289 18926 }
290 case SuperType::TY_CHAR: {
291 25795 out << "c";
292 25795 }
293 case SuperType::TY_STRING: {
294 46103 out << "PKc";
295 46103 }
296 case SuperType::TY_BOOL: {
297 8537 assert !signedness; // Signed bool types are forbidden
298 8537 out << "b";
299 8537 }
300 case SuperType::TY_STRUCT, SuperType::TY_INTERFACE, SuperType::TY_UNION: {
301 195837 bool nestedType = false;
302 // Append a disambiguation suffix for same-named but distinct structs/interfaces/unions (see issue #1253), so
303 // that two independent types sharing a name do not end up with the same mangled name (and thus clash at link
304 // time).
305 195837 const String name = chainElement.subType + getDisambiguationSuffix(chainElement.subType, chainElement.typeId);
306 195837 mangleName(out, name, nestedType);
307
2/2
✓ Branch 0 taken 64458 times.
✓ Branch 1 taken 131379 times.
195837 if !chainElement.templateTypes.isEmpty() {
308 64458 out << "I";
309
2/2
✓ Branch 0 taken 74388 times.
✓ Branch 1 taken 64458 times.
138846 foreach const QualType& templateType : chainElement.templateTypes {
310 74388 mangleType(out, templateType);
311 74388 }
312 64458 out << "E";
313 64458 }
314
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 195837 times.
195837 if nestedType {
315 ✗ out << "E";
316 ✗ }
317 195837 }
318 case SuperType::TY_ENUM: {
319 ✗ bool nestedType = false;
320 ✗ mangleName(out, chainElement.subType, nestedType);
321
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if nestedType {
322 ✗ out << "E";
323 ✗ }
324 ✗ }
325 case SuperType::TY_FUNCTION: {
326 184 out << (chainElement.data.hasCaptures ? "PFC" : "PF");
327
2/2
✓ Branch 0 taken 411 times.
✓ Branch 1 taken 184 times.
595 foreach const QualType& paramType : chainElement.paramTypes {
328 411 mangleType(out, paramType);
329 411 }
330 184 out << "E";
331 184 }
332 case SuperType::TY_PROCEDURE: {
333 3114 out << (chainElement.data.hasCaptures ? "PFCv" : "PFv");
334
2/2
✓ Branch 0 taken 1649 times.
✓ Branch 1 taken 3114 times.
4763 for unsigned long i = 1l; i < chainElement.paramTypes.getSize(); i++ {
335 1649 mangleType(out, chainElement.paramTypes.get(i));
336 1649 }
337 3114 out << "E";
338 3114 }
339 default: {
340 ✗ const String message = "Type " + chainElement.getName(false, true, true) + " cannot be mangled";
341 ✗ panic(Error(message.getRaw())); // Internal error
342 ✗ }
343 }
344 455507 }
345
346 1554 public f<String> mangleTypeInfoName(const StructBase* structBase) {
347 1554 StringStream out;
348 1554 out << "_ZTS";
349 1554 mangleType(out, structBase.entry.getQualType());
350 3108 return String(out.str());
351 1554 }
352
353 1554 public f<String> mangleTypeInfoValue(const String& value) {
354 3108 return toString(value.getLength()) + value;
355 1554 }
356
357 2260 public f<String> mangleTypeInfo(const StructBase* structBase) {
358 2260 StringStream out;
359 2260 out << "_ZTI";
360 2260 mangleType(out, structBase.entry.getQualType());
361 4520 return String(out.str());
362 2260 }
363
364 3122 public f<String> mangleVTable(const StructBase* structBase) {
365 3122 StringStream out;
366 3122 out << "_ZTV";
367 3122 mangleType(out, structBase.entry.getQualType());
368 6244 return String(out.str());
369 3122 }
370
371 1394 public f<String> mangleVTable(const String& typeName) {
372 2788 return "_ZTV" + toString(typeName.getLength()) + typeName;
373 1394 }
374