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