blob: bcc7b32dd0a86b6cb1daa6f0d682fe2564469286 [file] [log] [blame]
alan-bakerf67468c2019-11-25 15:51:49 -05001// Copyright 2019 The Clspv Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Marco Antogninid9ccec72020-12-01 16:52:34 +000015#include "llvm/Support/raw_ostream.h"
16
alan-bakerf67468c2019-11-25 15:51:49 -050017#include "Builtins.h"
18
SJW173c7e92020-03-16 08:44:47 -050019#include <cstdlib>
20#include <unordered_map>
21
alan-bakerf67468c2019-11-25 15:51:49 -050022using namespace llvm;
SJW173c7e92020-03-16 08:44:47 -050023using namespace clspv;
alan-bakerf67468c2019-11-25 15:51:49 -050024
SJW173c7e92020-03-16 08:44:47 -050025namespace {
26////////////////////////////////////////////////////////////////////////////////
27//// Convert Builtin function name to a Type enum
28////////////////////////////////////////////////////////////////////////////////
29Builtins::BuiltinType LookupBuiltinType(const std::string &name) {
30
31// Build static map of builtin function names
32#include "BuiltinsMap.inc"
33
34 auto ii = s_func_map.find(name.c_str());
35 if (ii != s_func_map.end()) {
36 return (*ii).second;
37 }
38 return Builtins::kBuiltinNone;
alan-bakerf67468c2019-11-25 15:51:49 -050039}
40
SJW173c7e92020-03-16 08:44:47 -050041////////////////////////////////////////////////////////////////////////////////
42// Mangled name parsing utilities
43// - We only handle Itanium-style C++ mangling, plus modifications for OpenCL.
44////////////////////////////////////////////////////////////////////////////////
45
46// Given a mangled name starting at character position |pos| in |str|, extracts
47// the original name (without mangling) and updates |pos| so it will index the
48// character just past that original name (which might be just past the end of
49// the string). If the mangling is invalid, then an empty string is returned,
50// and |pos| is not updated. Example: if str = "_Z3fooi", and *pos = 2, then
51// returns "foo" and adds 4 to *pos.
52std::string GetUnmangledName(const std::string &str, size_t *pos) {
53 char *end = nullptr;
54 assert(*pos < str.size());
55 auto name_len = strtol(&str[*pos], &end, 10);
56 if (!name_len) {
57 return "";
58 }
59 ptrdiff_t name_pos = end - str.data();
Marco Antognini7e338402021-03-15 12:48:37 +000060 if (static_cast<std::size_t>(name_pos + name_len) > str.size()) {
SJW173c7e92020-03-16 08:44:47 -050061 // Protect against maliciously large number.
62 return "";
63 }
64
65 *pos = name_pos + name_len;
66 return str.substr(size_t(name_pos), name_len);
alan-bakerf67468c2019-11-25 15:51:49 -050067}
68
SJW173c7e92020-03-16 08:44:47 -050069// Capture parameter type and qualifiers starting at |pos|
70// - return new parsing position pos, or zero for error
71size_t GetParameterType(const std::string &mangled_name,
72 clspv::Builtins::ParamTypeInfo *type_info, size_t pos) {
73 // Parse a parameter type encoding
74 char type_code = mangled_name[pos++];
75
76 switch (type_code) {
77 // qualifiers
78 case 'P': // Pointer
79 case 'R': // Reference
80 return GetParameterType(mangled_name, type_info, pos);
81 case 'k': // ??? not part of cxxabi
82 case 'K': // const
83 case 'V': // volatile
84 return GetParameterType(mangled_name, type_info, pos);
85 case 'U': { // Address space
86 // address_space name not captured
87 (void)GetUnmangledName(mangled_name, &pos);
88 return GetParameterType(mangled_name, type_info, pos);
89 }
90 // OCL types
91 case 'D':
92 type_code = mangled_name[pos++];
93 if (type_code == 'v') { // OCL vector
94 char *end = nullptr;
95 int numElems = strtol(&mangled_name[pos], &end, 10);
96 if (!numElems) {
97 return 0;
98 }
99 type_info->vector_size = numElems;
100 pos = end - mangled_name.data();
101 if (pos > mangled_name.size()) {
102 // Protect against maliciously large number.
103 return 0;
104 }
105
106 if (mangled_name[pos++] != '_') {
107 return 0;
108 }
109 return GetParameterType(mangled_name, type_info, pos);
110 } else if (type_code == 'h') { // OCL half
111 type_info->type_id = Type::FloatTyID;
112 type_info->is_signed = true;
113 type_info->byte_len = 2;
SJW2c317da2020-03-23 07:39:13 -0500114 return pos;
SJW173c7e92020-03-16 08:44:47 -0500115 } else {
116#ifdef DEBUG
117 llvm::outs() << "Func: " << mangled_name << "\n";
118 llvm_unreachable("failed to demangle name");
119#endif
120 return 0;
121 }
122 break;
123
124 // element types
125 case 'l': // long
126 case 'i': // int
127 case 's': // short
128 case 'c': // char
129 case 'a': // signed char
130 type_info->type_id = Type::IntegerTyID;
131 type_info->is_signed = true;
132 break;
133 case 'm': // unsigned long
134 case 'j': // unsigned int
135 case 't': // unsigned short
136 case 'h': // unsigned char
137 type_info->type_id = Type::IntegerTyID;
138 type_info->is_signed = false;
139 break;
140 case 'd': // double float
141 case 'f': // single float
142 type_info->type_id = Type::FloatTyID;
143 type_info->is_signed = true;
144 break;
145 case 'v': // void
146 break;
147 case '1': // struct name
148 case '2': // - a <positive length number> for size of the following name
149 case '3': // - e.g. struct Foobar {} - would be encoded as '6Foobar'
150 case '4': // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.unqualified-name
151 case '5':
152 case '6':
153 case '7':
154 case '8':
155 case '9':
156 type_info->type_id = Type::StructTyID;
157 pos--;
158 type_info->name = GetUnmangledName(mangled_name, &pos);
159 break;
160 case '.':
161 return 0;
162 default:
163#ifdef DEBUG
164 llvm::outs() << "Func: " << mangled_name << "\n";
165 llvm_unreachable("failed to demangle name");
166#endif
167 return 0;
168 }
169
170 switch (type_code) {
171 // element types
172 case 'l': // long
173 case 'm': // unsigned long
174 case 'd': // double float
175 type_info->byte_len = 8;
176 break;
177 case 'i': // int
178 case 'j': // unsigned int
179 case 'f': // single float
180 type_info->byte_len = 4;
181 break;
182 case 's': // short
183 case 't': // unsigned short
184 type_info->byte_len = 2;
185 break;
186 case 'c': // char
187 case 'a': // signed char
188 case 'h': // unsigned char
189 type_info->byte_len = 1;
190 break;
191 default:
192 break;
193 }
194 return pos;
195}
196} // namespace
197
198////////////////////////////////////////////////////////////////////////////////
199// FunctionInfo::GetFromMangledNameCheck
200// - parse mangled name as far as possible. Some names are an aggregate of
201// fields separated by '.'
202// - extract name and parameter types, and return type for 'convert' functions
203// - return true if the mangled name can be fully parsed
204bool Builtins::FunctionInfo::GetFromMangledNameCheck(
205 const std::string &mangled_name) {
206 size_t pos = 0;
207 if (!(mangled_name[pos++] == '_' && mangled_name[pos++] == 'Z')) {
208 name_ = mangled_name;
209 return false;
210 }
211
212 name_ = GetUnmangledName(mangled_name, &pos);
213 if (name_.empty()) {
214 return false;
215 }
216
SJW173c7e92020-03-16 08:44:47 -0500217 auto mangled_name_len = mangled_name.size();
218 while (pos < mangled_name_len) {
219 ParamTypeInfo type_info;
220 if (mangled_name[pos] == 'S') {
221 // handle duplicate param_type
222 if (mangled_name[pos + 1] != '_') {
223 return false;
224 }
225 pos += 2;
226 if (params_.empty()) {
227 return false;
228 }
229 params_.push_back(params_.back());
alan-bakeradf44262020-03-16 11:17:21 -0400230 } else if ((pos = GetParameterType(mangled_name, &type_info, pos))) {
SJW173c7e92020-03-16 08:44:47 -0500231 params_.push_back(type_info);
232 } else {
233 return false;
234 }
235 }
236
237 return true;
alan-bakerf67468c2019-11-25 15:51:49 -0500238}
239
SJW173c7e92020-03-16 08:44:47 -0500240////////////////////////////////////////////////////////////////////////////////
241// FunctionInfo ctor - parses mangled name
242Builtins::FunctionInfo::FunctionInfo(const std::string &mangled_name) {
243 is_valid_ = GetFromMangledNameCheck(mangled_name);
244 type_ = LookupBuiltinType(name_);
SJW61531372020-06-09 07:31:08 -0500245 if (type_ == kConvert) {
246 // deduce return type from name, only for convert
247 char tok = name_[8];
248 return_type_.is_signed = tok != 'u'; // unsigned
249 return_type_.type_id = tok == 'f' ? Type::FloatTyID : Type::IntegerTyID;
250 }
alan-bakerf67468c2019-11-25 15:51:49 -0500251}
252
SJW173c7e92020-03-16 08:44:47 -0500253// get const ParamTypeInfo for nth parameter
254const Builtins::ParamTypeInfo &
255Builtins::FunctionInfo::getParameter(size_t _arg) const {
256 assert(params_.size() > _arg);
257 return params_[_arg];
alan-bakerf67468c2019-11-25 15:51:49 -0500258}
259
Marco Antogninid9ccec72020-12-01 16:52:34 +0000260Builtins::ParamTypeInfo &Builtins::FunctionInfo::getParameter(size_t _arg) {
261 assert(params_.size() > _arg);
262 return params_[_arg];
263}
264
SJW2c317da2020-03-23 07:39:13 -0500265// Test for OCL Sampler parameter type
266bool Builtins::ParamTypeInfo::isSampler() const {
SJW61531372020-06-09 07:31:08 -0500267 return type_id == Type::StructTyID &&
268 (name == "ocl_sampler" || name == "opencl.sampler_t");
SJW2c317da2020-03-23 07:39:13 -0500269}
270
SJW173c7e92020-03-16 08:44:47 -0500271////////////////////////////////////////////////////////////////////////////////
272//// Lookup interface
273//// - only demangle once for any name encountered
274////////////////////////////////////////////////////////////////////////////////
275const Builtins::FunctionInfo &
276Builtins::Lookup(const std::string &mangled_name) {
277 static std::unordered_map<std::string, FunctionInfo> s_mangled_map;
278 auto fi = s_mangled_map.emplace(mangled_name, mangled_name);
279 return (*fi.first).second;
alan-baker75090e42020-02-20 11:21:04 -0500280}
281
SJW173c7e92020-03-16 08:44:47 -0500282////////////////////////////////////////////////////////////////////////////////
SJW61531372020-06-09 07:31:08 -0500283// Generate a mangled name loosely based on Itanium mangling
284std::string Builtins::GetMangledFunctionName(const char *name, Type *type) {
285 assert(name);
286 std::string mangled_name =
287 std::string("_Z") + std::to_string(strlen(name)) + name;
288 if (auto *func_type = dyn_cast<FunctionType>(type)) {
289 Type *last_arg_type = nullptr;
290 for (auto *arg_type : func_type->params()) {
alan-baker5f2e88e2020-12-07 15:24:04 -0500291 std::string arg_name = GetMangledTypeName(arg_type);
292 if (arg_name.size() > 1 && arg_type == last_arg_type) {
SJW61531372020-06-09 07:31:08 -0500293 mangled_name += "S_";
294 } else {
295 mangled_name += GetMangledTypeName(arg_type);
296 last_arg_type = arg_type;
297 }
298 }
299 } else {
300 mangled_name += GetMangledTypeName(type);
SJW173c7e92020-03-16 08:44:47 -0500301 }
SJW61531372020-06-09 07:31:08 -0500302 return mangled_name;
alan-baker75090e42020-02-20 11:21:04 -0500303}
304
SJW61531372020-06-09 07:31:08 -0500305// The mangling follows the Itanium convention.
306std::string Builtins::GetMangledFunctionName(const char *name) {
307 assert(name);
308 return std::string("_Z") + std::to_string(strlen(name)) + name;
309}
310
Marco Antogninid9ccec72020-12-01 16:52:34 +0000311std::string
312Builtins::GetMangledFunctionName(const Builtins::FunctionInfo &info) {
313 // This is a best-effort attempt at reconstructing the mangled name for the
314 // given function. Because demangling is a lossy process some information may
315 // be lost and is therefore no longer available.
316 std::string name;
317 raw_string_ostream out(name);
318
319 StringRef function_name = info.getName();
320 out << "_Z" << function_name.size() << function_name;
321
322 for (size_t i = 0; i < info.getParameterCount(); ++i) {
323 const auto &param = info.getParameter(i);
324
325 if (param.vector_size != 0) {
326 out << "Dv" << param.vector_size << '_';
327 }
328
329 switch (param.type_id) {
330 case Type::FloatTyID:
331 switch (param.byte_len) {
332 case 2:
333 out << "Dh";
334 break;
335 case 4:
336 out << "f";
337 break;
338 case 8:
339 out << "d";
340 break;
341 default:
342 llvm_unreachable("Invalid byte_len for floating point type.");
343 break;
344 }
345 break;
346
347 case Type::IntegerTyID:
348 if (param.is_signed) {
349 switch (param.byte_len) {
350 case 1:
351 // Not enough information to distinguish between char (c) and signed
352 // char (a).
353 out << 'c';
354 break;
355 case 2:
356 out << "s";
357 break;
358 case 4:
359 out << "i";
360 break;
361 case 8:
362 out << "l";
363 break;
364 default:
365 llvm_unreachable("Invalid byte_len for signed integer type.");
366 break;
367 }
368 } else {
369 switch (param.byte_len) {
370 case 1:
371 out << 'h';
372 break;
373 case 2:
374 out << "t";
375 break;
376 case 4:
377 out << "j";
378 break;
379 case 8:
380 out << "m";
381 break;
382 default:
383 llvm_unreachable("Invalid byte_len for unsigned integer type.");
384 break;
385 }
386 }
387 break;
388
389 case Type::StructTyID:
390 out << param.name.size() << param.name;
391 break;
392
393 default:
394 llvm_unreachable("Unsupported type id");
395 break;
396 }
397 }
398
399 out.flush();
400 return name;
401}
402
SJW61531372020-06-09 07:31:08 -0500403// The mangling loosely follows the Itanium convention.
404// Its purpose is solely to ensure uniqueness of names, it is not
405// meant to convey type information.
406std::string Builtins::GetMangledTypeName(Type *Ty) {
407 std::string mangled_type_str;
408
409 switch (Ty->getTypeID()) {
410 case Type::VoidTyID:
411 return "v";
412 case Type::HalfTyID:
413 return "Dh";
414 case Type::FloatTyID:
415 return "f";
416 case Type::DoubleTyID:
417 return "d";
418
419 case Type::IntegerTyID:
420 switch (Ty->getIntegerBitWidth()) {
421 case 1:
422 return "b";
423 case 8:
424 return "h";
425 case 16:
426 return "t";
427 case 32:
428 return "j";
429 case 64:
430 return "m";
431 default:
432 assert(0);
433 break;
434 }
435 break;
436
437 case Type::StructTyID: {
438 auto *StrTy = cast<StructType>(Ty);
439 if (StrTy->isLiteral()) {
440 assert(StrTy->getNumElements() == 1);
441 return GetMangledTypeName(StrTy->getElementType(0));
442 }
443 mangled_type_str =
444 std::to_string(Ty->getStructName().size()) + Ty->getStructName().str();
445 break;
SJW173c7e92020-03-16 08:44:47 -0500446 }
SJW61531372020-06-09 07:31:08 -0500447 case Type::ArrayTyID:
448 mangled_type_str = "P" + GetMangledTypeName(Ty->getArrayElementType());
449 break;
450 case Type::PointerTyID: {
451 mangled_type_str = "P";
452 auto AS = Ty->getPointerAddressSpace();
453 if (AS != 0) {
454 std::string AS_name = "AS" + std::to_string(AS);
455 mangled_type_str += "U" + std::to_string(AS_name.size()) + AS_name;
456 }
457 mangled_type_str += GetMangledTypeName(Ty->getPointerElementType());
458 break;
SJW173c7e92020-03-16 08:44:47 -0500459 }
SJW61531372020-06-09 07:31:08 -0500460 case Type::FixedVectorTyID: {
461 auto VecTy = cast<VectorType>(Ty);
alan-baker5a8c3be2020-09-09 13:44:26 -0400462 mangled_type_str =
463 "Dv" + std::to_string(VecTy->getElementCount().getKnownMinValue()) +
464 "_" + GetMangledTypeName(VecTy->getElementType());
SJW61531372020-06-09 07:31:08 -0500465 break;
SJW173c7e92020-03-16 08:44:47 -0500466 }
alan-bakerf67468c2019-11-25 15:51:49 -0500467
SJW61531372020-06-09 07:31:08 -0500468 case Type::FunctionTyID:
469 case Type::X86_FP80TyID:
470 case Type::FP128TyID:
471 case Type::PPC_FP128TyID:
472 case Type::LabelTyID:
473 case Type::MetadataTyID:
474 case Type::X86_MMXTyID:
475 case Type::TokenTyID:
476 default:
477 assert(0);
478 break;
SJW173c7e92020-03-16 08:44:47 -0500479 }
SJW61531372020-06-09 07:31:08 -0500480 return mangled_type_str;
alan-bakerf67468c2019-11-25 15:51:49 -0500481}