gen_runtime.cpp revision 46341434b4a766d9dee33a93469564a1de8b0793
1/* 2 * Copyright (C) 2013 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/* This program processes Renderscript function definitions described in spec files. 18 * For each spec file provided on the command line, it generates a corresponding 19 * Renderscript header (*.rsh) which is meant for inclusion in client scripts. 20 * 21 * This program also generates Junit test files to automatically test each of the 22 * functions using randomly generated data. We create two files for each function: 23 * - a Renderscript file named Test{Function}.rs, 24 * - a Junit file named Test{function}.java, which calls the above RS file. 25 * 26 * This program takes an optional -v parameter, the RS version to target the 27 * test files for. The header file will always contain all the functions. 28 * 29 * This program contains five main classes: 30 * - SpecFile: Represents on spec file. 31 * - Function: Each instance represents a function, like clamp. Even though the 32 * spec file contains many entries for clamp, we'll only have one clamp instance. 33 * - Specification: Defines one of the many variations of the function. There's 34 * a one to one correspondance between Specification objects and entries in the 35 * spec file. Strings that are parts of a Specification can include placeholders, 36 * which are "#1", "#2", "#3", and "#4". We'll replace these by values before 37 * generating the files. 38 * - Permutation: A concrete version of a specification, where all placeholders have 39 * been replaced by actual values. 40 * - ParameterDefinition: A definition of a parameter of a concrete function. 41 */ 42 43// TODO Rename runtime.spec to rs_core_math.spec. 44// TODO Handle NaN, +Inf, -Inf correctly. 45// TODO Add range(,) as an option for test values. 46 47#include <cctype> 48#include <cstdlib> 49#include <stdio.h> 50#include <functional> 51#include <list> 52#include <map> 53#include <set> 54#include <fstream> 55#include <sstream> 56#include <string> 57#include <vector> 58 59using namespace std; 60 61namespace { 62 63const char* AUTO_GENERATED_WARNING = 64 "// Don't edit this file! It is auto-generated by " 65 "frameworks/rs/api/gen_runtime.\n\n"; 66const char* LEGAL_NOTICE = 67 "/*\n" 68 " * Copyright (C) 2014 The Android Open Source Project\n" 69 " *\n" 70 " * Licensed under the Apache License, Version 2.0 (the \"License\");\n" 71 " * you may not use this file except in compliance with the License.\n" 72 " * You may obtain a copy of the License at\n" 73 " *\n" 74 " * http://www.apache.org/licenses/LICENSE-2.0\n" 75 " *\n" 76 " * Unless required by applicable law or agreed to in writing, software\n" 77 " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" 78 " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" 79 " * See the License for the specific language governing permissions and\n" 80 " * limitations under the License.\n" 81 " */\n\n"; 82 83class Function; 84class Specification; 85class Permutation; 86 87/* Information about a parameter to a function. The values of all the fields should only be set by 88 * parseParameterDefinition. 89 */ 90struct ParameterDefinition { 91 string rsType; // The Renderscript type, e.g. "uint3" 92 string rsBaseType; // As above but without the number, e.g. "uint" 93 string javaBaseType; // The type we need to declare in Java, e.g. "unsigned int" 94 95 /* The number of entries in the vector. It should be either "1", "2", "3", or "4". It's also 96 * "1" for scalars. 97 */ 98 string mVectorSize; 99 /* The space the vector takes in an array. It's the same as the vector size, except for size 100 * "3", where the width is "4". 101 */ 102 string vectorWidth; 103 104 string specName; // e.g. x, as found in the spec file 105 string variableName; // e.g. inX, used both in .rs and .java 106 string rsAllocName; // e.g. gAllocInX 107 string javaAllocName; // e.g. inX 108 string javaArrayName; // e.g. arrayInX 109 110 // If non empty, the mininum and maximum values to be used when generating the test data. 111 string minValue; 112 string maxValue; 113 /* If non empty, contains the name of another parameter that should be smaller or equal to this 114 * parameter, i.e. value(smallerParameter) <= value(this). This is used when testing clamp. 115 */ 116 string smallerParameter; 117 118 bool isOutParameter; // True if this parameter returns data from the script. 119 120 /* Parse the parameter definition found in the spec file. It will generate a name if none 121 * are present in the file. One of the two counts will be incremented, and potentially 122 * used to generate unique names. isReturn is true if we're processing the "return:" 123 * definition. 124 */ 125 void parseParameterDefinition(string s, bool isReturn, int* inputCount, int* outputCount); 126}; 127 128// An entire spec file and the methods to process it. 129class SpecFile { 130public: 131 explicit SpecFile(const string& specFileName) : mSpecFileName(specFileName) {} 132 bool process(int versionOfTestFiles); 133 134private: 135 const string mSpecFileName; 136 // The largest version number that we have found in all the specifications. 137 int mLargestVersionNumber; 138 139 map<string, Function*> mFunctionsMap; // All the known functions. 140 typedef map<string, Function*>::iterator FunctionsIterator; 141 142 bool readSpecFile(); 143 Function* getFunction(const string& name); 144 bool generateFiles(int versionOfTestFiles); 145 bool writeAllFunctions(ofstream& headerFile, int versionOfTestFiles); 146}; 147 148/* Represents a function, like "clamp". Even though the spec file contains many entries for clamp, 149 * we'll only have one clamp instance. 150 */ 151class Function { 152private: 153 string mName; // The lower case name, e.g. native_log 154 string mCapitalizedName; // The capitalized name, e.g. NativeLog 155 string mTestName; // e.g. TestNativeLog 156 string mRelaxedTestName; // e.g. TestNativeLogRelaxed 157 158 vector<Specification*> mSpecifications; 159 typedef vector<Specification*>::iterator SpecificationIterator; 160 161 /* We keep track of the allocations generated in the .rs file and the argument classes defined 162 * in the Java file, as we share these between the functions created for each specification. 163 */ 164 set<string> mRsAllocationsGenerated; 165 set<string> mJavaGeneratedArgumentClasses; 166 167 string mJavaCallAllCheckMethods; // Lines of Java code to invoke the check methods. 168 169 ofstream mRsFile; // The Renderscript test file we're generating. 170 ofstream mJavaFile; // The Jave test file we're generating. 171 172 bool startRsFile(); // Open the mRsFile and writes its header. 173 bool writeRelaxedRsFile(); // Write the entire relaxed rs test file (an include essentially) 174 bool startJavaFile(); // Open the mJavaFile and writes the header. 175 void finishJavaFile(); // Write the test method and closes the file. 176 177public: 178 explicit Function(const string& name); 179 void addSpecification(Specification* spec) { mSpecifications.push_back(spec); } 180 /* Write the .java and the two .rs test files. versionOfTestFiles is used to restrict which API 181 * to test. Also writes the section of the header file. 182 */ 183 bool writeFiles(ofstream& headerFile, int versionOfTestFiles); 184 // Write an allocation and keep track of having it written, so it can be shared. 185 void writeRsAllocationDefinition(const ParameterDefinition& param); 186 // Write an argument class definiton and keep track of having it written, so it can be shared. 187 void writeJavaArgumentClassDefinition(const string& className, const string& definition); 188 // Add a call to mJavaCallAllCheckMethods to be used at the end of the file generation. 189 void addJavaCheckCall(const string& call); 190}; 191 192/* Defines one of the many variations of the function. There's a one to one correspondance between 193 * Specification objects and entries in the spec file. Some of the strings that are parts of a 194 * Specification can include placeholders, which are "#1", "#2", "#3", and "#4". We'll replace 195 * these by values before generating the files. 196 */ 197class Specification { 198private: 199 /* The range of versions this specification applies to. 0 if there's no restriction, so an API 200 * that became available at 9 and is still valid would have min:9 max:0. 201 */ 202 int mMinVersion; 203 int mMaxVersion; 204 205 /* The name of the function without #n, e.g. convert. As of this writing, it only differs for 206 * convert. 207 */ 208 string mCleanName; 209 string mTest; // How to test. One of "scalar", "vector", "noverify", and "none". 210 211 vector<vector<string> > mReplaceables; 212 213 // The following fields may contain placeholders that will be replaced using the mReplaceables. 214 215 // The name of this function, can include #, e.g. convert_#1_#2 216 string mName; 217 218 string mReturn; // The return type 219 vector<string> mComment; // The comments to be included in the header 220 vector<string> mInline; // The inline code to be included in the header 221 vector<string> mParam; // One entry per parameter defined 222 223 // Substitute the placeholders in the strings by the corresponding entries in mReplaceables. 224 string expandString(string s, int i1, int i2, int i3, int i4) const; 225 void expandStringVector(const vector<string>& in, int i1, int i2, int i3, int i4, 226 vector<string>* out) const; 227 228public: 229 Specification() { 230 mMinVersion = 0; 231 mMaxVersion = 0; 232 } 233 int getMinVersion() const { return mMinVersion; } 234 int getMaxVersion() const { return mMaxVersion; } 235 236 string getName(int i1, int i2, int i3, int i4) const { 237 return expandString(mName, i1, i2, i3, i4); 238 } 239 string getReturn(int i1, int i2, int i3, int i4) const { 240 return expandString(mReturn, i1, i2, i3, i4); 241 } 242 void getComments(int i1, int i2, int i3, int i4, vector<string>* comments) const { 243 return expandStringVector(mComment, i1, i2, i3, i4, comments); 244 } 245 void getInlines(int i1, int i2, int i3, int i4, vector<string>* inlines) const { 246 return expandStringVector(mInline, i1, i2, i3, i4, inlines); 247 } 248 void getParams(int i1, int i2, int i3, int i4, vector<string>* params) const { 249 return expandStringVector(mParam, i1, i2, i3, i4, params); 250 } 251 string getTest() const { return mTest; } 252 string getCleanName() const { return mCleanName; } 253 254 void writeFiles(ofstream& headerFile, ofstream& rsFile, ofstream& javaFile, Function* function, 255 int versionOfTestFiles); 256 bool writeRelaxedRsFile() const; 257 // Return true if this specification should be generated for this version. 258 bool relevantForVersion(int versionOfTestFiles) const; 259 260 static Specification* scanSpecification(FILE* in); 261}; 262 263// A concrete version of a specification, where all placeholders have been replaced by actual 264// values. 265class Permutation { 266private: 267 Function* mFunction; 268 Specification* mSpecification; 269 270 // These are the expanded version of those found on Specification 271 string mName; 272 string mCleanName; 273 string mTest; // How to test. One of "scalar", "vector", "noverify", and "none". 274 vector<string> mInline; 275 vector<string> mComment; 276 277 // The inputs and outputs of the function. This include the return type, if present. 278 vector<ParameterDefinition*> mParams; 279 // The index of the return value in mParams, -1 if the function is void. 280 int mReturnIndex; 281 // The index of the first input value in mParams, -1 if there's no input. 282 int mFirstInputIndex; 283 // The number of input and output parameters. 284 int mInputCount; 285 int mOutputCount; 286 287 string mRsKernelName; 288 string mJavaArgumentsClassName; 289 string mJavaArgumentsNClassName; 290 string mJavaVerifierComputeMethodName; 291 string mJavaCheckMethodName; 292 string mJavaVerifyMethodName; 293 294 void writeHeaderSection(ofstream& file) const; 295 296 void writeRsSection(ofstream& rs) const; 297 298 void writeJavaSection(ofstream& file) const; 299 void writeJavaArgumentClass(ofstream& file, bool scalar) const; 300 void writeJavaCheckMethod(ofstream& file, bool generateCallToVerify) const; 301 void writeJavaVerifyScalarMethod(ofstream& file) const; 302 void writeJavaVerifyVectorMethod(ofstream& file) const; 303 void writeJavaVerifyFunctionHeader(ofstream& file) const; 304 void writeJavaInputAllocationDefinition(ofstream& file, const string& indent, 305 const ParameterDefinition& param) const; 306 void writeJavaOutputAllocationDefinition(ofstream& file, const string& indent, 307 const ParameterDefinition& param) const; 308 void writeJavaCallToRs(ofstream& file, bool relaxed, bool generateCallToVerify) const; 309 310 void writeJavaTestOneValue(ofstream& file, int indent, const string& rsBaseType, 311 const string& expected, const string& actual) const; 312 void writeJavaAppendOutputToMessage(ofstream& file, int indent, const string& rsBaseType, 313 const string& name, const string& expected, 314 const string& actual) const; 315 void writeJavaAppendInputToMessage(ofstream& file, int indent, const string& rsBaseType, 316 const string& name, const string& actual) const; 317 void writeJavaComputeNeededUlf(ofstream& file, int indent, const string& expected, 318 const string& actual) const; 319 void writeJavaAppendNewLineToMessage(ofstream& file, int indent) const; 320 void writeJavaAppendVariableToMessage(ofstream& file, int indent, const string& rsBaseType, 321 const string& legend, const string& value) const; 322 void writeJavaVectorComparison(ofstream& file, int indent, const ParameterDefinition& p) const; 323 void writeJavaAppendVectorInputToMessage(ofstream& file, int indent, 324 const ParameterDefinition& p) const; 325 void writeJavaAppendVectorOutputToMessage(ofstream& file, int indent, 326 const ParameterDefinition& p) const; 327 bool passByAddressToSet(const string& name) const; 328 void convertToRsType(const string& name, string* dataType, char* vectorSize) const; 329 330public: 331 Permutation(Function* function, Specification* specification, int i1, int i2, int i3, int i4); 332 void writeFiles(ofstream& headerFile, ofstream& rsFile, ofstream& javaFile, 333 int versionOfTestFiles); 334 bool hasLongOrDoubleParameter() const; 335}; 336 337// Table of type equivalences 338// TODO: We should just be pulling this from a shared header. Slang does exactly the same thing. 339struct Type { 340 const char* specType; // Name found in the .spec file 341 string rsDataType; // RS data type 342 string cType; // Type in a C file 343 const char* javaType; // Type in a Java file 344}; 345 346const Type TYPES[] = {{"f16", "FLOAT_16", "half", "half"}, 347 {"f32", "FLOAT_32", "float", "float"}, 348 {"f64", "FLOAT_64", "double", "double"}, 349 {"i8", "SIGNED_8", "char", "byte"}, 350 {"u8", "UNSIGNED_8", "uchar", "byte"}, 351 {"i16", "SIGNED_16", "short", "short"}, 352 {"u16", "UNSIGNED_16", "ushort", "short"}, 353 {"i32", "SIGNED_32", "int", "int"}, 354 {"u32", "UNSIGNED_32", "uint", "int"}, 355 {"i64", "SIGNED_64", "long", "long"}, 356 {"u64", "UNSIGNED_64", "ulong", "long"}}; 357 358const int NUM_TYPES = sizeof(TYPES) / sizeof(TYPES[0]); 359 360// Capitalizes and removes underscores. E.g. converts "native_log" to NativeLog. 361string capitalize(const string& source) { 362 int length = source.length(); 363 string result; 364 bool capitalize = true; 365 for (int s = 0; s < length; s++) { 366 if (source[s] == '_') { 367 capitalize = true; 368 } else if (capitalize) { 369 result += toupper(source[s]); 370 capitalize = false; 371 } else { 372 result += source[s]; 373 } 374 } 375 return result; 376} 377 378string tab(int n) { return string(n * 4, ' '); } 379 380long hashString(const string& s) { 381 long hash = 0; 382 for (size_t i = 0; i < s.length(); i++) { 383 hash = hash * 43 + s[i]; 384 } 385 return hash; 386} 387 388// Removes the character from present. Returns true if the string contained the character. 389static bool charRemoved(char c, string* s) { 390 size_t p = s->find(c); 391 if (p != string::npos) { 392 s->erase(p, 1); 393 return true; 394 } 395 return false; 396} 397 398// Return true if the string is already in the set. Inserts it if not. 399bool testAndSet(const string& flag, set<string>* set) { 400 if (set->find(flag) == set->end()) { 401 set->insert(flag); 402 return false; 403 } 404 return true; 405} 406 407// Convert an int into a string. 408string toString(int n) { 409 char buf[100]; 410 snprintf(buf, sizeof(buf), "%d", n); 411 return string(buf); 412} 413 414void trim(string* s, size_t start) { 415 if (start > 0) { 416 s->erase(0, start); 417 } 418 419 while (s->size() && (s->at(0) == ' ')) { 420 s->erase(0, 1); 421 } 422 423 size_t p = s->find_first_of("\n\r"); 424 if (p != string::npos) { 425 s->erase(p); 426 } 427 428 while ((s->size() > 0) && (s->at(s->size() - 1) == ' ')) { 429 s->erase(s->size() - 1); 430 } 431} 432 433string stringReplace(string s, string match, string rep) { 434 while (1) { 435 size_t p = s.find(match); 436 if (p == string::npos) break; 437 438 s.erase(p, match.size()); 439 s.insert(p, rep); 440 } 441 return s; 442} 443 444// Return the next line from the input file. 445bool getNextLine(FILE* in, string* s) { 446 s->clear(); 447 while (1) { 448 int c = fgetc(in); 449 if (c == EOF) return s->size() != 0; 450 if (c == '\n') break; 451 s->push_back((char)c); 452 } 453 return true; 454} 455 456void writeIfdef(ofstream& file, string filename, bool isStart) { 457 string t = "__"; 458 t += filename; 459 t += "__"; 460 461 for (size_t i = 2; i < t.size(); i++) { 462 if (t[i] == '.') { 463 t[i] = '_'; 464 } 465 } 466 467 if (isStart) { 468 file << "#ifndef " << t << "\n"; 469 file << "#define " << t << "\n"; 470 } else { 471 file << "#endif // " << t << "\n"; 472 } 473} 474 475void writeJavaArrayInitialization(ofstream& file, const ParameterDefinition& p) { 476 file << tab(2) << p.javaBaseType << "[] " << p.javaArrayName << " = new " << p.javaBaseType 477 << "[INPUTSIZE * " << p.vectorWidth << "];\n"; 478 file << tab(2) << p.javaAllocName << ".copyTo(" << p.javaArrayName << ");\n"; 479} 480 481bool parseCommandLine(int argc, char* argv[], int* versionOfTestFiles, 482 vector<string>* specFileNames) { 483 for (int i = 1; i < argc; i++) { 484 if (argv[i][0] == '-') { 485 if (argv[i][1] == 'v') { 486 i++; 487 if (i < argc) { 488 char* end; 489 *versionOfTestFiles = strtol(argv[i], &end, 10); 490 if (*end != '\0') { 491 printf("Can't parse the version number %s\n", argv[i]); 492 return false; 493 } 494 } else { 495 printf("Missing version number after -v\n"); 496 return false; 497 } 498 } else { 499 printf("Unrecognized flag %s\n", argv[i]); 500 return false; 501 } 502 } else { 503 specFileNames->push_back(argv[i]); 504 } 505 } 506 if (specFileNames->size() == 0) { 507 printf("No spec file specified\n"); 508 return false; 509 } 510 return true; 511} 512 513/* Parse a parameter definition. It's of the form "type [*][name]". The type 514 * is required. The name is optional. The * indicates it's an output 515 * parameter. We also pass the indexed of this parameter in the definition, so 516 * we can create names like in2, in3, etc. */ 517void ParameterDefinition::parseParameterDefinition(string s, bool isReturn, int* inputCount, 518 int* outputCount) { 519 istringstream stream(s); 520 string name, type, option; 521 stream >> rsType; 522 stream >> specName; 523 stream >> option; 524 525 // Determine if this is an output. 526 isOutParameter = charRemoved('*', &rsType) || charRemoved('*', &specName) || isReturn; 527 528 // Extract the vector size out of the type. 529 int last = rsType.size() - 1; 530 char lastChar = rsType[last]; 531 if (lastChar >= '0' && lastChar <= '9') { 532 rsBaseType = rsType.substr(0, last); 533 mVectorSize = lastChar; 534 } else { 535 rsBaseType = rsType; 536 mVectorSize = "1"; 537 } 538 if (mVectorSize == "3") { 539 vectorWidth = "4"; 540 } else { 541 vectorWidth = mVectorSize; 542 } 543 544 /* Create variable names to be used in the java and .rs files. Because x and 545 * y are reserved in .rs files, we prefix variable names with "in" or "out". 546 */ 547 if (isOutParameter) { 548 variableName = "out"; 549 if (!specName.empty()) { 550 variableName += capitalize(specName); 551 } else if (!isReturn) { 552 variableName += toString(*outputCount); 553 } 554 (*outputCount)++; 555 } else { 556 variableName = "in"; 557 if (!specName.empty()) { 558 variableName += capitalize(specName); 559 } else if (*inputCount > 0) { 560 variableName += toString(*inputCount); 561 } 562 (*inputCount)++; 563 } 564 rsAllocName = "gAlloc" + capitalize(variableName); 565 javaAllocName = variableName; 566 javaArrayName = "array" + capitalize(javaAllocName); 567 568 // Process the option. 569 if (!option.empty()) { 570 if (option.compare(0, 6, "range(") == 0) { 571 size_t pComma = option.find(','); 572 size_t pParen = option.find(')'); 573 if (pComma == string::npos || pParen == string::npos) { 574 printf("Incorrect range %s\n", option.c_str()); 575 } else { 576 minValue = option.substr(6, pComma - 6); 577 maxValue = option.substr(pComma + 1, pParen - pComma - 1); 578 } 579 } else if (option.compare(0, 6, "above(") == 0) { 580 size_t pParen = option.find(')'); 581 if (pParen == string::npos) { 582 printf("Incorrect option %s\n", option.c_str()); 583 } else { 584 smallerParameter = option.substr(6, pParen - 6); 585 } 586 } else { 587 printf("Unrecognized option %s\n", option.c_str()); 588 } 589 } 590 591 for (int i = 0; i < NUM_TYPES; i++) { 592 if (rsBaseType == TYPES[i].cType) { 593 javaBaseType = TYPES[i].javaType; 594 break; 595 } 596 } 597} 598 599bool SpecFile::process(int versionOfTestFiles) { 600 if (!readSpecFile()) { 601 return false; 602 } 603 if (versionOfTestFiles == 0) { 604 versionOfTestFiles = mLargestVersionNumber; 605 } 606 if (!generateFiles(versionOfTestFiles)) { 607 return false; 608 } 609 printf("%s: %ld functions processed.\n", mSpecFileName.c_str(), mFunctionsMap.size()); 610 return true; 611} 612 613// Read the specification, adding the definitions to the global functions map. 614bool SpecFile::readSpecFile() { 615 FILE* specFile = fopen(mSpecFileName.c_str(), "rt"); 616 if (!specFile) { 617 printf("Error opening input file: %s\n", mSpecFileName.c_str()); 618 return false; 619 } 620 621 mLargestVersionNumber = 0; 622 while (1) { 623 Specification* spec = Specification::scanSpecification(specFile); 624 if (spec == NULL) { 625 break; 626 } 627 getFunction(spec->getCleanName())->addSpecification(spec); 628 int specMin = spec->getMinVersion(); 629 int specMax = spec->getMaxVersion(); 630 if (specMin && specMin > mLargestVersionNumber) { 631 mLargestVersionNumber = specMin; 632 } 633 if (specMax && specMax > mLargestVersionNumber) { 634 mLargestVersionNumber = specMax; 635 } 636 } 637 638 fclose(specFile); 639 return true; 640} 641 642bool SpecFile::generateFiles(int versionOfTestFiles) { 643 printf("%s: Generating test files for version %d\n", mSpecFileName.c_str(), versionOfTestFiles); 644 645 // The header file name should have the same base but with a ".rsh" extension. 646 string headerFileName = mSpecFileName; 647 size_t l = headerFileName.length(); 648 const char SPEC[] = ".spec"; 649 const int SPEC_SIZE = sizeof(SPEC) - 1; 650 const int start = l - SPEC_SIZE; 651 if (start >= 0 && headerFileName.compare(start, SPEC_SIZE, SPEC) == 0) { 652 headerFileName.erase(start); 653 } 654 headerFileName += ".rsh"; 655 656 // Write the start of the header file. 657 ofstream headerFile; 658 headerFile.open(headerFileName.c_str(), ios::out | ios::trunc); 659 if (!headerFile.is_open()) { 660 printf("Error opening output file: %s\n", headerFileName.c_str()); 661 return false; 662 } 663 headerFile << LEGAL_NOTICE; 664 headerFile << AUTO_GENERATED_WARNING; 665 writeIfdef(headerFile, headerFileName, true); 666 667 // Write the functions to the header and test files. 668 bool success = writeAllFunctions(headerFile, versionOfTestFiles); 669 670 // Finish the header file. 671 writeIfdef(headerFile, headerFileName, false); 672 headerFile.close(); 673 674 return success; 675} 676 677// Return the named function from the map. Creates it if it's not there. 678Function* SpecFile::getFunction(const string& name) { 679 FunctionsIterator iter = mFunctionsMap.find(name); 680 if (iter != mFunctionsMap.end()) { 681 return iter->second; 682 } 683 Function* f = new Function(name); 684 mFunctionsMap[name] = f; 685 return f; 686} 687 688bool SpecFile::writeAllFunctions(ofstream& headerFile, int versionOfTestFiles) { 689 bool success = true; 690 for (FunctionsIterator iter = mFunctionsMap.begin(); iter != mFunctionsMap.end(); iter++) { 691 Function* func = iter->second; 692 if (!func->writeFiles(headerFile, versionOfTestFiles)) { 693 success = false; 694 } 695 } 696 return success; 697} 698 699Function::Function(const string& name) { 700 mName = name; 701 mCapitalizedName = capitalize(mName); 702 mTestName = "Test" + mCapitalizedName; 703 mRelaxedTestName = mTestName + "Relaxed"; 704} 705 706bool Function::writeFiles(ofstream& headerFile, int versionOfTestFiles) { 707 if (!startRsFile() || !startJavaFile() || !writeRelaxedRsFile()) { 708 return false; 709 } 710 711 for (SpecificationIterator i = mSpecifications.begin(); i < mSpecifications.end(); i++) { 712 (*i)->writeFiles(headerFile, mRsFile, mJavaFile, this, versionOfTestFiles); 713 } 714 715 finishJavaFile(); 716 // There's no work to wrap-up in the .rs file. 717 718 mRsFile.close(); 719 mJavaFile.close(); 720 return true; 721} 722 723bool Function::startRsFile() { 724 string fileName = mTestName + ".rs"; 725 mRsFile.open(fileName.c_str(), ios::out | ios::trunc); 726 if (!mRsFile.is_open()) { 727 printf("Error opening file: %s\n", fileName.c_str()); 728 return false; 729 } 730 mRsFile << LEGAL_NOTICE; 731 mRsFile << "#pragma version(1)\n"; 732 mRsFile << "#pragma rs java_package_name(android.renderscript.cts)\n\n"; 733 mRsFile << AUTO_GENERATED_WARNING; 734 return true; 735} 736 737// Write an allocation definition if not already emitted in the .rs file. 738void Function::writeRsAllocationDefinition(const ParameterDefinition& param) { 739 if (!testAndSet(param.rsAllocName, &mRsAllocationsGenerated)) { 740 mRsFile << "rs_allocation " << param.rsAllocName << ";\n"; 741 } 742} 743 744// Write the entire *Relaxed.rs test file, as it only depends on the name. 745bool Function::writeRelaxedRsFile() { 746 string name = mRelaxedTestName + ".rs"; 747 FILE* file = fopen(name.c_str(), "wt"); 748 if (!file) { 749 printf("Error opening file: %s\n", name.c_str()); 750 return false; 751 } 752 fputs(LEGAL_NOTICE, file); 753 string s; 754 s += "#include \"" + mTestName + ".rs\"\n"; 755 s += "#pragma rs_fp_relaxed\n"; 756 s += AUTO_GENERATED_WARNING; 757 fputs(s.c_str(), file); 758 fclose(file); 759 return true; 760} 761 762bool Function::startJavaFile() { 763 string fileName = mTestName + ".java"; 764 mJavaFile.open(fileName.c_str(), ios::out | ios::trunc); 765 if (!mJavaFile.is_open()) { 766 printf("Error opening file: %s\n", fileName.c_str()); 767 return false; 768 } 769 mJavaFile << LEGAL_NOTICE; 770 mJavaFile << AUTO_GENERATED_WARNING; 771 mJavaFile << "package android.renderscript.cts;\n\n"; 772 773 mJavaFile << "import android.renderscript.Allocation;\n"; 774 mJavaFile << "import android.renderscript.RSRuntimeException;\n"; 775 mJavaFile << "import android.renderscript.Element;\n\n"; 776 777 mJavaFile << "public class " << mTestName << " extends RSBaseCompute {\n\n"; 778 779 mJavaFile << tab(1) << "private ScriptC_" << mTestName << " script;\n"; 780 mJavaFile << tab(1) << "private ScriptC_" << mRelaxedTestName << " scriptRelaxed;\n\n"; 781 782 mJavaFile << tab(1) << "@Override\n"; 783 mJavaFile << tab(1) << "protected void setUp() throws Exception {\n"; 784 mJavaFile << tab(2) << "super.setUp();\n"; 785 mJavaFile << tab(2) << "script = new ScriptC_" << mTestName << "(mRS);\n"; 786 mJavaFile << tab(2) << "scriptRelaxed = new ScriptC_" << mRelaxedTestName << "(mRS);\n"; 787 mJavaFile << tab(1) << "}\n\n"; 788 return true; 789} 790 791void Function::writeJavaArgumentClassDefinition(const string& className, const string& definition) { 792 if (!testAndSet(className, &mJavaGeneratedArgumentClasses)) { 793 mJavaFile << definition; 794 } 795} 796 797void Function::addJavaCheckCall(const string& call) { 798 mJavaCallAllCheckMethods += tab(2) + call + "\n"; 799} 800 801void Function::finishJavaFile() { 802 mJavaFile << tab(1) << "public void test" << mCapitalizedName << "() {\n"; 803 mJavaFile << mJavaCallAllCheckMethods; 804 mJavaFile << tab(1) << "}\n"; 805 mJavaFile << "}\n"; 806} 807 808void Specification::expandStringVector(const vector<string>& in, int i1, int i2, int i3, int i4, 809 vector<string>* out) const { 810 out->clear(); 811 for (vector<string>::const_iterator iter = in.begin(); iter != in.end(); iter++) { 812 out->push_back(expandString(*iter, i1, i2, i3, i4)); 813 } 814} 815 816Specification* Specification::scanSpecification(FILE* in) { 817 Specification* spec = new Specification(); 818 spec->mTest = "scalar"; // default 819 bool modeComment = false; 820 bool modeInline = false; 821 bool success = true; 822 823 while (1) { 824 string s; 825 bool ret = getNextLine(in, &s); 826 if (!ret) break; 827 828 if (modeComment) { 829 if (!s.size() || (s[0] == ' ')) { 830 trim(&s, 0); 831 spec->mComment.push_back(s); 832 continue; 833 } else { 834 modeComment = false; 835 } 836 } 837 838 if (modeInline) { 839 if (!s.size() || (s[0] == ' ')) { 840 trim(&s, 0); 841 spec->mInline.push_back(s); 842 continue; 843 } else { 844 modeInline = false; 845 } 846 } 847 848 if (s[0] == '#') { 849 continue; 850 } 851 852 if (s.compare(0, 5, "name:") == 0) { 853 trim(&s, 5); 854 spec->mName = s; 855 // Some functions like convert have # part of the name. Truncate at that point. 856 size_t p = s.find('#'); 857 if (p != string::npos) { 858 if (p > 0 && s[p - 1] == '_') { 859 p--; 860 } 861 s.erase(p); 862 } 863 spec->mCleanName = s; 864 continue; 865 } 866 867 if (s.compare(0, 4, "arg:") == 0) { 868 trim(&s, 4); 869 spec->mParam.push_back(s); 870 continue; 871 } 872 873 if (s.compare(0, 4, "ret:") == 0) { 874 trim(&s, 4); 875 spec->mReturn = s; 876 continue; 877 } 878 879 if (s.compare(0, 5, "test:") == 0) { 880 trim(&s, 5); 881 if (s == "scalar" || s == "vector" || s == "noverify" || s == "none") { 882 spec->mTest = s; 883 } else { 884 printf("Unrecognized test option: %s\n", s.c_str()); 885 success = false; 886 } 887 continue; 888 } 889 890 if (s.compare(0, 4, "end:") == 0) { 891 if (success) { 892 return spec; 893 } else { 894 delete spec; 895 return NULL; 896 } 897 } 898 899 if (s.compare(0, 8, "comment:") == 0) { 900 modeComment = true; 901 continue; 902 } 903 904 if (s.compare(0, 7, "inline:") == 0) { 905 modeInline = true; 906 continue; 907 } 908 909 if (s.compare(0, 8, "version:") == 0) { 910 trim(&s, 8); 911 sscanf(s.c_str(), "%i %i", &spec->mMinVersion, &spec->mMaxVersion); 912 continue; 913 } 914 915 if (s.compare(0, 8, "start:") == 0) { 916 continue; 917 } 918 919 if (s.compare(0, 2, "w:") == 0) { 920 vector<string> t; 921 if (s.find("1") != string::npos) { 922 t.push_back(""); 923 } 924 if (s.find("2") != string::npos) { 925 t.push_back("2"); 926 } 927 if (s.find("3") != string::npos) { 928 t.push_back("3"); 929 } 930 if (s.find("4") != string::npos) { 931 t.push_back("4"); 932 } 933 spec->mReplaceables.push_back(t); 934 continue; 935 } 936 937 if (s.compare(0, 2, "t:") == 0) { 938 vector<string> t; 939 for (int i = 0; i < NUM_TYPES; i++) { 940 if (s.find(TYPES[i].specType) != string::npos) { 941 t.push_back(TYPES[i].cType); 942 } 943 } 944 spec->mReplaceables.push_back(t); 945 continue; 946 } 947 948 if (s.size() == 0) { 949 // eat empty line 950 continue; 951 } 952 953 printf("Error, line:\n"); 954 printf(" %s\n", s.c_str()); 955 } 956 957 delete spec; 958 return NULL; 959} 960 961void Specification::writeFiles(ofstream& headerFile, ofstream& rsFile, ofstream& javaFile, 962 Function* function, int versionOfTestFiles) { 963 int start[4]; 964 int end[4]; 965 for (int i = 0; i < 4; i++) { 966 if (i < (int)mReplaceables.size()) { 967 start[i] = 0; 968 end[i] = mReplaceables[i].size(); 969 } else { 970 start[i] = -1; 971 end[i] = 0; 972 } 973 } 974 for (int i4 = start[3]; i4 < end[3]; i4++) { 975 for (int i3 = start[2]; i3 < end[2]; i3++) { 976 for (int i2 = start[1]; i2 < end[1]; i2++) { 977 for (int i1 = start[0]; i1 < end[0]; i1++) { 978 Permutation p(function, this, i1, i2, i3, i4); 979 p.writeFiles(headerFile, rsFile, javaFile, versionOfTestFiles); 980 } 981 } 982 } 983 } 984} 985 986bool Specification::relevantForVersion(int versionOfTestFiles) const { 987 if (mMinVersion != 0 && mMinVersion > versionOfTestFiles) { 988 return false; 989 } 990 if (mMaxVersion != 0 && mMaxVersion < versionOfTestFiles) { 991 return false; 992 } 993 return true; 994} 995 996string Specification::expandString(string s, int i1, int i2, int i3, int i4) const { 997 if (mReplaceables.size() > 0) { 998 s = stringReplace(s, "#1", mReplaceables[0][i1]); 999 } 1000 if (mReplaceables.size() > 1) { 1001 s = stringReplace(s, "#2", mReplaceables[1][i2]); 1002 } 1003 if (mReplaceables.size() > 2) { 1004 s = stringReplace(s, "#3", mReplaceables[2][i3]); 1005 } 1006 if (mReplaceables.size() > 3) { 1007 s = stringReplace(s, "#4", mReplaceables[3][i4]); 1008 } 1009 return s; 1010} 1011 1012Permutation::Permutation(Function* func, Specification* spec, int i1, int i2, int i3, int i4) 1013 : mFunction(func), 1014 mSpecification(spec), 1015 mReturnIndex(-1), 1016 mFirstInputIndex(-1), 1017 mInputCount(0), 1018 mOutputCount(0) { 1019 // We expand the strings now to make capitalization easier. The previous code preserved the #n 1020 // markers just before emitting, which made capitalization difficult. 1021 mName = spec->getName(i1, i2, i3, i4); 1022 mCleanName = spec->getCleanName(); 1023 mTest = spec->getTest(); 1024 spec->getInlines(i1, i2, i3, i4, &mInline); 1025 spec->getComments(i1, i2, i3, i4, &mComment); 1026 1027 vector<string> paramDefinitions; 1028 spec->getParams(i1, i2, i3, i4, ¶mDefinitions); 1029 for (size_t i = 0; i < paramDefinitions.size(); i++) { 1030 ParameterDefinition* def = new ParameterDefinition(); 1031 def->parseParameterDefinition(paramDefinitions[i], false, &mInputCount, &mOutputCount); 1032 if (!def->isOutParameter && mFirstInputIndex < 0) { 1033 mFirstInputIndex = mParams.size(); 1034 } 1035 mParams.push_back(def); 1036 } 1037 1038 const string s = spec->getReturn(i1, i2, i3, i4); 1039 if (!s.empty() && s != "void") { 1040 ParameterDefinition* def = new ParameterDefinition(); 1041 // Adding "*" tells the parse method it's an output. 1042 def->parseParameterDefinition(s, true, &mInputCount, &mOutputCount); 1043 mReturnIndex = mParams.size(); 1044 mParams.push_back(def); 1045 } 1046 1047 mRsKernelName = "test" + capitalize(mName); 1048 mJavaArgumentsClassName = "Arguments"; 1049 mJavaArgumentsNClassName = "Arguments"; 1050 mJavaCheckMethodName = "check" + capitalize(mCleanName); 1051 mJavaVerifyMethodName = "verifyResults" + capitalize(mCleanName); 1052 for (int i = 0; i < (int)mParams.size(); i++) { 1053 const ParameterDefinition& p = *mParams[i]; 1054 mRsKernelName += capitalize(p.rsType); 1055 mJavaArgumentsClassName += capitalize(p.rsBaseType); 1056 mJavaArgumentsNClassName += capitalize(p.rsBaseType); 1057 if (p.mVectorSize != "1") { 1058 mJavaArgumentsNClassName += "N"; 1059 } 1060 mJavaCheckMethodName += capitalize(p.rsType); 1061 mJavaVerifyMethodName += capitalize(p.rsType); 1062 } 1063 mJavaVerifierComputeMethodName = "compute" + capitalize(mCleanName); 1064} 1065 1066// TODO Remove once we have long/double copyTo/copyFrom 1067bool Permutation::hasLongOrDoubleParameter() const { 1068 for (size_t i = 0; i < mParams.size(); i++) { 1069 const ParameterDefinition& p = *mParams[i]; 1070 if (p.javaBaseType == "long" || p.javaBaseType == "double") { 1071 return true; 1072 } 1073 } 1074 return false; 1075} 1076 1077void Permutation::writeFiles(ofstream& headerFile, ofstream& rsFile, ofstream& javaFile, 1078 int versionOfTestFiles) { 1079 writeHeaderSection(headerFile); 1080 if (hasLongOrDoubleParameter()) { 1081 printf("Warning: skipping a test for %s as we don't support long or double arguments (due " 1082 "to Allocation not supporting them).\n", 1083 mName.c_str()); 1084 return; 1085 } 1086 1087 if (mSpecification->relevantForVersion(versionOfTestFiles) && mTest != "none") { 1088 writeRsSection(rsFile); 1089 writeJavaSection(javaFile); 1090 } 1091} 1092 1093void Permutation::writeHeaderSection(ofstream& file) const { 1094 int minVersion = mSpecification->getMinVersion(); 1095 int maxVersion = mSpecification->getMaxVersion(); 1096 bool hasVersion = minVersion || maxVersion; 1097 1098 if (hasVersion) { 1099 if (maxVersion) { 1100 file << "#if (defined(RS_VERSION) && (RS_VERSION >= " << minVersion 1101 << ") && (RS_VERSION <= " << maxVersion << "))\n"; 1102 } else { 1103 file << "#if (defined(RS_VERSION) && (RS_VERSION >= " << minVersion << "))\n"; 1104 } 1105 } 1106 1107 file << "/*\n"; 1108 for (size_t ct = 0; ct < mComment.size(); ct++) { 1109 if (!mComment[ct].empty()) { 1110 file << " * " << mComment[ct] << "\n"; 1111 } else { 1112 file << " *\n"; 1113 } 1114 } 1115 file << " *\n"; 1116 if (minVersion || maxVersion) { 1117 if (maxVersion) { 1118 file << " * Suppored by API versions " << minVersion << " - " << maxVersion << "\n"; 1119 } else { 1120 file << " * Supported by API versions " << minVersion << " and newer.\n"; 1121 } 1122 } 1123 file << " */\n"; 1124 if (mInline.size() > 0) { 1125 file << "static "; 1126 } else { 1127 file << "extern "; 1128 } 1129 if (mReturnIndex >= 0) { 1130 file << mParams[mReturnIndex]->rsType; 1131 } else { 1132 file << "void"; 1133 } 1134 file << " __attribute__(("; 1135 if (mOutputCount <= 1) { 1136 file << "const, "; 1137 } 1138 file << "overloadable))"; 1139 file << mName; 1140 file << "("; 1141 bool needComma = false; 1142 for (int i = 0; i < (int)mParams.size(); i++) { 1143 if (i != mReturnIndex) { 1144 const ParameterDefinition& p = *mParams[i]; 1145 if (needComma) { 1146 file << ", "; 1147 } 1148 file << p.rsType; 1149 if (p.isOutParameter) { 1150 file << "*"; 1151 } 1152 if (!p.specName.empty()) { 1153 file << " " << p.specName; 1154 } 1155 needComma = true; 1156 } 1157 } 1158 if (mInline.size() > 0) { 1159 file << ") {\n"; 1160 for (size_t ct = 0; ct < mInline.size(); ct++) { 1161 file << " " << mInline[ct].c_str() << "\n"; 1162 } 1163 file << "}\n"; 1164 } else { 1165 file << ");\n"; 1166 } 1167 if (hasVersion) { 1168 file << "#endif\n"; 1169 } 1170 file << "\n"; 1171} 1172 1173/* Write the section of the .rs file for this permutation. 1174 * 1175 * We communicate the extra input and output parameters via global allocations. 1176 * For example, if we have a function that takes three arguments, two for input 1177 * and one for output: 1178 * 1179 * start: 1180 * name: gamn 1181 * ret: float3 1182 * arg: float3 a 1183 * arg: int b 1184 * arg: float3 *c 1185 * end: 1186 * 1187 * We'll produce: 1188 * 1189 * rs_allocation gAllocInB; 1190 * rs_allocation gAllocOutC; 1191 * 1192 * float3 __attribute__((kernel)) test_gamn_float3_int_float3(float3 inA, unsigned int x) { 1193 * int inB; 1194 * float3 outC; 1195 * float2 out; 1196 * inB = rsGetElementAt_int(gAllocInB, x); 1197 * out = gamn(a, in_b, &outC); 1198 * rsSetElementAt_float4(gAllocOutC, &outC, x); 1199 * return out; 1200 * } 1201 * 1202 * We avoid re-using x and y from the definition because these have reserved 1203 * meanings in a .rs file. 1204 */ 1205void Permutation::writeRsSection(ofstream& rs) const { 1206 // Write the allocation declarations we'll need. 1207 for (int i = 0; i < (int)mParams.size(); i++) { 1208 const ParameterDefinition& p = *mParams[i]; 1209 // Don't need allocation for one input and one return value. 1210 if (i != mReturnIndex && i != mFirstInputIndex) { 1211 mFunction->writeRsAllocationDefinition(p); 1212 } 1213 } 1214 rs << "\n"; 1215 1216 // Write the function header. 1217 if (mReturnIndex >= 0) { 1218 rs << mParams[mReturnIndex]->rsType; 1219 } else { 1220 rs << "void"; 1221 } 1222 rs << " __attribute__((kernel)) " << mRsKernelName; 1223 rs << "("; 1224 bool needComma = false; 1225 if (mFirstInputIndex >= 0) { 1226 rs << mParams[mFirstInputIndex]->rsType << " " << mParams[mFirstInputIndex]->variableName; 1227 needComma = true; 1228 } 1229 if (mOutputCount > 1 || mInputCount > 1) { 1230 if (needComma) { 1231 rs << ", "; 1232 } 1233 rs << "unsigned int x"; 1234 } 1235 rs << ") {\n"; 1236 1237 // Write the local variable declarations and initializations. 1238 for (int i = 0; i < (int)mParams.size(); i++) { 1239 if (i == mFirstInputIndex || i == mReturnIndex) { 1240 continue; 1241 } 1242 const ParameterDefinition& p = *mParams[i]; 1243 rs << tab(1) << p.rsType << " " << p.variableName; 1244 if (p.isOutParameter) { 1245 rs << " = 0;\n"; 1246 } else { 1247 rs << " = rsGetElementAt_" << p.rsType << "(" << p.rsAllocName << ", x);\n"; 1248 } 1249 } 1250 1251 // Write the function call. 1252 if (mReturnIndex >= 0) { 1253 if (mOutputCount > 1) { 1254 rs << tab(1) << mParams[mReturnIndex]->rsType << " " 1255 << mParams[mReturnIndex]->variableName << " = "; 1256 } else { 1257 rs << tab(1) << "return "; 1258 } 1259 } 1260 rs << mName << "("; 1261 needComma = false; 1262 for (int i = 0; i < (int)mParams.size(); i++) { 1263 const ParameterDefinition& p = *mParams[i]; 1264 if (i == mReturnIndex) { 1265 continue; 1266 } 1267 if (needComma) { 1268 rs << ", "; 1269 } 1270 if (p.isOutParameter) { 1271 rs << "&"; 1272 } 1273 rs << p.variableName; 1274 needComma = true; 1275 } 1276 rs << ");\n"; 1277 1278 if (mOutputCount > 1) { 1279 // Write setting the extra out parameters into the allocations. 1280 for (int i = 0; i < (int)mParams.size(); i++) { 1281 const ParameterDefinition& p = *mParams[i]; 1282 if (p.isOutParameter && i != mReturnIndex) { 1283 rs << tab(1) << "rsSetElementAt_" << p.rsType << "(" << p.rsAllocName << ", "; 1284 if (passByAddressToSet(p.variableName)) { 1285 rs << "&"; 1286 } 1287 rs << p.variableName << ", x);\n"; 1288 } 1289 } 1290 if (mReturnIndex >= 0) { 1291 rs << tab(1) << "return " << mParams[mReturnIndex]->variableName << ";\n"; 1292 } 1293 } 1294 rs << "}\n"; 1295} 1296 1297bool Permutation::passByAddressToSet(const string& name) const { 1298 string s = name; 1299 int last = s.size() - 1; 1300 char lastChar = s[last]; 1301 return lastChar >= '0' && lastChar <= '9'; 1302} 1303 1304void Permutation::writeJavaSection(ofstream& file) const { 1305 // By default, we test the results using item by item comparison. 1306 if (mTest == "scalar") { 1307 writeJavaArgumentClass(file, true); 1308 writeJavaCheckMethod(file, true); 1309 writeJavaVerifyScalarMethod(file); 1310 } else if (mTest == "vector") { 1311 writeJavaArgumentClass(file, false); 1312 writeJavaCheckMethod(file, true); 1313 writeJavaVerifyVectorMethod(file); 1314 } else if (mTest == "noverify") { 1315 writeJavaCheckMethod(file, false); 1316 } 1317 1318 // Register the check method to be called. This code will be written at the end. 1319 mFunction->addJavaCheckCall(mJavaCheckMethodName + "();"); 1320} 1321 1322void Permutation::writeJavaArgumentClass(ofstream& file, bool scalar) const { 1323 string name; 1324 if (scalar) { 1325 name = mJavaArgumentsClassName; 1326 } else { 1327 name = mJavaArgumentsNClassName; 1328 } 1329 string s; 1330 s += tab(1) + "public class " + name + " {\n"; 1331 for (size_t i = 0; i < mParams.size(); i++) { 1332 const ParameterDefinition& p = *mParams[i]; 1333 s += tab(2) + "public " + p.javaBaseType; 1334 if (!scalar && p.mVectorSize != "1") { 1335 s += "[]"; 1336 } 1337 s += " " + p.variableName + ";\n"; 1338 } 1339 s += "\n"; 1340 s += tab(2) + "public int ulf;\n"; 1341 s += tab(2) + "public int ulfRelaxed;\n"; 1342 s += tab(1) + "}\n\n"; 1343 1344 mFunction->writeJavaArgumentClassDefinition(name, s); 1345} 1346 1347void Permutation::writeJavaCheckMethod(ofstream& file, bool generateCallToVerify) const { 1348 file << tab(1) << "private void " << mJavaCheckMethodName << "() {\n"; 1349 // Generate the input allocations and initialization. 1350 for (size_t i = 0; i < mParams.size(); i++) { 1351 const ParameterDefinition& p = *mParams[i]; 1352 if (!p.isOutParameter) { 1353 writeJavaInputAllocationDefinition(file, tab(2), p); 1354 } 1355 } 1356 // Enforce ordering if needed. 1357 for (size_t i = 0; i < mParams.size(); i++) { 1358 const ParameterDefinition& p = *mParams[i]; 1359 if (!p.isOutParameter && !p.smallerParameter.empty()) { 1360 string smallerAlloc = "in" + capitalize(p.smallerParameter); 1361 file << tab(2) << "enforceOrdering(" << smallerAlloc << ", " << p.javaAllocName 1362 << ");\n"; 1363 } 1364 } 1365 writeJavaCallToRs(file, false, generateCallToVerify); 1366 writeJavaCallToRs(file, true, generateCallToVerify); 1367 file << tab(1) << "}\n\n"; 1368} 1369 1370void Permutation::writeJavaInputAllocationDefinition(ofstream& file, const string& indent, 1371 const ParameterDefinition& param) const { 1372 string dataType; 1373 char vectorSize; 1374 convertToRsType(param.rsType, &dataType, &vectorSize); 1375 long seed = hashString(mJavaCheckMethodName + param.javaAllocName); 1376 file << indent << "Allocation " << param.javaAllocName 1377 << " = createRandomAllocation(mRS, Element.DataType." << dataType << ", " << vectorSize 1378 << ", 0x" << std::hex << seed << "l"; 1379 if (!param.minValue.empty()) { 1380 file << ", " << param.minValue << ", " << param.maxValue; 1381 } else { 1382 file << ", false"; // TODO set to false only for native 1383 } 1384 file << ");\n"; 1385} 1386 1387void Permutation::writeJavaOutputAllocationDefinition(ofstream& file, const string& indent, 1388 const ParameterDefinition& param) const { 1389 string dataType; 1390 char vectorSize; 1391 convertToRsType(param.rsType, &dataType, &vectorSize); 1392 file << indent << "Allocation " << param.javaAllocName << " = Allocation.createSized(mRS, " 1393 << "getElement(mRS, Element.DataType." << dataType << ", " << vectorSize 1394 << "), INPUTSIZE);\n"; 1395} 1396 1397// Converts float2 to FLOAT_32 and 2, etc. 1398void Permutation::convertToRsType(const string& name, string* dataType, char* vectorSize) const { 1399 string s = name; 1400 int last = s.size() - 1; 1401 char lastChar = s[last]; 1402 if (lastChar >= '1' && lastChar <= '4') { 1403 s.erase(last); 1404 *vectorSize = lastChar; 1405 } else { 1406 *vectorSize = '1'; 1407 } 1408 dataType->clear(); 1409 for (int i = 0; i < NUM_TYPES; i++) { 1410 if (s == TYPES[i].cType) { 1411 *dataType = TYPES[i].rsDataType; 1412 break; 1413 } 1414 } 1415} 1416 1417void Permutation::writeJavaVerifyScalarMethod(ofstream& file) const { 1418 writeJavaVerifyFunctionHeader(file); 1419 string vectorSize = "1"; 1420 for (size_t i = 0; i < mParams.size(); i++) { 1421 const ParameterDefinition& p = *mParams[i]; 1422 writeJavaArrayInitialization(file, p); 1423 if (p.mVectorSize != "1" && p.mVectorSize != vectorSize) { 1424 if (vectorSize == "1") { 1425 vectorSize = p.mVectorSize; 1426 } else { 1427 printf("Yikes, had vector %s and %s\n", vectorSize.c_str(), p.mVectorSize.c_str()); 1428 } 1429 } 1430 } 1431 1432 file << tab(2) << "for (int i = 0; i < INPUTSIZE; i++) {\n"; 1433 file << tab(3) << "for (int j = 0; j < " << vectorSize << " ; j++) {\n"; 1434 1435 file << tab(4) << "// Extract the inputs.\n"; 1436 file << tab(4) << mJavaArgumentsClassName << " args = new " << mJavaArgumentsClassName 1437 << "();\n"; 1438 for (size_t i = 0; i < mParams.size(); i++) { 1439 const ParameterDefinition& p = *mParams[i]; 1440 if (!p.isOutParameter) { 1441 file << tab(4) << "args." << p.variableName << " = " << p.javaArrayName << "[i"; 1442 if (p.vectorWidth != "1") { 1443 file << " * " << p.vectorWidth << " + j"; 1444 } 1445 file << "];\n"; 1446 } 1447 } 1448 1449 file << tab(4) << "// Figure out what the outputs should have been.\n"; 1450 file << tab(4) << "CoreMathVerifier." << mJavaVerifierComputeMethodName << "(args);\n"; 1451 file << tab(4) << "int ulf = relaxed ? args.ulfRelaxed : args.ulf;\n"; 1452 1453 file << tab(4) << "// Figure out what the outputs should have been.\n"; 1454 file << tab(4) << "boolean valid = true;\n"; 1455 file << tab(4) << "int neededUlf = 0;\n"; 1456 for (size_t i = 0; i < mParams.size(); i++) { 1457 const ParameterDefinition& p = *mParams[i]; 1458 if (p.isOutParameter) { 1459 writeJavaTestOneValue(file, 4, p.rsBaseType, "args." + p.variableName, 1460 p.javaArrayName + "[i * " + p.vectorWidth + " + j]"); 1461 } 1462 } 1463 1464 file << tab(4) << "if (!valid) {\n"; 1465 file << tab(5) << "StringBuilder message = new StringBuilder();\n"; 1466 for (size_t i = 0; i < mParams.size(); i++) { 1467 const ParameterDefinition& p = *mParams[i]; 1468 if (p.isOutParameter) { 1469 writeJavaAppendOutputToMessage(file, 5, p.rsBaseType, p.variableName, 1470 "args." + p.variableName, 1471 p.javaArrayName + "[i * " + p.vectorWidth + " + j]"); 1472 } else { 1473 writeJavaAppendInputToMessage(file, 5, p.rsBaseType, p.variableName, 1474 "args." + p.variableName); 1475 } 1476 } 1477 1478 file << tab(5) << "assertTrue(\"Incorrect output for " << mJavaCheckMethodName << "\" +\n"; 1479 file << tab(7) << "(relaxed ? \"_relaxed\" : \"\") + \":\\n\" + message.toString(), valid);\n"; 1480 file << tab(4) << "}\n"; 1481 file << tab(3) << "}\n"; 1482 file << tab(2) << "}\n"; 1483 file << tab(1) << "}\n\n"; 1484} 1485 1486void Permutation::writeJavaVerifyFunctionHeader(ofstream& file) const { 1487 file << tab(1) << "private void " << mJavaVerifyMethodName << "("; 1488 for (size_t i = 0; i < mParams.size(); i++) { 1489 const ParameterDefinition& p = *mParams[i]; 1490 file << "Allocation " << p.javaAllocName << ", "; 1491 } 1492 file << "boolean relaxed) {\n"; 1493} 1494 1495void Permutation::writeJavaTestOneValue(ofstream& file, int indent, const string& rsBaseType, 1496 const string& expected, const string& actual) const { 1497 if (rsBaseType[0] == 'f') { 1498 writeJavaComputeNeededUlf(file, indent, expected, actual); 1499 file << tab(indent) << "if (neededUlf > ulf) {\n"; 1500 file << tab(indent + 1) << "valid = false;\n"; 1501 file << tab(indent) << "}\n"; 1502 } else { 1503 file << tab(indent) << "if (" + expected + " != " + actual + ") {\n"; 1504 file << tab(indent + 1) << "valid = false;\n"; 1505 file << tab(indent) << "}\n"; 1506 } 1507} 1508 1509void Permutation::writeJavaAppendOutputToMessage(ofstream& file, int indent, 1510 const string& rsBaseType, const string& name, 1511 const string& expected, 1512 const string& actual) const { 1513 writeJavaAppendVariableToMessage(file, indent, rsBaseType, "Expected output " + name, expected); 1514 writeJavaAppendNewLineToMessage(file, indent); 1515 writeJavaAppendVariableToMessage(file, indent, rsBaseType, "Actual output " + name, actual); 1516 if (rsBaseType[0] == 'f') { 1517 writeJavaComputeNeededUlf(file, indent, expected, actual); 1518 file << tab(indent) << "if (neededUlf > ulf) {\n"; 1519 file << tab(indent + 1) << "message.append(String.format(\" FAILED, ulf needed %d, " 1520 "specified %d\", neededUlf, ulf));\n"; 1521 file << tab(indent) << "}\n"; 1522 } else { 1523 file << tab(indent) << "if (" + expected + " != " + actual + ") {\n"; 1524 file << tab(indent + 1) << "message.append(\" FAIL\");\n"; 1525 file << tab(indent) << "}\n"; 1526 } 1527 writeJavaAppendNewLineToMessage(file, indent); 1528} 1529 1530void Permutation::writeJavaAppendInputToMessage(ofstream& file, int indent, 1531 const string& rsBaseType, const string& name, 1532 const string& actual) const { 1533 writeJavaAppendVariableToMessage(file, indent, rsBaseType, "Input " + name, actual); 1534 writeJavaAppendNewLineToMessage(file, indent); 1535} 1536 1537void Permutation::writeJavaComputeNeededUlf(ofstream& file, int indent, const string& expected, 1538 const string& actual) const { 1539 file << tab(indent) << "neededUlf = (int) (Math.abs(" << expected << " - " << actual 1540 << ") / Math.ulp(" << expected << ") + 0.5);\n"; 1541} 1542 1543void Permutation::writeJavaAppendNewLineToMessage(ofstream& file, int indent) const { 1544 file << tab(indent) << "message.append(\"\\n\");\n"; 1545} 1546 1547void Permutation::writeJavaAppendVariableToMessage(ofstream& file, int indent, 1548 const string& rsBaseType, const string& legend, 1549 const string& value) const { 1550 file << tab(indent) << "message.append(String.format(\"" + legend + ": "; 1551 if (rsBaseType[0] == 'f') { 1552 file << "%14.8g %8x %15a\",\n"; 1553 file << tab(indent + 2) << value << ", " 1554 << "Float.floatToRawIntBits(" << value << "), " << value; 1555 } else if (rsBaseType[0] == 'u') { 1556 file << "0x%x\",\n"; 1557 file << tab(indent + 2) << value; 1558 } else { 1559 file << "%d\",\n"; 1560 file << tab(indent + 2) << value; 1561 } 1562 file << "));\n"; 1563} 1564 1565void Permutation::writeJavaVectorComparison(ofstream& file, int indent, 1566 const ParameterDefinition& p) const { 1567 if (p.mVectorSize == "1") { 1568 writeJavaTestOneValue(file, indent, p.javaBaseType, "args." + p.variableName, 1569 p.javaArrayName + "[i]"); 1570 1571 } else { 1572 file << tab(indent) << "for (int j = 0; j < " << p.mVectorSize << " ; j++) {\n"; 1573 writeJavaTestOneValue(file, indent + 1, p.rsBaseType, "args." + p.variableName + "[j]", 1574 p.javaArrayName + "[i * " + p.vectorWidth + " + j]"); 1575 file << tab(indent) << "}\n"; 1576 } 1577} 1578 1579void Permutation::writeJavaAppendVectorInputToMessage(ofstream& file, int indent, 1580 const ParameterDefinition& p) const { 1581 if (p.mVectorSize == "1") { 1582 writeJavaAppendInputToMessage(file, indent, p.rsBaseType, p.variableName, 1583 p.javaArrayName + "[i]"); 1584 } else { 1585 file << tab(indent) << "for (int j = 0; j < " << p.mVectorSize << " ; j++) {\n"; 1586 writeJavaAppendInputToMessage(file, indent + 1, p.rsBaseType, p.variableName, 1587 p.javaArrayName + "[i * " + p.vectorWidth + " + j]"); 1588 file << tab(indent) << "}\n"; 1589 } 1590} 1591 1592void Permutation::writeJavaAppendVectorOutputToMessage(ofstream& file, int indent, 1593 const ParameterDefinition& p) const { 1594 if (p.mVectorSize == "1") { 1595 writeJavaAppendOutputToMessage(file, indent, p.rsBaseType, p.variableName, 1596 "args." + p.variableName, p.javaArrayName + "[i]"); 1597 1598 } else { 1599 file << tab(indent) << "for (int j = 0; j < " << p.mVectorSize << " ; j++) {\n"; 1600 writeJavaAppendOutputToMessage(file, indent + 1, p.rsBaseType, p.variableName, 1601 "args." + p.variableName + "[j]", 1602 p.javaArrayName + "[i * " + p.vectorWidth + " + j]"); 1603 file << tab(indent) << "}\n"; 1604 } 1605} 1606 1607void Permutation::writeJavaVerifyVectorMethod(ofstream& file) const { 1608 writeJavaVerifyFunctionHeader(file); 1609 for (size_t i = 0; i < mParams.size(); i++) { 1610 const ParameterDefinition& p = *mParams[i]; 1611 writeJavaArrayInitialization(file, p); 1612 } 1613 file << tab(2) + "for (int i = 0; i < INPUTSIZE; i++) {\n"; 1614 file << tab(3) << mJavaArgumentsNClassName << " args = new " << mJavaArgumentsNClassName 1615 << "();\n"; 1616 1617 file << tab(3) << "// Create the appropriate sized arrays in args\n"; 1618 for (size_t i = 0; i < mParams.size(); i++) { 1619 const ParameterDefinition& p = *mParams[i]; 1620 if (p.mVectorSize != "1") { 1621 file << tab(3) << "args." << p.variableName << " = new " << p.javaBaseType << "[" 1622 << p.mVectorSize << "];\n"; 1623 } 1624 } 1625 1626 file << tab(3) << "// Fill args with the input values\n"; 1627 for (size_t i = 0; i < mParams.size(); i++) { 1628 const ParameterDefinition& p = *mParams[i]; 1629 if (!p.isOutParameter) { 1630 if (p.mVectorSize == "1") { 1631 file << tab(3) << "args." << p.variableName << " = " << p.javaArrayName + "[i]" 1632 << ";\n"; 1633 } else { 1634 file << tab(3) << "for (int j = 0; j < " << p.mVectorSize << " ; j++) {\n"; 1635 file << tab(4) << "args." << p.variableName + "[j] = " 1636 << p.javaArrayName + "[i * " + p.vectorWidth + " + j]" 1637 << ";\n"; 1638 file << tab(3) << "}\n"; 1639 } 1640 } 1641 } 1642 file << tab(3) << "CoreMathVerifier." << mJavaVerifierComputeMethodName << "(args);\n\n"; 1643 file << tab(3) << "int ulf = relaxed ? args.ulfRelaxed : args.ulf;\n"; 1644 1645 file << tab(3) << "// Compare the expected outputs to the actual values returned by RS.\n"; 1646 file << tab(3) << "boolean valid = true;\n"; 1647 file << tab(3) << "int neededUlf;\n"; 1648 for (size_t i = 0; i < mParams.size(); i++) { 1649 const ParameterDefinition& p = *mParams[i]; 1650 if (p.isOutParameter) { 1651 writeJavaVectorComparison(file, 3, p); 1652 } 1653 } 1654 1655 file << tab(3) << "if (!valid) {\n"; 1656 file << tab(4) << "StringBuilder message = new StringBuilder();\n"; 1657 for (size_t i = 0; i < mParams.size(); i++) { 1658 const ParameterDefinition& p = *mParams[i]; 1659 if (p.isOutParameter) { 1660 writeJavaAppendVectorOutputToMessage(file, 4, p); 1661 } else { 1662 writeJavaAppendVectorInputToMessage(file, 4, p); 1663 } 1664 } 1665 1666 file << tab(4) << "assertTrue(\"Incorrect output for " << mJavaCheckMethodName << "\" +\n"; 1667 file << tab(6) << "(relaxed ? \"_relaxed\" : \"\") + \":\\n\" + message.toString(), valid);\n"; 1668 file << tab(3) << "}\n"; 1669 file << tab(2) << "}\n"; 1670 file << tab(1) << "}\n\n"; 1671} 1672 1673void Permutation::writeJavaCallToRs(ofstream& file, bool relaxed, bool generateCallToVerify) const { 1674 string script = "script"; 1675 if (relaxed) { 1676 script += "Relaxed"; 1677 } 1678 1679 file << tab(2) << "try {\n"; 1680 for (size_t i = 0; i < mParams.size(); i++) { 1681 const ParameterDefinition& p = *mParams[i]; 1682 if (p.isOutParameter) { 1683 writeJavaOutputAllocationDefinition(file, tab(3), p); 1684 } 1685 } 1686 1687 for (int i = 0; i < (int)mParams.size(); i++) { 1688 const ParameterDefinition& p = *mParams[i]; 1689 if (i != mReturnIndex && i != mFirstInputIndex) { 1690 file << tab(3) << script << ".set_" << p.rsAllocName << "(" << p.javaAllocName 1691 << ");\n"; 1692 } 1693 } 1694 1695 file << tab(3) << script << ".forEach_" << mRsKernelName << "("; 1696 bool needComma = false; 1697 if (mFirstInputIndex >= 0) { 1698 file << mParams[mFirstInputIndex]->javaAllocName; 1699 needComma = true; 1700 } 1701 if (mReturnIndex >= 0) { 1702 if (needComma) { 1703 file << ", "; 1704 } 1705 file << mParams[mReturnIndex]->variableName << ");\n"; 1706 } 1707 1708 if (generateCallToVerify) { 1709 file << tab(3) << mJavaVerifyMethodName << "("; 1710 for (size_t i = 0; i < mParams.size(); i++) { 1711 const ParameterDefinition& p = *mParams[i]; 1712 file << p.variableName << ", "; 1713 } 1714 1715 if (relaxed) { 1716 file << "true"; 1717 } else { 1718 file << "false"; 1719 } 1720 file << ");\n"; 1721 } 1722 file << tab(2) << "} catch (Exception e) {\n"; 1723 file << tab(3) << "throw new RSRuntimeException(\"RenderScript. Can't invoke forEach_" 1724 << mRsKernelName << ": \" + e.toString());\n"; 1725 file << tab(2) << "}\n"; 1726} 1727 1728} // namespace 1729 1730int main(int argc, char* argv[]) { 1731 int versionOfTestFiles = 0; 1732 vector<string> specFileNames; 1733 if (!parseCommandLine(argc, argv, &versionOfTestFiles, &specFileNames)) { 1734 printf("Usage: gen_runtime spec_file [spec_file...] [-v version_of_test_files]\n"); 1735 return -1; 1736 } 1737 int result = 0; 1738 for (size_t i = 0; i < specFileNames.size(); i++) { 1739 SpecFile specFile(specFileNames[i]); 1740 if (!specFile.process(versionOfTestFiles)) { 1741 result = -1; 1742 } 1743 } 1744 return result; 1745} 1746