• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   * Copyright (C) 2015 The Android Open Source Project
3   *
4   * Licensed under the Apache License, Version 2.0 (the "License");
5   * you may not use this file except in compliance with the License.
6   * You may obtain a copy of the License at
7   *
8   *      http://www.apache.org/licenses/LICENSE-2.0
9   *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  #include <algorithm>
18  #include <climits>
19  #include <iostream>
20  #include <iterator>
21  #include <sstream>
22  
23  #include "Generator.h"
24  #include "Specification.h"
25  #include "Utilities.h"
26  
27  using namespace std;
28  
29  const unsigned int kMinimumApiLevelForTests = 11;
30  const unsigned int kApiLevelWithFirst64Bit = 21;
31  
32  // Used to map the built-in types to their mangled representations
33  struct BuiltInMangling {
34      const char* token[3];     // The last two entries can be nullptr
35      const char* equivalence;  // The mangled equivalent
36  };
37  
38  BuiltInMangling builtInMangling[] = {
39              {{"long", "long"}, "x"},
40              {{"unsigned", "long", "long"}, "y"},
41              {{"long"}, "l"},
42              {{"unsigned", "long"}, "m"},
43              {{"int"}, "i"},
44              {{"unsigned", "int"}, "j"},
45              {{"short"}, "s"},
46              {{"unsigned", "short"}, "t"},
47              {{"char"}, "c"},
48              {{"unsigned", "char"}, "h"},
49              {{"signed", "char"}, "a"},
50              {{"void"}, "v"},
51              {{"wchar_t"}, "w"},
52              {{"bool"}, "b"},
53              {{"__fp16"}, "Dh"},
54              {{"float"}, "f"},
55              {{"double"}, "d"},
56  };
57  
58  /* For the given API level and bitness (e.g. 32 or 64 bit), try to find a
59   * substitution for the provided type name, as would be done (mostly) by a
60   * preprocessor.  Returns empty string if there's no substitution.
61   */
findSubstitute(const string & typeName,unsigned int apiLevel,int intSize)62  static string findSubstitute(const string& typeName, unsigned int apiLevel, int intSize) {
63      const auto& types = systemSpecification.getTypes();
64      const auto type = types.find(typeName);
65      if (type != types.end()) {
66          for (TypeSpecification* spec : type->second->getSpecifications()) {
67              // Verify this specification applies
68              const VersionInfo info = spec->getVersionInfo();
69              if (!info.includesVersion(apiLevel) || (info.intSize != 0 && info.intSize != intSize)) {
70                  continue;
71              }
72              switch (spec->getKind()) {
73                  case SIMPLE: {
74                      return spec->getSimpleType();
75                  }
76                  case RS_OBJECT: {
77                      // Do nothing for RS object types.
78                      break;
79                  }
80                  case STRUCT: {
81                      return spec->getStructName();
82                  }
83                  case ENUM:
84                      // Do nothing
85                      break;
86              }
87          }
88      }
89      return "";
90  }
91  
92  /* Expand the typedefs found in 'type' into their equivalents and tokenize
93   * the resulting list.  'apiLevel' and 'intSize' specifies the API level and bitness
94   * we are currently processing.
95   */
expandTypedefs(const string type,unsigned int apiLevel,int intSize,string & vectorSize)96  list<string> expandTypedefs(const string type, unsigned int apiLevel, int intSize, string& vectorSize) {
97      // Split the string in tokens.
98      istringstream stream(type);
99      list<string> tokens{istream_iterator<string>{stream}, istream_iterator<string>{}};
100      // Try to substitue each token.
101      for (auto i = tokens.begin(); i != tokens.end();) {
102          const string substitute = findSubstitute(*i, apiLevel, intSize);
103          if (substitute.empty()) {
104              // No substitution possible, just go to the next token.
105              i++;
106          } else {
107              // Split the replacement string in tokens.
108  
109              /* Get the new vector size. This is for the case of the type being for example
110               * rs_quaternion* == float4*, where we need the vector size to be 4 for the
111               * purposes of mangling, although the parameter itself is not determined to be
112               * a vector. */
113              string unused;
114              string newVectorSize;
115              getVectorSizeAndBaseType(*i, newVectorSize, unused);
116  
117              istringstream vectorSizeBuf(vectorSize);
118              int vectorSizeVal;
119              vectorSizeBuf >> vectorSizeVal;
120  
121              istringstream newVectorSizeBuf(newVectorSize);
122              int newVectorSizeVal;
123              newVectorSizeBuf >> newVectorSizeVal;
124  
125              if (newVectorSizeVal > vectorSizeVal)
126                  vectorSize = newVectorSize;
127  
128              istringstream stream(substitute);
129              list<string> newTokens{istream_iterator<string>{stream}, istream_iterator<string>{}};
130              // Replace the token with the substitution. Don't advance, as the new substitution
131              // might itself be replaced.
132              // hold previous node
133              auto prev = i;
134              // insert new nodes after node i
135              tokens.splice(++i, std::move(newTokens));
136              // remove previous node and set i to beginning of inserted nodes
137              i = tokens.erase(prev);
138          }
139      }
140      return tokens;
141  }
142  
143  // Remove the first element of the list if it equals 'prefix'.  Return true in that case.
eatFront(list<string> * tokens,const char * prefix)144  static bool eatFront(list<string>* tokens, const char* prefix) {
145      if (tokens->front() == prefix) {
146          tokens->pop_front();
147          return true;
148      }
149      return false;
150  }
151  
152  /* Search the table of translations for the built-ins for the mangling that
153   * corresponds to this list of tokens.  If a match is found, consume these tokens
154   * and return a pointer to the string.  If not, return nullptr.
155   */
findManglingOfBuiltInType(list<string> * tokens)156  static const char* findManglingOfBuiltInType(list<string>* tokens) {
157      for (const BuiltInMangling& a : builtInMangling) {
158          auto t = tokens->begin();
159          auto end = tokens->end();
160          bool match = true;
161          // We match up to three tokens.
162          for (int i = 0; i < 3; i++) {
163              if (!a.token[i]) {
164                  // No more tokens
165                  break;
166              }
167              if (t == end || *t++ != a.token[i]) {
168                  match = false;
169              }
170          }
171          if (match) {
172              tokens->erase(tokens->begin(), t);
173              return a.equivalence;
174          }
175      }
176      return nullptr;
177  }
178  
179  // Mangle a long name by prefixing it with its length, e.g. "13rs_allocation".
mangleLongName(const string & name)180  static inline string mangleLongName(const string& name) {
181      return to_string(name.size()) + name;
182  }
183  
184  /* Mangle the type name that's represented by the vector size and list of tokens.
185   * The mangling will be returned in full form in 'mangling'.  'compressedMangling'
186   * will have the compressed equivalent.  This is built using the 'previousManglings'
187   * list.  false is returned if an error is encountered.
188   *
189   * This function is recursive because compression is possible at each level of the definition.
190   * See http://mentorembedded.github.io/cxx-abi/abi.html#mangle.type for a description
191   * of the Itanium mangling used by llvm.
192   *
193   * This function mangles correctly the types currently used by RenderScript.  It does
194   * not currently mangle more complicated types like function pointers, namespaces,
195   * or other C++ types.  In particular, we don't deal correctly with parenthesis.
196   */
mangleType(string vectorSize,list<string> * tokens,vector<string> * previousManglings,string * mangling,string * compressedMangling)197  static bool mangleType(string vectorSize, list<string>* tokens, vector<string>* previousManglings,
198                         string* mangling, string* compressedMangling) {
199      string delta;                 // The part of the mangling we're generating for this recursion.
200      bool isTerminal = false;      // True if this iteration parses a terminal node in the production.
201      bool canBeCompressed = true;  // Will be false for manglings of builtins.
202  
203      if (tokens->back() == "*") {
204          delta = "P";
205          tokens->pop_back();
206      } else if (eatFront(tokens, "const")) {
207          delta = "K";
208      } else if (eatFront(tokens, "volatile")) {
209          delta = "V";
210      } else if (vectorSize != "1" && vectorSize != "") {
211          // For vector, prefix with the abbreviation for a vector, including the size.
212          delta = "Dv" + vectorSize + "_";
213          vectorSize.clear();  // Reset to mark the size as consumed.
214      } else if (eatFront(tokens, "struct")) {
215          // For a structure, we just use the structure name
216          if (tokens->size() == 0) {
217              cerr << "Expected a name after struct\n";
218              return false;
219          }
220          delta = mangleLongName(tokens->front());
221          isTerminal = true;
222          tokens->pop_front();
223      } else if (eatFront(tokens, "...")) {
224          delta = "z";
225          isTerminal = true;
226      } else {
227          const char* c = findManglingOfBuiltInType(tokens);
228          if (c) {
229              // It's a basic type.  We don't use those directly for compression.
230              delta = c;
231              isTerminal = true;
232              canBeCompressed = false;
233          } else if (tokens->size() > 0) {
234              // It's a complex type name.
235              delta = mangleLongName(tokens->front());
236              isTerminal = true;
237              tokens->pop_front();
238          }
239      }
240  
241      if (isTerminal) {
242          // If we're the terminal node, there should be nothing left to mangle.
243          if (tokens->size() > 0) {
244              cerr << "Expected nothing else but found";
245              for (const auto& t : *tokens) {
246                  cerr << " " << t;
247              }
248              cerr << "\n";
249              return false;
250          }
251          *mangling = delta;
252          *compressedMangling = delta;
253      } else {
254          // We're not terminal.  Recurse and prefix what we've translated this pass.
255          if (tokens->size() == 0) {
256              cerr << "Expected a more complete type\n";
257              return false;
258          }
259          string rest, compressedRest;
260          if (!mangleType(vectorSize, tokens, previousManglings, &rest, &compressedRest)) {
261              return false;
262          }
263          *mangling = delta + rest;
264          *compressedMangling = delta + compressedRest;
265      }
266  
267      /* If it's a built-in type, we don't look at previously emitted ones and we
268       * don't keep track of it.
269       */
270      if (!canBeCompressed) {
271          return true;
272      }
273  
274      // See if we've encountered this mangling before.
275      for (size_t i = 0; i < previousManglings->size(); ++i) {
276          if ((*previousManglings)[i] == *mangling) {
277              // We have a match, construct an index reference to that previously emitted mangling.
278              ostringstream stream2;
279              stream2 << 'S';
280              if (i > 0) {
281                  stream2 << (char)('0' + i - 1);
282              }
283              stream2 << '_';
284              *compressedMangling = stream2.str();
285              return true;
286          }
287      }
288  
289      // We have not encountered this before.  Add it to the list.
290      previousManglings->push_back(*mangling);
291      return true;
292  }
293  
294  // Write to the stream the mangled representation of each parameter.
writeParameters(ostringstream * stream,const std::vector<ParameterDefinition * > & params,unsigned int apiLevel,int intSize)295  static bool writeParameters(ostringstream* stream, const std::vector<ParameterDefinition*>& params,
296                              unsigned int apiLevel, int intSize) {
297      if (params.empty()) {
298          *stream << "v";
299          return true;
300      }
301      /* We keep track of the previously generated parameter types, as type mangling
302       * is compressed by reusing previous manglings.
303       */
304      vector<string> previousManglings;
305      for (ParameterDefinition* p : params) {
306          // Expand the typedefs and create a tokenized list.
307          string vectorSize = p->mVectorSize;
308          list<string> tokens = expandTypedefs(p->rsType, apiLevel, intSize, vectorSize);
309          if (p->isOutParameter) {
310              tokens.push_back("*");
311          }
312          string mangling, compressedMangling;
313  
314          if (!mangleType(vectorSize, &tokens, &previousManglings, &mangling,
315                          &compressedMangling)) {
316              return false;
317          }
318          *stream << compressedMangling;
319      }
320      return true;
321  }
322  
323  /* Add the mangling for this permutation of the function.  apiLevel and intSize is used
324   * to select the correct type when expanding complex type.
325   */
addFunctionManglingToSet(const FunctionPermutation & permutation,bool overloadable,unsigned int apiLevel,int intSize,set<string> * allManglings)326  static bool addFunctionManglingToSet(const FunctionPermutation& permutation,
327                                       bool overloadable, unsigned int apiLevel,
328                                       int intSize, set<string>* allManglings) {
329      const string& functionName = permutation.getName();
330      string mangling;
331      if (overloadable) {
332          ostringstream stream;
333          stream << "_Z" << mangleLongName(functionName);
334          if (!writeParameters(&stream, permutation.getParams(), apiLevel, intSize)) {
335              cerr << "Error mangling " << functionName << ".  See above message.\n";
336              return false;
337          }
338          mangling = stream.str();
339      } else {
340          mangling = functionName;
341      }
342      allManglings->insert(mangling);
343      return true;
344  }
345  
346  /* Add to the set the mangling of each function prototype that can be generated from this
347   * specification, i.e. for all the versions covered and for 32/64 bits.  We call this
348   * for each API level because the implementation of a type may have changed in the range
349   * of API levels covered.
350   */
addManglingsForSpecification(const FunctionSpecification & spec,unsigned int lastApiLevel,set<string> * allManglings)351  static bool addManglingsForSpecification(const FunctionSpecification& spec,
352                                           unsigned int lastApiLevel,
353                                           set<string>* allManglings) {
354      // If the function is inlined, we won't generate an unresolved external for that.
355      if (spec.hasInline()) {
356          return true;
357      }
358      const VersionInfo info = spec.getVersionInfo();
359      unsigned int minApiLevel, maxApiLevel;
360      minApiLevel = info.minVersion ? info.minVersion : kMinimumApiLevelForTests;
361      maxApiLevel = info.maxVersion ? info.maxVersion : lastApiLevel;
362      const bool overloadable = spec.isOverloadable();
363  
364      /* We track success rather than aborting early in case of failure so that we
365       * generate all the error messages.
366       */
367      bool success = true;
368      // Use 64-bit integer here for the loop count to avoid overflow
369      // (minApiLevel == maxApiLevel == UINT_MAX for unreleased API)
370      for (int64_t apiLevel = minApiLevel; apiLevel <= maxApiLevel; ++apiLevel) {
371          for (auto permutation : spec.getPermutations()) {
372              if (info.intSize == 0 || info.intSize == 32) {
373                  if (!addFunctionManglingToSet(*permutation, overloadable,
374                                                apiLevel, 32, allManglings)) {
375                      success = false;
376                  }
377              }
378              if (apiLevel >= kApiLevelWithFirst64Bit && (info.intSize == 0 || info.intSize == 64)) {
379                  if (!addFunctionManglingToSet(*permutation, overloadable,
380                                                apiLevel, 64, allManglings)) {
381                      success = false;
382                  }
383              }
384          }
385      }
386      return success;
387  }
388  
389  /* Generate the file with the mangled function names.  This generated list is used
390   * to validate unresolved external references.  'lastApiLevel' is the largest api level found in
391   * all spec files.
392   */
generateRSFunctionsListFile(unsigned int lastApiLevel)393  static bool generateRSFunctionsListFile(unsigned int lastApiLevel) {
394      bool success = true;
395      // We generate all the manglings in a set to remove duplicates and to order them.
396      set<string> allManglings;
397      for (auto f : systemSpecification.getFunctions()) {
398          const Function* function = f.second;
399          for (auto spec : function->getSpecifications()) {
400              // Compiler intrinsics are not runtime APIs. Do not include them.
401              if (spec->isIntrinsic()) {
402                  continue;
403              }
404              if (!addManglingsForSpecification(*spec, lastApiLevel,
405                                                &allManglings)) {
406                  success = false;  // We continue so we can generate all errors.
407              }
408          }
409      }
410  
411      if (success) {
412          GeneratedFile file;
413          if (!file.start(".", "RSFunctionsList.cpp")) {
414              return false;
415          }
416  
417          file.writeNotices();
418          file << "#include \"RSFunctionsList.h\"\n\n";
419          file << "std::array<std::string_view, " << allManglings.size() << "> stubList = {\n";
420          for (const auto& e : allManglings) {
421              file << "\"" << e << "\",\n";
422          }
423          file << "};\n";
424  
425          GeneratedFile header;
426          if (!header.start(".", "RSFunctionsList.h")) {
427              return false;
428          }
429  
430          header.writeNotices();
431          header << "#ifndef RSFunctionsList_H\n";
432          header << "#define RSFunctionsList_H\n\n";
433          header << "#include <cstdlib>\n";
434          header << "#include <array>\n";
435          header << "#include <string_view>\n\n";
436          header << "extern std::array<std::string_view, " << allManglings.size() << "> stubList;\n\n";
437          header << "#endif // RSFunctionsList_H\n";
438      }
439      return success;
440  }
441  
442  // Add a uniquely named variable definition to the file and return its name.
addVariable(GeneratedFile * file,unsigned int * variableNumber)443  static const string addVariable(GeneratedFile* file, unsigned int* variableNumber) {
444      const string name = "buf" + to_string((*variableNumber)++);
445      /* Some data structures like rs_tm can't be exported.  We'll just use a unexpected buffer
446       * and cast its address later on.
447       */
448      *file << "char " << name << "[200];\n";
449      return name;
450  }
451  
452  /* Write to the file the globals needed to make the call for this permutation.  The actual
453   * call is stored in 'calls', as we'll need to generate all the global variable declarations
454   * before the function definition.
455   */
generateTestCall(GeneratedFile * file,ostringstream * calls,unsigned int * variableNumber,const FunctionPermutation & permutation)456  static void generateTestCall(GeneratedFile* file, ostringstream* calls,
457                               unsigned int* variableNumber,
458                               const FunctionPermutation& permutation) {
459      *calls << "    ";
460  
461      // Handle the return type.
462      const auto ret = permutation.getReturn();
463      if (ret && ret->rsType != "void" && ret->rsType != "const void") {
464          *calls << "*(" << ret->rsType << "*)" << addVariable(file, variableNumber) << " = ";
465      }
466  
467      *calls << permutation.getName() << "(";
468  
469      // Generate the arguments.
470      const char* separator = "";
471      for (auto p : permutation.getParams()) {
472          *calls << separator;
473          if (p->rsType == "rs_kernel_context") {
474              // Special case for the kernel context, as it has a special existence.
475              *calls << "context";
476          } else if (p->rsType == "...") {
477              // Special case for varargs. No need for casting.
478              *calls << addVariable(file, variableNumber);
479          } else if (p->isOutParameter) {
480              *calls << "(" << p->rsType << "*) " << addVariable(file, variableNumber);
481          } else {
482              *calls << "*(" << p->rsType << "*)" << addVariable(file, variableNumber);
483          }
484          separator = ", ";
485      }
486      *calls << ");\n";
487  }
488  
489  /* Generate a test file that will be used in the frameworks/compile/slang/tests unit tests.
490   * This file tests that all RenderScript APIs can be called for the specified API level.
491   * To avoid the compiler agressively pruning out our calls, we use globals as inputs and outputs.
492   *
493   * Since some structures can't be defined at the global level, we use casts of simple byte
494   * buffers to get around that restriction.
495   *
496   * This file can be used to verify the function list that's also generated in this file.  To do so,
497   * run "llvm-nm -undefined-only -just-symbol-name" on the resulting bit code.
498   */
generateApiTesterFile(const string & slangTestDirectory,unsigned int apiLevel)499  static bool generateApiTesterFile(const string& slangTestDirectory, unsigned int apiLevel) {
500      GeneratedFile file;
501      if (!file.start(slangTestDirectory, "all" + to_string(apiLevel) + ".rs")) {
502          return false;
503      }
504  
505      /* This unusual comment is used by slang/tests/test.py to know which parameter to pass
506       * to llvm-rs-cc when compiling the test.
507       */
508      file << "// -target-api " << apiLevel << " -Wno-deprecated-declarations\n";
509  
510      file.writeNotices();
511      file << "#pragma version(1)\n";
512      file << "#pragma rs java_package_name(com.example.renderscript.testallapi)\n\n";
513      if (apiLevel < 23) {  // All rs_graphics APIs were deprecated in api level 23.
514          file << "#include \"rs_graphics.rsh\"\n\n";
515      }
516  
517      /* The code below emits globals and calls to functions in parallel.  We store
518       * the calls in a stream so that we can emit them in the file in the proper order.
519       */
520      ostringstream calls;
521      unsigned int variableNumber = 0;  // Used to generate unique names.
522      for (auto f : systemSpecification.getFunctions()) {
523          const Function* function = f.second;
524          for (auto spec : function->getSpecifications()) {
525              // Do not include internal APIs in the API tests.
526              if (spec->isInternal()) {
527                  continue;
528              }
529              VersionInfo info = spec->getVersionInfo();
530              if (!info.includesVersion(apiLevel)) {
531                  continue;
532              }
533              if (info.intSize == 32) {
534                  calls << "#ifndef __LP64__\n";
535              } else if (info.intSize == 64) {
536                  calls << "#ifdef __LP64__\n";
537              }
538              for (auto permutation : spec->getPermutations()) {
539                  // http://b/27358969 Do not test rsForEach in the all-api test.
540                  if (apiLevel >= 24 && permutation->getName().compare(0, 9, "rsForEach") == 0)
541                    continue;
542                  generateTestCall(&file, &calls, &variableNumber, *permutation);
543              }
544              if (info.intSize != 0) {
545                  calls << "#endif\n";
546              }
547          }
548      }
549      file << "\n";
550  
551      // Modify the style of kernel as required by the API level.
552      if (apiLevel >= 23) {
553          file << "void RS_KERNEL test(int in, rs_kernel_context context) {\n";
554      } else if (apiLevel >= 17) {
555          file << "void RS_KERNEL test(int in) {\n";
556      } else {
557          file << "void root(const int* in) {\n";
558      }
559      file << calls.str();
560      file << "}\n";
561  
562      return true;
563  }
564  
generateRSFunctionsList(const string & slangTestDirectory,unsigned int maxApiLevel)565  bool generateRSFunctionsList(const string& slangTestDirectory, unsigned int maxApiLevel) {
566      unsigned int lastApiLevel = min(systemSpecification.getMaximumApiLevel(), maxApiLevel);
567      if (!generateRSFunctionsListFile(lastApiLevel)) {
568          return false;
569      }
570      // Generate a test file for each apiLevel.
571      for (unsigned int i = kMinimumApiLevelForTests; i <= lastApiLevel; ++i) {
572          if (!generateApiTesterFile(slangTestDirectory, i)) {
573              return false;
574          }
575      }
576      return true;
577  }
578