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