slang_rs_export_foreach.cpp revision ab94bccca64c9b126cbd1b732aa5e681d8639b99
1/* 2 * Copyright 2011-2012, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "slang_rs_export_foreach.h" 18 19#include <string> 20 21#include "clang/AST/ASTContext.h" 22#include "clang/AST/Attr.h" 23#include "clang/AST/Decl.h" 24#include "clang/AST/TypeLoc.h" 25 26#include "llvm/IR/DerivedTypes.h" 27 28#include "bcinfo/MetadataExtractor.h" 29 30#include "slang_assert.h" 31#include "slang_rs_context.h" 32#include "slang_rs_export_type.h" 33#include "slang_version.h" 34 35namespace { 36 37const size_t RS_KERNEL_INPUT_LIMIT = 8; // see frameworks/base/libs/rs/cpu_ref/rsCpuCoreRuntime.h 38 39enum SpecialParameterKind { 40 SPK_INT, // 'int' or 'unsigned int' 41 SPK_CTXT, // rs_kernel_context 42}; 43 44struct SpecialParameter { 45 const char *name; 46 bcinfo::MetadataSignatureBitval bitval; 47 SpecialParameterKind kind; 48 SlangTargetAPI minAPI; 49}; 50 51// Table entries are in the order parameters must occur in a kernel parameter list. 52const SpecialParameter specialParameterTable[] = { 53 { "ctxt", bcinfo::MD_SIG_Ctxt, SPK_CTXT, SLANG_23_TARGET_API }, 54 { "x", bcinfo::MD_SIG_X, SPK_INT, SLANG_MINIMUM_TARGET_API }, 55 { "y", bcinfo::MD_SIG_Y, SPK_INT, SLANG_MINIMUM_TARGET_API }, 56 { "z", bcinfo::MD_SIG_Z, SPK_INT, SLANG_23_TARGET_API }, 57 { nullptr, bcinfo::MD_SIG_None, SPK_INT, SLANG_MINIMUM_TARGET_API }, // marks end of table 58}; 59 60// If the specified name matches the name of an entry in 61// specialParameterTable, return the corresponding table index; 62// otherwise return -1. 63int lookupSpecialParameter(const llvm::StringRef name) { 64 for (int i = 0; specialParameterTable[i].name != nullptr; ++i) 65 if (name.equals(specialParameterTable[i].name)) 66 return i; 67 return -1; 68} 69 70// Return a comma-separated list of names in specialParameterTable 71// that are available at the specified API level. 72std::string listSpecialParameters(unsigned int api) { 73 std::string ret; 74 bool first = true; 75 for (int i = 0; specialParameterTable[i].name != nullptr; ++i) { 76 if (specialParameterTable[i].minAPI > api) 77 continue; 78 if (first) 79 first = false; 80 else 81 ret += ", "; 82 ret += "'"; 83 ret += specialParameterTable[i].name; 84 ret += "'"; 85 } 86 return ret; 87} 88 89} 90 91namespace slang { 92 93// This function takes care of additional validation and construction of 94// parameters related to forEach_* reflection. 95bool RSExportForEach::validateAndConstructParams( 96 RSContext *Context, const clang::FunctionDecl *FD) { 97 slangAssert(Context && FD); 98 bool valid = true; 99 100 numParams = FD->getNumParams(); 101 102 if (Context->getTargetAPI() < SLANG_JB_TARGET_API) { 103 // Before JellyBean, we allowed only one kernel per file. It must be called "root". 104 if (!isRootRSFunc(FD)) { 105 Context->ReportError(FD->getLocation(), 106 "Non-root compute kernel %0() is " 107 "not supported in SDK levels %1-%2") 108 << FD->getName() << SLANG_MINIMUM_TARGET_API 109 << (SLANG_JB_TARGET_API - 1); 110 return false; 111 } 112 } 113 114 mResultType = FD->getReturnType().getCanonicalType(); 115 // Compute kernel functions are defined differently when the 116 // "__attribute__((kernel))" is set. 117 if (FD->hasAttr<clang::KernelAttr>()) { 118 valid |= validateAndConstructKernelParams(Context, FD); 119 } else { 120 valid |= validateAndConstructOldStyleParams(Context, FD); 121 } 122 123 valid |= setSignatureMetadata(Context, FD); 124 return valid; 125} 126 127bool RSExportForEach::validateAndConstructOldStyleParams( 128 RSContext *Context, const clang::FunctionDecl *FD) { 129 slangAssert(Context && FD); 130 // If numParams is 0, we already marked this as a graphics root(). 131 slangAssert(numParams > 0); 132 133 bool valid = true; 134 135 // Compute kernel functions of this style are required to return a void type. 136 clang::ASTContext &C = Context->getASTContext(); 137 if (mResultType != C.VoidTy) { 138 Context->ReportError(FD->getLocation(), 139 "Compute kernel %0() is required to return a " 140 "void type") 141 << FD->getName(); 142 valid = false; 143 } 144 145 // Validate remaining parameter types 146 147 size_t IndexOfFirstSpecialParameter = numParams; 148 valid |= validateSpecialParameters(Context, FD, &IndexOfFirstSpecialParameter); 149 150 // Validate the non-special parameters, which should all be found before the 151 // first special parameter. 152 for (size_t i = 0; i < IndexOfFirstSpecialParameter; i++) { 153 const clang::ParmVarDecl *PVD = FD->getParamDecl(i); 154 clang::QualType QT = PVD->getType().getCanonicalType(); 155 156 if (!QT->isPointerType()) { 157 Context->ReportError(PVD->getLocation(), 158 "Compute kernel %0() cannot have non-pointer " 159 "parameters besides (%1). Parameter '%2' is " 160 "of type: '%3'") 161 << FD->getName() << listSpecialParameters(Context->getTargetAPI()) 162 << PVD->getName() << PVD->getType().getAsString(); 163 valid = false; 164 continue; 165 } 166 167 // The only non-const pointer should be out. 168 if (!QT->getPointeeType().isConstQualified()) { 169 if (mOut == nullptr) { 170 mOut = PVD; 171 } else { 172 Context->ReportError(PVD->getLocation(), 173 "Compute kernel %0() can only have one non-const " 174 "pointer parameter. Parameters '%1' and '%2' are " 175 "both non-const.") 176 << FD->getName() << mOut->getName() << PVD->getName(); 177 valid = false; 178 } 179 } else { 180 if (mIns.empty() && mOut == nullptr) { 181 mIns.push_back(PVD); 182 } else if (mUsrData == nullptr) { 183 mUsrData = PVD; 184 } else { 185 Context->ReportError( 186 PVD->getLocation(), 187 "Unexpected parameter '%0' for compute kernel %1()") 188 << PVD->getName() << FD->getName(); 189 valid = false; 190 } 191 } 192 } 193 194 if (mIns.empty() && !mOut) { 195 Context->ReportError(FD->getLocation(), 196 "Compute kernel %0() must have at least one " 197 "parameter for in or out") 198 << FD->getName(); 199 valid = false; 200 } 201 202 return valid; 203} 204 205bool RSExportForEach::validateAndConstructKernelParams( 206 RSContext *Context, const clang::FunctionDecl *FD) { 207 slangAssert(Context && FD); 208 bool valid = true; 209 clang::ASTContext &C = Context->getASTContext(); 210 211 if (Context->getTargetAPI() < SLANG_JB_MR1_TARGET_API) { 212 Context->ReportError(FD->getLocation(), 213 "Compute kernel %0() targeting SDK levels " 214 "%1-%2 may not use pass-by-value with " 215 "__attribute__((kernel))") 216 << FD->getName() << SLANG_MINIMUM_TARGET_API 217 << (SLANG_JB_MR1_TARGET_API - 1); 218 return false; 219 } 220 221 // Denote that we are indeed a pass-by-value kernel. 222 mIsKernelStyle = true; 223 mHasReturnType = (mResultType != C.VoidTy); 224 225 if (mResultType->isPointerType()) { 226 Context->ReportError( 227 FD->getTypeSpecStartLoc(), 228 "Compute kernel %0() cannot return a pointer type: '%1'") 229 << FD->getName() << mResultType.getAsString(); 230 valid = false; 231 } 232 233 // Validate remaining parameter types 234 235 size_t IndexOfFirstSpecialParameter = numParams; 236 valid |= validateSpecialParameters(Context, FD, &IndexOfFirstSpecialParameter); 237 238 // Validate the non-special parameters, which should all be found before the 239 // first special. 240 for (size_t i = 0; i < IndexOfFirstSpecialParameter; i++) { 241 const clang::ParmVarDecl *PVD = FD->getParamDecl(i); 242 243 /* 244 * FIXME: Change this to a test against an actual API version when the 245 * multi-input feature is officially supported. 246 */ 247 if (Context->getTargetAPI() == SLANG_DEVELOPMENT_TARGET_API || i == 0) { 248 if (i >= RS_KERNEL_INPUT_LIMIT) { 249 Context->ReportError(PVD->getLocation(), 250 "Invalid parameter '%0' for compute kernel %1(). " 251 "Kernels targeting SDK levels %2-%3 may not use " 252 "more than %4 input parameters.") << PVD->getName() << 253 FD->getName() << SLANG_MINIMUM_TARGET_API << 254 SLANG_MAXIMUM_TARGET_API << int(RS_KERNEL_INPUT_LIMIT); 255 256 } else { 257 mIns.push_back(PVD); 258 } 259 } else { 260 Context->ReportError(PVD->getLocation(), 261 "Invalid parameter '%0' for compute kernel %1(). " 262 "Kernels targeting SDK levels %2-%3 may not use " 263 "multiple input parameters.") << PVD->getName() << 264 FD->getName() << SLANG_MINIMUM_TARGET_API << 265 SLANG_MAXIMUM_TARGET_API; 266 valid = false; 267 } 268 clang::QualType QT = PVD->getType().getCanonicalType(); 269 if (QT->isPointerType()) { 270 Context->ReportError(PVD->getLocation(), 271 "Compute kernel %0() cannot have " 272 "parameter '%1' of pointer type: '%2'") 273 << FD->getName() << PVD->getName() << PVD->getType().getAsString(); 274 valid = false; 275 } 276 } 277 278 // Check that we have at least one allocation to use for dimensions. 279 if (valid && mIns.empty() && !mHasReturnType && Context->getTargetAPI() < SLANG_23_TARGET_API) { 280 Context->ReportError(FD->getLocation(), 281 "Compute kernel %0() targeting SDK levels " 282 "%1-%2 must have at least one " 283 "input parameter or a non-void return " 284 "type") 285 << FD->getName() << SLANG_MINIMUM_TARGET_API 286 << (SLANG_23_TARGET_API - 1); 287 valid = false; 288 } 289 290 return valid; 291} 292 293// Search for the optional special parameters. Returns true if valid. Also 294// sets *IndexOfFirstSpecialParameter to the index of the first special parameter, or 295// FD->getNumParams() if none are found. 296bool RSExportForEach::validateSpecialParameters( 297 RSContext *Context, const clang::FunctionDecl *FD, 298 size_t *IndexOfFirstSpecialParameter) { 299 slangAssert(IndexOfFirstSpecialParameter != nullptr); 300 slangAssert(mSpecialParameterSignatureMetadata == 0); 301 clang::ASTContext &C = Context->getASTContext(); 302 303 // Find all special parameters if present. 304 int LastSpecialParameterIdx = -1; // index into specialParameterTable 305 int FirstIntSpecialParameterIdx = -1; // index into specialParameterTable 306 clang::QualType FirstIntSpecialParameterType; 307 size_t NumParams = FD->getNumParams(); 308 *IndexOfFirstSpecialParameter = NumParams; 309 bool valid = true; 310 for (size_t i = 0; i < NumParams; i++) { 311 const clang::ParmVarDecl *PVD = FD->getParamDecl(i); 312 llvm::StringRef ParamName = PVD->getName(); 313 int SpecialParameterIdx = lookupSpecialParameter(ParamName); 314 if (SpecialParameterIdx >= 0) { 315 const SpecialParameter &SP = specialParameterTable[SpecialParameterIdx]; 316 // We won't be invoked if two parameters of the same name are present. 317 slangAssert(!(mSpecialParameterSignatureMetadata & SP.bitval)); 318 319 if (Context->getTargetAPI() < SP.minAPI) { 320 Context->ReportError(PVD->getLocation(), 321 "Compute kernel %0() targeting SDK levels " 322 "%1-%2 may not use parameter '%3'.") 323 << FD->getName() 324 << SLANG_MINIMUM_TARGET_API 325 << (SP.minAPI - 1) 326 << SP.name; 327 valid = false; 328 } 329 330 mSpecialParameterSignatureMetadata |= SP.bitval; 331 if (SpecialParameterIdx < LastSpecialParameterIdx) { 332 Context->ReportError(PVD->getLocation(), 333 "In compute kernel %0(), parameter '%1' must " 334 "be defined before parameter '%2'.") 335 << FD->getName() 336 << SP.name 337 << specialParameterTable[LastSpecialParameterIdx].name; 338 valid = false; 339 } 340 LastSpecialParameterIdx = SpecialParameterIdx; 341 342 // Ensure that all SPK_INT special parameters have the same type. 343 if (SP.kind == SPK_INT) { 344 clang::QualType SpecialParameterType = PVD->getType(); 345 if (FirstIntSpecialParameterIdx >= 0) { 346 if (SpecialParameterType != FirstIntSpecialParameterType) { 347 Context->ReportError(PVD->getLocation(), 348 "Parameters '%0' and '%1' must be of the same type. " 349 "'%0' is of type '%2' while '%1' is of type '%3'.") 350 << specialParameterTable[FirstIntSpecialParameterIdx].name 351 << SP.name 352 << FirstIntSpecialParameterType.getAsString() 353 << SpecialParameterType.getAsString(); 354 valid = false; 355 } 356 } else { 357 FirstIntSpecialParameterIdx = SpecialParameterIdx; 358 FirstIntSpecialParameterType = SpecialParameterType; 359 } 360 } 361 } else { 362 // It's not a special parameter. 363 if (*IndexOfFirstSpecialParameter < NumParams) { 364 Context->ReportError(PVD->getLocation(), 365 "In compute kernel %0(), parameter '%1' cannot " 366 "appear after any of the (%2) parameters.") 367 << FD->getName() << ParamName << listSpecialParameters(Context->getTargetAPI()); 368 valid = false; 369 } 370 continue; 371 } 372 // Validate the data type of the special parameter. 373 switch (specialParameterTable[SpecialParameterIdx].kind) { 374 case SPK_INT: { 375 clang::QualType QT = PVD->getType().getCanonicalType(); 376 clang::QualType UT = QT.getUnqualifiedType(); 377 if (UT != C.UnsignedIntTy && UT != C.IntTy) { 378 Context->ReportError(PVD->getLocation(), 379 "Parameter '%0' must be of type 'int' or " 380 "'unsigned int'. It is of type '%1'.") 381 << ParamName << PVD->getType().getAsString(); 382 valid = false; 383 } 384 break; 385 } 386 case SPK_CTXT: { 387 static const char ExpectedTypeNameMatch[] = "const struct rs_kernel_context_t *"; 388 static const char ExpectedTypeNamePrint[] = "rs_kernel_context"; 389 clang::QualType QT = PVD->getType().getCanonicalType(); 390 clang::QualType UT = QT.getUnqualifiedType(); 391 if (UT.getAsString() != ExpectedTypeNameMatch) { 392 Context->ReportError(PVD->getLocation(), 393 "Parameter '%0' must be of type '%1'. " 394 "It is of type '%2'.") 395 << ParamName << ExpectedTypeNamePrint << PVD->getType().getAsString(); 396 valid = false; 397 } 398 break; 399 } 400 default: 401 slangAssert(!"Unexpected special parameter type"); 402 } 403 // If this is the first time we find a special parameter, save it. 404 if (*IndexOfFirstSpecialParameter >= NumParams) { 405 *IndexOfFirstSpecialParameter = i; 406 } 407 } 408 return valid; 409} 410 411bool RSExportForEach::setSignatureMetadata(RSContext *Context, 412 const clang::FunctionDecl *FD) { 413 mSignatureMetadata = 0; 414 bool valid = true; 415 416 if (mIsKernelStyle) { 417 slangAssert(mOut == nullptr); 418 slangAssert(mUsrData == nullptr); 419 } else { 420 slangAssert(!mHasReturnType); 421 } 422 423 // Set up the bitwise metadata encoding for runtime argument passing. 424 const bool HasOut = mOut || mHasReturnType; 425 mSignatureMetadata |= (hasIns() ? bcinfo::MD_SIG_In : 0); 426 mSignatureMetadata |= (HasOut ? bcinfo::MD_SIG_Out : 0); 427 mSignatureMetadata |= (mUsrData ? bcinfo::MD_SIG_Usr : 0); 428 mSignatureMetadata |= (mIsKernelStyle ? bcinfo::MD_SIG_Kernel : 0); // pass-by-value 429 mSignatureMetadata |= mSpecialParameterSignatureMetadata; 430 431 if (Context->getTargetAPI() < SLANG_ICS_TARGET_API) { 432 // APIs before ICS cannot skip between parameters. It is ok, however, for 433 // them to omit further parameters (i.e. skipping X is ok if you skip Y). 434 if (mSignatureMetadata != (bcinfo::MD_SIG_In | bcinfo::MD_SIG_Out | bcinfo::MD_SIG_Usr | 435 bcinfo::MD_SIG_X | bcinfo::MD_SIG_Y) && 436 mSignatureMetadata != (bcinfo::MD_SIG_In | bcinfo::MD_SIG_Out | bcinfo::MD_SIG_Usr | 437 bcinfo::MD_SIG_X) && 438 mSignatureMetadata != (bcinfo::MD_SIG_In | bcinfo::MD_SIG_Out | bcinfo::MD_SIG_Usr) && 439 mSignatureMetadata != (bcinfo::MD_SIG_In | bcinfo::MD_SIG_Out) && 440 mSignatureMetadata != (bcinfo::MD_SIG_In)) { 441 Context->ReportError(FD->getLocation(), 442 "Compute kernel %0() targeting SDK levels " 443 "%1-%2 may not skip parameters") 444 << FD->getName() << SLANG_MINIMUM_TARGET_API 445 << (SLANG_ICS_TARGET_API - 1); 446 valid = false; 447 } 448 } 449 return valid; 450} 451 452RSExportForEach *RSExportForEach::Create(RSContext *Context, 453 const clang::FunctionDecl *FD) { 454 slangAssert(Context && FD); 455 llvm::StringRef Name = FD->getName(); 456 RSExportForEach *FE; 457 458 slangAssert(!Name.empty() && "Function must have a name"); 459 460 FE = new RSExportForEach(Context, Name); 461 462 if (!FE->validateAndConstructParams(Context, FD)) { 463 return nullptr; 464 } 465 466 clang::ASTContext &Ctx = Context->getASTContext(); 467 468 std::string Id = CreateDummyName("helper_foreach_param", FE->getName()); 469 470 // Extract the usrData parameter (if we have one) 471 if (FE->mUsrData) { 472 const clang::ParmVarDecl *PVD = FE->mUsrData; 473 clang::QualType QT = PVD->getType().getCanonicalType(); 474 slangAssert(QT->isPointerType() && 475 QT->getPointeeType().isConstQualified()); 476 477 const clang::ASTContext &C = Context->getASTContext(); 478 if (QT->getPointeeType().getCanonicalType().getUnqualifiedType() == 479 C.VoidTy) { 480 // In the case of using const void*, we can't reflect an appopriate 481 // Java type, so we fall back to just reflecting the ain/aout parameters 482 FE->mUsrData = nullptr; 483 } else { 484 clang::RecordDecl *RD = 485 clang::RecordDecl::Create(Ctx, clang::TTK_Struct, 486 Ctx.getTranslationUnitDecl(), 487 clang::SourceLocation(), 488 clang::SourceLocation(), 489 &Ctx.Idents.get(Id)); 490 491 clang::FieldDecl *FD = 492 clang::FieldDecl::Create(Ctx, 493 RD, 494 clang::SourceLocation(), 495 clang::SourceLocation(), 496 PVD->getIdentifier(), 497 QT->getPointeeType(), 498 nullptr, 499 /* BitWidth = */ nullptr, 500 /* Mutable = */ false, 501 /* HasInit = */ clang::ICIS_NoInit); 502 RD->addDecl(FD); 503 RD->completeDefinition(); 504 505 // Create an export type iff we have a valid usrData type 506 clang::QualType T = Ctx.getTagDeclType(RD); 507 slangAssert(!T.isNull()); 508 509 RSExportType *ET = RSExportType::Create(Context, T.getTypePtr()); 510 511 slangAssert(ET && "Failed to export a kernel"); 512 513 slangAssert((ET->getClass() == RSExportType::ExportClassRecord) && 514 "Parameter packet must be a record"); 515 516 FE->mParamPacketType = static_cast<RSExportRecordType *>(ET); 517 } 518 } 519 520 if (FE->hasIns()) { 521 522 for (InIter BI = FE->mIns.begin(), EI = FE->mIns.end(); BI != EI; BI++) { 523 const clang::Type *T = (*BI)->getType().getCanonicalType().getTypePtr(); 524 RSExportType *InExportType = RSExportType::Create(Context, T); 525 526 if (FE->mIsKernelStyle) { 527 slangAssert(InExportType != nullptr); 528 } 529 530 FE->mInTypes.push_back(InExportType); 531 } 532 } 533 534 if (FE->mIsKernelStyle && FE->mHasReturnType) { 535 const clang::Type *T = FE->mResultType.getTypePtr(); 536 FE->mOutType = RSExportType::Create(Context, T); 537 slangAssert(FE->mOutType); 538 } else if (FE->mOut) { 539 const clang::Type *T = FE->mOut->getType().getCanonicalType().getTypePtr(); 540 FE->mOutType = RSExportType::Create(Context, T); 541 } 542 543 return FE; 544} 545 546RSExportForEach *RSExportForEach::CreateDummyRoot(RSContext *Context) { 547 slangAssert(Context); 548 llvm::StringRef Name = "root"; 549 RSExportForEach *FE = new RSExportForEach(Context, Name); 550 FE->mDummyRoot = true; 551 return FE; 552} 553 554bool RSExportForEach::isGraphicsRootRSFunc(unsigned int targetAPI, 555 const clang::FunctionDecl *FD) { 556 if (FD->hasAttr<clang::KernelAttr>()) { 557 return false; 558 } 559 560 if (!isRootRSFunc(FD)) { 561 return false; 562 } 563 564 if (FD->getNumParams() == 0) { 565 // Graphics root function 566 return true; 567 } 568 569 // Check for legacy graphics root function (with single parameter). 570 if ((targetAPI < SLANG_ICS_TARGET_API) && (FD->getNumParams() == 1)) { 571 const clang::QualType &IntType = FD->getASTContext().IntTy; 572 if (FD->getReturnType().getCanonicalType() == IntType) { 573 return true; 574 } 575 } 576 577 return false; 578} 579 580bool RSExportForEach::isRSForEachFunc(unsigned int targetAPI, 581 slang::RSContext* Context, 582 const clang::FunctionDecl *FD) { 583 slangAssert(Context && FD); 584 bool hasKernelAttr = FD->hasAttr<clang::KernelAttr>(); 585 586 if (FD->getStorageClass() == clang::SC_Static) { 587 if (hasKernelAttr) { 588 Context->ReportError(FD->getLocation(), 589 "Invalid use of attribute kernel with " 590 "static function declaration: %0") 591 << FD->getName(); 592 } 593 return false; 594 } 595 596 // Anything tagged as a kernel is definitely used with ForEach. 597 if (hasKernelAttr) { 598 return true; 599 } 600 601 if (isGraphicsRootRSFunc(targetAPI, FD)) { 602 return false; 603 } 604 605 // Check if first parameter is a pointer (which is required for ForEach). 606 unsigned int numParams = FD->getNumParams(); 607 608 if (numParams > 0) { 609 const clang::ParmVarDecl *PVD = FD->getParamDecl(0); 610 clang::QualType QT = PVD->getType().getCanonicalType(); 611 612 if (QT->isPointerType()) { 613 return true; 614 } 615 616 // Any non-graphics root() is automatically a ForEach candidate. 617 // At this point, however, we know that it is not going to be a valid 618 // compute root() function (due to not having a pointer parameter). We 619 // still want to return true here, so that we can issue appropriate 620 // diagnostics. 621 if (isRootRSFunc(FD)) { 622 return true; 623 } 624 } 625 626 return false; 627} 628 629bool 630RSExportForEach::validateSpecialFuncDecl(unsigned int targetAPI, 631 slang::RSContext *Context, 632 clang::FunctionDecl const *FD) { 633 slangAssert(Context && FD); 634 bool valid = true; 635 const clang::ASTContext &C = FD->getASTContext(); 636 const clang::QualType &IntType = FD->getASTContext().IntTy; 637 638 if (isGraphicsRootRSFunc(targetAPI, FD)) { 639 if ((targetAPI < SLANG_ICS_TARGET_API) && (FD->getNumParams() == 1)) { 640 // Legacy graphics root function 641 const clang::ParmVarDecl *PVD = FD->getParamDecl(0); 642 clang::QualType QT = PVD->getType().getCanonicalType(); 643 if (QT != IntType) { 644 Context->ReportError(PVD->getLocation(), 645 "invalid parameter type for legacy " 646 "graphics root() function: %0") 647 << PVD->getType(); 648 valid = false; 649 } 650 } 651 652 // Graphics root function, so verify that it returns an int 653 if (FD->getReturnType().getCanonicalType() != IntType) { 654 Context->ReportError(FD->getLocation(), 655 "root() is required to return " 656 "an int for graphics usage"); 657 valid = false; 658 } 659 } else if (isInitRSFunc(FD) || isDtorRSFunc(FD)) { 660 if (FD->getNumParams() != 0) { 661 Context->ReportError(FD->getLocation(), 662 "%0(void) is required to have no " 663 "parameters") 664 << FD->getName(); 665 valid = false; 666 } 667 668 if (FD->getReturnType().getCanonicalType() != C.VoidTy) { 669 Context->ReportError(FD->getLocation(), 670 "%0(void) is required to have a void " 671 "return type") 672 << FD->getName(); 673 valid = false; 674 } 675 } else { 676 slangAssert(false && "must be called on root, init or .rs.dtor function!"); 677 } 678 679 return valid; 680} 681 682} // namespace slang 683