blob: 7b03bd14057b6991184691760a0c704cbaefc74c [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();
60 if (name_pos + name_len > str.size()) {
61 // 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()) {
291 if (arg_type == last_arg_type) {
292 mangled_name += "S_";
293 } else {
294 mangled_name += GetMangledTypeName(arg_type);
295 last_arg_type = arg_type;
296 }
297 }
298 } else {
299 mangled_name += GetMangledTypeName(type);
SJW173c7e92020-03-16 08:44:47 -0500300 }
SJW61531372020-06-09 07:31:08 -0500301 return mangled_name;
alan-baker75090e42020-02-20 11:21:04 -0500302}
303
SJW61531372020-06-09 07:31:08 -0500304// The mangling follows the Itanium convention.
305std::string Builtins::GetMangledFunctionName(const char *name) {
306 assert(name);
307 return std::string("_Z") + std::to_string(strlen(name)) + name;
308}
309
Marco Antogninid9ccec72020-12-01 16:52:34 +0000310std::string
311Builtins::GetMangledFunctionName(const Builtins::FunctionInfo &info) {
312 // This is a best-effort attempt at reconstructing the mangled name for the
313 // given function. Because demangling is a lossy process some information may
314 // be lost and is therefore no longer available.
315 std::string name;
316 raw_string_ostream out(name);
317
318 StringRef function_name = info.getName();
319 out << "_Z" << function_name.size() << function_name;
320
321 for (size_t i = 0; i < info.getParameterCount(); ++i) {
322 const auto &param = info.getParameter(i);
323
324 if (param.vector_size != 0) {
325 out << "Dv" << param.vector_size << '_';
326 }
327
328 switch (param.type_id) {
329 case Type::FloatTyID:
330 switch (param.byte_len) {
331 case 2:
332 out << "Dh";
333 break;
334 case 4:
335 out << "f";
336 break;
337 case 8:
338 out << "d";
339 break;
340 default:
341 llvm_unreachable("Invalid byte_len for floating point type.");
342 break;
343 }
344 break;
345
346 case Type::IntegerTyID:
347 if (param.is_signed) {
348 switch (param.byte_len) {
349 case 1:
350 // Not enough information to distinguish between char (c) and signed
351 // char (a).
352 out << 'c';
353 break;
354 case 2:
355 out << "s";
356 break;
357 case 4:
358 out << "i";
359 break;
360 case 8:
361 out << "l";
362 break;
363 default:
364 llvm_unreachable("Invalid byte_len for signed integer type.");
365 break;
366 }
367 } else {
368 switch (param.byte_len) {
369 case 1:
370 out << 'h';
371 break;
372 case 2:
373 out << "t";
374 break;
375 case 4:
376 out << "j";
377 break;
378 case 8:
379 out << "m";
380 break;
381 default:
382 llvm_unreachable("Invalid byte_len for unsigned integer type.");
383 break;
384 }
385 }
386 break;
387
388 case Type::StructTyID:
389 out << param.name.size() << param.name;
390 break;
391
392 default:
393 llvm_unreachable("Unsupported type id");
394 break;
395 }
396 }
397
398 out.flush();
399 return name;
400}
401
SJW61531372020-06-09 07:31:08 -0500402// The mangling loosely follows the Itanium convention.
403// Its purpose is solely to ensure uniqueness of names, it is not
404// meant to convey type information.
405std::string Builtins::GetMangledTypeName(Type *Ty) {
406 std::string mangled_type_str;
407
408 switch (Ty->getTypeID()) {
409 case Type::VoidTyID:
410 return "v";
411 case Type::HalfTyID:
412 return "Dh";
413 case Type::FloatTyID:
414 return "f";
415 case Type::DoubleTyID:
416 return "d";
417
418 case Type::IntegerTyID:
419 switch (Ty->getIntegerBitWidth()) {
420 case 1:
421 return "b";
422 case 8:
423 return "h";
424 case 16:
425 return "t";
426 case 32:
427 return "j";
428 case 64:
429 return "m";
430 default:
431 assert(0);
432 break;
433 }
434 break;
435
436 case Type::StructTyID: {
437 auto *StrTy = cast<StructType>(Ty);
438 if (StrTy->isLiteral()) {
439 assert(StrTy->getNumElements() == 1);
440 return GetMangledTypeName(StrTy->getElementType(0));
441 }
442 mangled_type_str =
443 std::to_string(Ty->getStructName().size()) + Ty->getStructName().str();
444 break;
SJW173c7e92020-03-16 08:44:47 -0500445 }
SJW61531372020-06-09 07:31:08 -0500446 case Type::ArrayTyID:
447 mangled_type_str = "P" + GetMangledTypeName(Ty->getArrayElementType());
448 break;
449 case Type::PointerTyID: {
450 mangled_type_str = "P";
451 auto AS = Ty->getPointerAddressSpace();
452 if (AS != 0) {
453 std::string AS_name = "AS" + std::to_string(AS);
454 mangled_type_str += "U" + std::to_string(AS_name.size()) + AS_name;
455 }
456 mangled_type_str += GetMangledTypeName(Ty->getPointerElementType());
457 break;
SJW173c7e92020-03-16 08:44:47 -0500458 }
SJW61531372020-06-09 07:31:08 -0500459 case Type::FixedVectorTyID: {
460 auto VecTy = cast<VectorType>(Ty);
alan-baker5a8c3be2020-09-09 13:44:26 -0400461 mangled_type_str =
462 "Dv" + std::to_string(VecTy->getElementCount().getKnownMinValue()) +
463 "_" + GetMangledTypeName(VecTy->getElementType());
SJW61531372020-06-09 07:31:08 -0500464 break;
SJW173c7e92020-03-16 08:44:47 -0500465 }
alan-bakerf67468c2019-11-25 15:51:49 -0500466
SJW61531372020-06-09 07:31:08 -0500467 case Type::FunctionTyID:
468 case Type::X86_FP80TyID:
469 case Type::FP128TyID:
470 case Type::PPC_FP128TyID:
471 case Type::LabelTyID:
472 case Type::MetadataTyID:
473 case Type::X86_MMXTyID:
474 case Type::TokenTyID:
475 default:
476 assert(0);
477 break;
SJW173c7e92020-03-16 08:44:47 -0500478 }
SJW61531372020-06-09 07:31:08 -0500479 return mangled_type_str;
alan-bakerf67468c2019-11-25 15:51:49 -0500480}