Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright (c) 2021-2024 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 | 16174 | 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 1 not taken.
✓ Branch 2 taken 16174 times.
|
16174 | if (!spiceFunc.predefinedMangledName.empty()) |
24 | ✗ | return spiceFunc.predefinedMangledName; | |
25 | |||
26 | // Check if mangling is required | ||
27 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16174 times.
|
16174 | if (!spiceFunc.mangleFunctionName) |
28 | ✗ | return spiceFunc.name; | |
29 | |||
30 |
1/2✓ Branch 1 taken 16174 times.
✗ Branch 2 not taken.
|
16174 | std::stringstream mangledName; |
31 |
1/2✓ Branch 1 taken 16174 times.
✗ Branch 2 not taken.
|
16174 | mangledName << "_Z"; |
32 | |||
33 | // This type | ||
34 |
2/2✓ Branch 0 taken 11664 times.
✓ Branch 1 taken 4510 times.
|
16174 | if (spiceFunc.isMethod()) { |
35 |
1/2✓ Branch 1 taken 11664 times.
✗ Branch 2 not taken.
|
11664 | mangledName << "N"; |
36 |
1/2✓ Branch 1 taken 11664 times.
✗ Branch 2 not taken.
|
11664 | mangleType(mangledName, spiceFunc.thisType); |
37 | } | ||
38 | |||
39 | // Function name | ||
40 |
1/2✓ Branch 1 taken 16174 times.
✗ Branch 2 not taken.
|
16174 | const std::string name = spiceFunc.name + spiceFunc.mangleSuffix; |
41 |
2/4✓ Branch 2 taken 16174 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 16174 times.
✗ Branch 6 not taken.
|
16174 | mangledName << name.length() << name; |
42 | |||
43 | // Template types | ||
44 |
1/2✓ Branch 2 taken 16174 times.
✗ Branch 3 not taken.
|
16174 | bool isSelfGeneric = !spiceFunc.templateTypes.empty(); |
45 |
2/2✓ Branch 0 taken 11664 times.
✓ Branch 1 taken 4510 times.
|
16174 | if (spiceFunc.isMethod()) |
46 |
1/2✓ Branch 2 taken 11664 times.
✗ Branch 3 not taken.
|
11664 | isSelfGeneric = spiceFunc.templateTypes.size() > spiceFunc.thisType.getTemplateTypes().size(); |
47 |
2/2✓ Branch 0 taken 829 times.
✓ Branch 1 taken 15345 times.
|
16174 | if (isSelfGeneric) { |
48 |
1/2✓ Branch 1 taken 829 times.
✗ Branch 2 not taken.
|
829 | mangledName << "I"; |
49 | // Template types themselves | ||
50 |
2/2✓ Branch 5 taken 916 times.
✓ Branch 6 taken 829 times.
|
1745 | for (const GenericType &genericTemplateType : spiceFunc.templateTypes) { |
51 |
3/6✓ Branch 1 taken 916 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 916 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 916 times.
|
916 | assert(spiceFunc.typeMapping.contains(genericTemplateType.getSubType())); |
52 |
2/4✓ Branch 1 taken 916 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 916 times.
✗ Branch 5 not taken.
|
916 | const QualType &actualType = spiceFunc.typeMapping.at(genericTemplateType.getSubType()); |
53 |
1/2✓ Branch 1 taken 916 times.
✗ Branch 2 not taken.
|
916 | mangleType(mangledName, actualType); |
54 | } | ||
55 |
1/2✓ Branch 1 taken 829 times.
✗ Branch 2 not taken.
|
829 | mangledName << "E"; |
56 | |||
57 | // Insert second end marker to end the nested type | ||
58 |
2/2✓ Branch 0 taken 108 times.
✓ Branch 1 taken 721 times.
|
829 | if (spiceFunc.isMethod()) |
59 |
1/2✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
|
108 | mangledName << "E"; |
60 | |||
61 | // Return type | ||
62 |
2/2✓ Branch 0 taken 599 times.
✓ Branch 1 taken 230 times.
|
829 | if (spiceFunc.isFunction()) |
63 |
1/2✓ Branch 1 taken 599 times.
✗ Branch 2 not taken.
|
599 | mangleType(mangledName, spiceFunc.returnType); |
64 | else | ||
65 |
1/2✓ Branch 1 taken 230 times.
✗ Branch 2 not taken.
|
230 | mangledName << "v"; |
66 | |||
67 |
2/2✓ Branch 0 taken 11556 times.
✓ Branch 1 taken 3789 times.
|
15345 | } else if (spiceFunc.isMethod()) { |
68 |
1/2✓ Branch 1 taken 11556 times.
✗ Branch 2 not taken.
|
11556 | mangledName << "E"; |
69 | } | ||
70 | |||
71 | // Parameter types | ||
72 |
2/2✓ Branch 5 taken 14917 times.
✓ Branch 6 taken 16174 times.
|
31091 | for (const auto &[qualType, isOptional] : spiceFunc.paramList) { |
73 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14917 times.
|
14917 | assert(!isOptional); |
74 |
1/2✓ Branch 1 taken 14917 times.
✗ Branch 2 not taken.
|
14917 | mangleType(mangledName, qualType); |
75 | } | ||
76 |
2/2✓ Branch 1 taken 5773 times.
✓ Branch 2 taken 10401 times.
|
16174 | if (spiceFunc.paramList.empty()) |
77 |
1/2✓ Branch 1 taken 5773 times.
✗ Branch 2 not taken.
|
5773 | mangledName << "v"; |
78 | |||
79 | #ifndef NDEBUG | ||
80 | 16174 | const TypeMapping &typeMapping = spiceFunc.typeMapping; | |
81 |
2/4✓ Branch 1 taken 16174 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16174 times.
✗ Branch 5 not taken.
|
16174 | const bool returnTypeIsFctOrProc = spiceFunc.returnType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE}); |
82 |
2/4✓ Branch 1 taken 14879 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14879 times.
✗ Branch 5 not taken.
|
14879 | const auto paramPredicate = [](const Param &p) { return p.qualType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE}); }; |
83 |
1/2✓ Branch 1 taken 16174 times.
✗ Branch 2 not taken.
|
16174 | const bool paramTypeIsFctOrProc = std::ranges::any_of(spiceFunc.paramList, paramPredicate); |
84 | 5029 | const auto templateTypePredicate = [&](const GenericType &t) { | |
85 |
4/8✓ Branch 1 taken 5029 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5029 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5029 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 5029 times.
✗ Branch 11 not taken.
|
5029 | return typeMapping.at(t.getSubType()).getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE}); |
86 | 16174 | }; | |
87 |
1/2✓ Branch 1 taken 16174 times.
✗ Branch 2 not taken.
|
16174 | const bool templateTypeIsFctOrProc = std::ranges::any_of(spiceFunc.templateTypes, templateTypePredicate); |
88 |
6/6✓ Branch 0 taken 16172 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 16102 times.
✓ Branch 3 taken 70 times.
✓ Branch 4 taken 16084 times.
✓ Branch 5 taken 18 times.
|
16174 | if (!returnTypeIsFctOrProc && !paramTypeIsFctOrProc && !templateTypeIsFctOrProc) |
89 |
3/6✓ Branch 1 taken 16084 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16084 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 16084 times.
|
16084 | assert(CommonUtil::isValidMangledName(mangledName.str())); |
90 | #endif | ||
91 | |||
92 |
1/2✓ Branch 1 taken 16174 times.
✗ Branch 2 not taken.
|
16174 | return mangledName.str(); |
93 | 16174 | } | |
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 | 1200 | 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 | 142 | 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 | 19407 | void NameMangling::mangleName(std::stringstream &out, const std::string &name, bool &nestedType) { | |
122 | 19407 | std::vector<std::string> fragments; | |
123 |
1/2✓ Branch 1 taken 19407 times.
✗ Branch 2 not taken.
|
19407 | std::istringstream ss(name); |
124 | 19407 | std::string token; | |
125 | |||
126 |
4/6✓ Branch 1 taken 38814 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38814 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 19407 times.
✓ Branch 7 taken 19407 times.
|
38814 | while (std::getline(ss, token, ':')) { |
127 |
1/2✓ Branch 1 taken 19407 times.
✗ Branch 2 not taken.
|
19407 | std::istringstream subStream(token); |
128 | 19407 | std::string subToken; | |
129 |
4/6✓ Branch 1 taken 38814 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38814 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 19407 times.
✓ Branch 7 taken 19407 times.
|
38814 | while (std::getline(subStream, subToken, '/')) { |
130 |
1/2✓ Branch 1 taken 19407 times.
✗ Branch 2 not taken.
|
19407 | fragments.push_back(subToken); |
131 | } | ||
132 | 19407 | } | |
133 | |||
134 | // Start a nested type if needed. The caller needs to emit the end marker. | ||
135 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 19407 times.
|
19407 | 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 4 taken 19407 times.
✓ Branch 5 taken 19407 times.
|
38814 | for (const std::string &fragment : fragments) { |
142 | 19407 | int fragmentLength = static_cast<int>(fragment.length()); | |
143 |
2/4✓ Branch 2 taken 19407 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 19407 times.
✗ Branch 6 not taken.
|
19407 | out << std::to_string(fragmentLength) << fragment; |
144 | } | ||
145 | 19407 | } | |
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 | 37511 | void NameMangling::mangleType(std::stringstream &out, QualType qualType) { // NOLINT(*-no-recursion) | |
156 | 37511 | const Type *type = qualType.getType(); | |
157 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 37511 times.
|
37511 | assert(!qualType.hasAnyGenericParts()); |
158 | |||
159 | // Unwrap qualType chain | ||
160 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 37511 times.
|
37511 | assert(!type->typeChain.empty()); |
161 |
2/2✓ Branch 1 taken 5424 times.
✓ Branch 2 taken 37511 times.
|
42935 | for (size_t i = type->typeChain.size() - 1; i >= 1; i--) |
162 | 5424 | mangleTypeChainElement(out, type->typeChain.at(i), false); | |
163 | |||
164 | // Specifiers | ||
165 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 37511 times.
|
37511 | assert(qualType.getSpecifiers().isSigned == !qualType.getSpecifiers().isUnsigned); |
166 | 37511 | const bool signedness = qualType.getSpecifiers().isSigned; | |
167 |
6/6✓ Branch 1 taken 4309 times.
✓ Branch 2 taken 33202 times.
✓ Branch 4 taken 3198 times.
✓ Branch 5 taken 1111 times.
✓ Branch 6 taken 3198 times.
✓ Branch 7 taken 34313 times.
|
37511 | if (qualType.getSpecifiers().isConst && type->typeChain.size() > 1) |
168 | 3198 | out << "K"; | |
169 | |||
170 | // Base chain element | ||
171 | 37511 | mangleTypeChainElement(out, type->typeChain.front(), signedness); | |
172 | 37511 | } | |
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 | 42935 | void NameMangling::mangleTypeChainElement(std::stringstream &out, const TypeChainElement &chainElement, bool signedness) { | |
183 |
13/15✓ Branch 0 taken 1330 times.
✓ Branch 1 taken 4094 times.
✓ Branch 2 taken 242 times.
✓ Branch 3 taken 4109 times.
✓ Branch 4 taken 189 times.
✓ Branch 5 taken 2839 times.
✓ Branch 6 taken 1097 times.
✓ Branch 7 taken 1520 times.
✓ Branch 8 taken 7567 times.
✓ Branch 9 taken 446 times.
✓ Branch 10 taken 19407 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 10 times.
✓ Branch 13 taken 85 times.
✗ Branch 14 not taken.
|
42935 | switch (chainElement.superType) { |
184 | 1330 | case TY_PTR: // fall-through | |
185 | case TY_ARRAY: | ||
186 | 1330 | out << "P"; | |
187 | 1330 | break; | |
188 | 4094 | case TY_REF: | |
189 | 4094 | out << "R"; | |
190 | 4094 | break; | |
191 | 242 | case TY_DOUBLE: | |
192 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 242 times.
|
242 | assert(signedness && "Unsigned double types are forbidden"); |
193 | 242 | out << "d"; | |
194 | 242 | break; | |
195 | 4109 | case TY_INT: | |
196 |
2/2✓ Branch 0 taken 3772 times.
✓ Branch 1 taken 337 times.
|
4109 | out << (signedness ? "i" : "j"); |
197 | 4109 | break; | |
198 | 189 | case TY_SHORT: | |
199 |
2/2✓ Branch 0 taken 166 times.
✓ Branch 1 taken 23 times.
|
189 | out << (signedness ? "s" : "t"); |
200 | 189 | break; | |
201 | 2839 | case TY_LONG: | |
202 |
2/2✓ Branch 0 taken 496 times.
✓ Branch 1 taken 2343 times.
|
2839 | out << (signedness ? "l" : "m"); |
203 | 2839 | break; | |
204 | 1097 | case TY_BYTE: | |
205 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1097 times.
|
1097 | out << (signedness ? "a" : "h"); |
206 | 1097 | break; | |
207 | 1520 | case TY_CHAR: | |
208 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1520 times.
|
1520 | out << (signedness ? "c" : "h"); |
209 | 1520 | break; | |
210 | 7567 | case TY_STRING: | |
211 | 7567 | out << "PKc"; | |
212 | 7567 | break; | |
213 | 446 | case TY_BOOL: | |
214 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 446 times.
|
446 | assert(!signedness && "Signed bool types are forbidden"); |
215 | 446 | out << "b"; | |
216 | 446 | break; | |
217 | 19407 | case TY_STRUCT: // fall-through | |
218 | case TY_INTERFACE: { | ||
219 | 19407 | bool nestedType = false; | |
220 |
1/2✓ Branch 1 taken 19407 times.
✗ Branch 2 not taken.
|
19407 | mangleName(out, chainElement.subType, nestedType); |
221 |
2/2✓ Branch 1 taken 6710 times.
✓ Branch 2 taken 12697 times.
|
19407 | if (!chainElement.templateTypes.empty()) { |
222 |
1/2✓ Branch 1 taken 6710 times.
✗ Branch 2 not taken.
|
6710 | out << "I"; |
223 |
2/2✓ Branch 5 taken 8254 times.
✓ Branch 6 taken 6710 times.
|
14964 | for (const QualType &templateType : chainElement.templateTypes) |
224 |
1/2✓ Branch 1 taken 8254 times.
✗ Branch 2 not taken.
|
8254 | mangleType(out, templateType); |
225 |
1/2✓ Branch 1 taken 6710 times.
✗ Branch 2 not taken.
|
6710 | out << "E"; |
226 | } | ||
227 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19407 times.
|
19407 | if (nestedType) |
228 | ✗ | out << "E"; | |
229 | 19407 | break; | |
230 | } | ||
231 | ✗ | case TY_ENUM: { | |
232 | ✗ | bool nestedType = false; | |
233 | ✗ | mangleName(out, chainElement.subType, nestedType); | |
234 | ✗ | if (nestedType) | |
235 | ✗ | out << "E"; | |
236 | ✗ | break; | |
237 | } | ||
238 | 10 | case TY_FUNCTION: { | |
239 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8 times.
|
10 | out << (chainElement.data.hasCaptures ? "PFC" : "PF"); |
240 |
2/2✓ Branch 5 taken 22 times.
✓ Branch 6 taken 10 times.
|
32 | for (const QualType ¶mType : chainElement.paramTypes) |
241 |
1/2✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
|
22 | mangleType(out, paramType); |
242 | 10 | out << "E"; | |
243 | 10 | break; | |
244 | } | ||
245 | 85 | case TY_PROCEDURE: { | |
246 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 81 times.
|
85 | out << (chainElement.data.hasCaptures ? "PFCv" : "PFv"); |
247 |
2/2✓ Branch 1 taken 38 times.
✓ Branch 2 taken 85 times.
|
123 | for (size_t i = 1; i < chainElement.paramTypes.size(); i++) |
248 | 38 | mangleType(out, chainElement.paramTypes.at(i)); | |
249 | 85 | out << "E"; | |
250 | 85 | break; | |
251 | } | ||
252 | − | default: // GCOV_EXCL_LINE | |
253 | − | throw CompilerError(INTERNAL_ERROR, "Type " + chainElement.getName(false) + " cannot be mangled"); // GCOV_EXCL_LINE | |
254 | } | ||
255 | 42935 | } | |
256 | |||
257 | 249 | std::string NameMangling::mangleTypeInfoName(const StructBase *structBase) { | |
258 |
1/2✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
|
249 | std::stringstream out; |
259 |
1/2✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
|
249 | out << "_ZTS"; |
260 |
2/4✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 249 times.
✗ Branch 5 not taken.
|
249 | mangleType(out, structBase->entry->getQualType()); |
261 |
1/2✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
|
498 | return out.str(); |
262 | 249 | } | |
263 | |||
264 |
2/4✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 249 times.
✗ Branch 6 not taken.
|
249 | std::string NameMangling::mangleTypeInfoValue(const std::string &value) { return std::to_string(value.size()) + value; } |
265 | |||
266 | 354 | std::string NameMangling::mangleTypeInfo(const StructBase *structBase) { | |
267 |
1/2✓ Branch 1 taken 354 times.
✗ Branch 2 not taken.
|
354 | std::stringstream out; |
268 |
1/2✓ Branch 1 taken 354 times.
✗ Branch 2 not taken.
|
354 | out << "_ZTI"; |
269 |
2/4✓ Branch 1 taken 354 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 354 times.
✗ Branch 5 not taken.
|
354 | mangleType(out, structBase->entry->getQualType()); |
270 |
1/2✓ Branch 1 taken 354 times.
✗ Branch 2 not taken.
|
708 | return out.str(); |
271 | 354 | } | |
272 | |||
273 | 498 | std::string NameMangling::mangleVTable(const StructBase *structBase) { | |
274 |
1/2✓ Branch 1 taken 498 times.
✗ Branch 2 not taken.
|
498 | std::stringstream out; |
275 |
1/2✓ Branch 1 taken 498 times.
✗ Branch 2 not taken.
|
498 | out << "_ZTV"; |
276 |
2/4✓ Branch 1 taken 498 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 498 times.
✗ Branch 5 not taken.
|
498 | mangleType(out, structBase->entry->getQualType()); |
277 |
1/2✓ Branch 1 taken 498 times.
✗ Branch 2 not taken.
|
996 | return out.str(); |
278 | 498 | } | |
279 | |||
280 | 213 | std::string NameMangling::mangleVTable(const std::string &typeName) { | |
281 |
3/6✓ Branch 2 taken 213 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 213 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 213 times.
✗ Branch 9 not taken.
|
213 | return "_ZTV" + std::to_string(typeName.size()) + typeName; |
282 | } | ||
283 | |||
284 | } // namespace spice::compiler | ||
285 |