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 "slang_assert.h" 29 #include "slang_rs_context.h" 30 #include "slang_rs_export_type.h" 31 #include "slang_version.h" 32 33 namespace slang { 34 35 namespace { 36 37 static void ReportNameError(clang::DiagnosticsEngine *DiagEngine, 38 clang::ParmVarDecl const *PVD) { 39 slangAssert(DiagEngine && PVD); 40 const clang::SourceManager &SM = DiagEngine->getSourceManager(); 41 42 DiagEngine->Report( 43 clang::FullSourceLoc(PVD->getLocation(), SM), 44 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 45 "Duplicate parameter entry " 46 "(by position/name): '%0'")) 47 << PVD->getName(); 48 return; 49 } 50 51 } // namespace 52 53 54 // This function takes care of additional validation and construction of 55 // parameters related to forEach_* reflection. 56 bool RSExportForEach::validateAndConstructParams( 57 RSContext *Context, const clang::FunctionDecl *FD) { 58 slangAssert(Context && FD); 59 bool valid = true; 60 clang::ASTContext &C = Context->getASTContext(); 61 clang::DiagnosticsEngine *DiagEngine = Context->getDiagnostics(); 62 63 numParams = FD->getNumParams(); 64 65 if (Context->getTargetAPI() < SLANG_JB_TARGET_API) { 66 if (!isRootRSFunc(FD)) { 67 DiagEngine->Report( 68 clang::FullSourceLoc(FD->getLocation(), DiagEngine->getSourceManager()), 69 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 70 "Non-root compute kernel %0() is " 71 "not supported in SDK levels %1-%2")) 72 << FD->getName() 73 << SLANG_MINIMUM_TARGET_API 74 << (SLANG_JB_TARGET_API - 1); 75 return false; 76 } 77 } 78 79 mResultType = FD->getResultType().getCanonicalType(); 80 // Compute kernel functions are required to return a void type or 81 // be marked explicitly as a kernel. In the case of 82 // "__attribute__((kernel))", we handle validation differently. 83 if (FD->hasAttr<clang::KernelAttr>()) { 84 return validateAndConstructKernelParams(Context, FD); 85 } 86 87 // If numParams is 0, we already marked this as a graphics root(). 88 slangAssert(numParams > 0); 89 90 // Compute kernel functions of this type are required to return a void type. 91 if (mResultType != C.VoidTy) { 92 DiagEngine->Report( 93 clang::FullSourceLoc(FD->getLocation(), DiagEngine->getSourceManager()), 94 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 95 "Compute kernel %0() is required to return a " 96 "void type")) << FD->getName(); 97 valid = false; 98 } 99 100 // Validate remaining parameter types 101 // TODO(all): Add support for LOD/face when we have them 102 103 size_t i = 0; 104 const clang::ParmVarDecl *PVD = FD->getParamDecl(i); 105 clang::QualType QT = PVD->getType().getCanonicalType(); 106 107 // Check for const T1 *in 108 if (QT->isPointerType() && QT->getPointeeType().isConstQualified()) { 109 mIn = PVD; 110 i++; // advance parameter pointer 111 } 112 113 // Check for T2 *out 114 if (i < numParams) { 115 PVD = FD->getParamDecl(i); 116 QT = PVD->getType().getCanonicalType(); 117 if (QT->isPointerType() && !QT->getPointeeType().isConstQualified()) { 118 mOut = PVD; 119 i++; // advance parameter pointer 120 } 121 } 122 123 if (!mIn && !mOut) { 124 DiagEngine->Report( 125 clang::FullSourceLoc(FD->getLocation(), 126 DiagEngine->getSourceManager()), 127 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 128 "Compute kernel %0() must have at least one " 129 "parameter for in or out")) << FD->getName(); 130 valid = false; 131 } 132 133 // Check for T3 *usrData 134 if (i < numParams) { 135 PVD = FD->getParamDecl(i); 136 QT = PVD->getType().getCanonicalType(); 137 if (QT->isPointerType() && QT->getPointeeType().isConstQualified()) { 138 mUsrData = PVD; 139 i++; // advance parameter pointer 140 } 141 } 142 143 while (i < numParams) { 144 PVD = FD->getParamDecl(i); 145 QT = PVD->getType().getCanonicalType(); 146 147 if (QT.getUnqualifiedType() != C.UnsignedIntTy) { 148 DiagEngine->Report( 149 clang::FullSourceLoc(PVD->getLocation(), 150 DiagEngine->getSourceManager()), 151 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 152 "Unexpected kernel %0() parameter '%1' " 153 "of type '%2'")) 154 << FD->getName() << PVD->getName() << PVD->getType().getAsString(); 155 valid = false; 156 } else { 157 llvm::StringRef ParamName = PVD->getName(); 158 if (ParamName.equals("x")) { 159 if (mX) { 160 ReportNameError(DiagEngine, PVD); 161 valid = false; 162 } else if (mY) { 163 // Can't go back to X after skipping Y 164 ReportNameError(DiagEngine, PVD); 165 valid = false; 166 } else { 167 mX = PVD; 168 } 169 } else if (ParamName.equals("y")) { 170 if (mY) { 171 ReportNameError(DiagEngine, PVD); 172 valid = false; 173 } else { 174 mY = PVD; 175 } 176 } else { 177 if (!mX && !mY) { 178 mX = PVD; 179 } else if (!mY) { 180 mY = PVD; 181 } else { 182 DiagEngine->Report( 183 clang::FullSourceLoc(PVD->getLocation(), 184 DiagEngine->getSourceManager()), 185 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 186 "Unexpected kernel %0() parameter '%1' " 187 "of type '%2'")) 188 << FD->getName() << PVD->getName() << PVD->getType().getAsString(); 189 valid = false; 190 } 191 } 192 } 193 194 i++; 195 } 196 197 mSignatureMetadata = 0; 198 if (valid) { 199 // Set up the bitwise metadata encoding for runtime argument passing. 200 mSignatureMetadata |= (mIn ? 0x01 : 0); 201 mSignatureMetadata |= (mOut ? 0x02 : 0); 202 mSignatureMetadata |= (mUsrData ? 0x04 : 0); 203 mSignatureMetadata |= (mX ? 0x08 : 0); 204 mSignatureMetadata |= (mY ? 0x10 : 0); 205 } 206 207 if (Context->getTargetAPI() < SLANG_ICS_TARGET_API) { 208 // APIs before ICS cannot skip between parameters. It is ok, however, for 209 // them to omit further parameters (i.e. skipping X is ok if you skip Y). 210 if (mSignatureMetadata != 0x1f && // In, Out, UsrData, X, Y 211 mSignatureMetadata != 0x0f && // In, Out, UsrData, X 212 mSignatureMetadata != 0x07 && // In, Out, UsrData 213 mSignatureMetadata != 0x03 && // In, Out 214 mSignatureMetadata != 0x01) { // In 215 DiagEngine->Report( 216 clang::FullSourceLoc(FD->getLocation(), 217 DiagEngine->getSourceManager()), 218 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 219 "Compute kernel %0() targeting SDK levels " 220 "%1-%2 may not skip parameters")) 221 << FD->getName() << SLANG_MINIMUM_TARGET_API 222 << (SLANG_ICS_TARGET_API - 1); 223 valid = false; 224 } 225 } 226 227 return valid; 228 } 229 230 231 bool RSExportForEach::validateAndConstructKernelParams(RSContext *Context, 232 const clang::FunctionDecl *FD) { 233 slangAssert(Context && FD); 234 bool valid = true; 235 clang::ASTContext &C = Context->getASTContext(); 236 clang::DiagnosticsEngine *DiagEngine = Context->getDiagnostics(); 237 238 if (Context->getTargetAPI() < SLANG_JB_MR1_TARGET_API) { 239 DiagEngine->Report( 240 clang::FullSourceLoc(FD->getLocation(), 241 DiagEngine->getSourceManager()), 242 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 243 "Compute kernel %0() targeting SDK levels " 244 "%1-%2 may not use pass-by-value with " 245 "__attribute__((kernel))")) 246 << FD->getName() << SLANG_MINIMUM_TARGET_API 247 << (SLANG_JB_MR1_TARGET_API - 1); 248 return false; 249 } 250 251 // Denote that we are indeed a pass-by-value kernel. 252 mKernel = true; 253 254 if (mResultType != C.VoidTy) { 255 mReturn = true; 256 } 257 258 if (mResultType->isPointerType()) { 259 DiagEngine->Report( 260 clang::FullSourceLoc(FD->getTypeSpecStartLoc(), 261 DiagEngine->getSourceManager()), 262 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 263 "Compute kernel %0() cannot return a " 264 "pointer type: '%1'")) 265 << FD->getName() << mResultType.getAsString(); 266 valid = false; 267 } 268 269 // Validate remaining parameter types 270 // TODO(all): Add support for LOD/face when we have them 271 272 size_t i = 0; 273 const clang::ParmVarDecl *PVD = NULL; 274 clang::QualType QT; 275 276 if (i < numParams) { 277 PVD = FD->getParamDecl(i); 278 QT = PVD->getType().getCanonicalType(); 279 280 if (QT->isPointerType()) { 281 DiagEngine->Report( 282 clang::FullSourceLoc(PVD->getLocation(), 283 DiagEngine->getSourceManager()), 284 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 285 "Compute kernel %0() cannot have " 286 "parameter '%1' of pointer type: '%2'")) 287 << FD->getName() << PVD->getName() << PVD->getType().getAsString(); 288 valid = false; 289 } else if (QT.getUnqualifiedType() == C.UnsignedIntTy) { 290 // First parameter is either input or x, y (iff it is uint32_t). 291 llvm::StringRef ParamName = PVD->getName(); 292 if (ParamName.equals("x")) { 293 mX = PVD; 294 } else if (ParamName.equals("y")) { 295 mY = PVD; 296 } else { 297 mIn = PVD; 298 } 299 } else { 300 mIn = PVD; 301 } 302 303 i++; // advance parameter pointer 304 } 305 306 // Check that we have at least one allocation to use for dimensions. 307 if (valid && !mIn && !mReturn) { 308 DiagEngine->Report( 309 clang::FullSourceLoc(FD->getLocation(), 310 DiagEngine->getSourceManager()), 311 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 312 "Compute kernel %0() must have at least one " 313 "input parameter or a non-void return " 314 "type")) << FD->getName(); 315 valid = false; 316 } 317 318 // TODO: Abstract this block away, since it is duplicate code. 319 while (i < numParams) { 320 PVD = FD->getParamDecl(i); 321 QT = PVD->getType().getCanonicalType(); 322 323 if (QT.getUnqualifiedType() != C.UnsignedIntTy) { 324 DiagEngine->Report( 325 clang::FullSourceLoc(PVD->getLocation(), 326 DiagEngine->getSourceManager()), 327 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 328 "Unexpected kernel %0() parameter '%1' " 329 "of type '%2'")) 330 << FD->getName() << PVD->getName() << PVD->getType().getAsString(); 331 valid = false; 332 } else { 333 llvm::StringRef ParamName = PVD->getName(); 334 if (ParamName.equals("x")) { 335 if (mX) { 336 ReportNameError(DiagEngine, PVD); 337 valid = false; 338 } else if (mY) { 339 // Can't go back to X after skipping Y 340 ReportNameError(DiagEngine, PVD); 341 valid = false; 342 } else { 343 mX = PVD; 344 } 345 } else if (ParamName.equals("y")) { 346 if (mY) { 347 ReportNameError(DiagEngine, PVD); 348 valid = false; 349 } else { 350 mY = PVD; 351 } 352 } else { 353 if (!mX && !mY) { 354 mX = PVD; 355 } else if (!mY) { 356 mY = PVD; 357 } else { 358 DiagEngine->Report( 359 clang::FullSourceLoc(PVD->getLocation(), 360 DiagEngine->getSourceManager()), 361 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 362 "Unexpected kernel %0() parameter '%1' " 363 "of type '%2'")) 364 << FD->getName() << PVD->getName() << PVD->getType().getAsString(); 365 valid = false; 366 } 367 } 368 } 369 370 i++; // advance parameter pointer 371 } 372 373 mSignatureMetadata = 0; 374 if (valid) { 375 // Set up the bitwise metadata encoding for runtime argument passing. 376 mSignatureMetadata |= (mIn ? 0x01 : 0); 377 slangAssert(mOut == NULL); 378 mSignatureMetadata |= (mReturn ? 0x02 : 0); 379 slangAssert(mUsrData == NULL); 380 mSignatureMetadata |= (mUsrData ? 0x04 : 0); 381 mSignatureMetadata |= (mX ? 0x08 : 0); 382 mSignatureMetadata |= (mY ? 0x10 : 0); 383 mSignatureMetadata |= (mKernel ? 0x20 : 0); // pass-by-value 384 } 385 386 return valid; 387 } 388 389 390 RSExportForEach *RSExportForEach::Create(RSContext *Context, 391 const clang::FunctionDecl *FD) { 392 slangAssert(Context && FD); 393 llvm::StringRef Name = FD->getName(); 394 RSExportForEach *FE; 395 396 slangAssert(!Name.empty() && "Function must have a name"); 397 398 FE = new RSExportForEach(Context, Name); 399 400 if (!FE->validateAndConstructParams(Context, FD)) { 401 return NULL; 402 } 403 404 clang::ASTContext &Ctx = Context->getASTContext(); 405 406 std::string Id(DUMMY_RS_TYPE_NAME_PREFIX"helper_foreach_param:"); 407 Id.append(FE->getName()).append(DUMMY_RS_TYPE_NAME_POSTFIX); 408 409 // Extract the usrData parameter (if we have one) 410 if (FE->mUsrData) { 411 const clang::ParmVarDecl *PVD = FE->mUsrData; 412 clang::QualType QT = PVD->getType().getCanonicalType(); 413 slangAssert(QT->isPointerType() && 414 QT->getPointeeType().isConstQualified()); 415 416 const clang::ASTContext &C = Context->getASTContext(); 417 if (QT->getPointeeType().getCanonicalType().getUnqualifiedType() == 418 C.VoidTy) { 419 // In the case of using const void*, we can't reflect an appopriate 420 // Java type, so we fall back to just reflecting the ain/aout parameters 421 FE->mUsrData = NULL; 422 } else { 423 clang::RecordDecl *RD = 424 clang::RecordDecl::Create(Ctx, clang::TTK_Struct, 425 Ctx.getTranslationUnitDecl(), 426 clang::SourceLocation(), 427 clang::SourceLocation(), 428 &Ctx.Idents.get(Id)); 429 430 clang::FieldDecl *FD = 431 clang::FieldDecl::Create(Ctx, 432 RD, 433 clang::SourceLocation(), 434 clang::SourceLocation(), 435 PVD->getIdentifier(), 436 QT->getPointeeType(), 437 NULL, 438 /* BitWidth = */ NULL, 439 /* Mutable = */ false, 440 /* HasInit = */ clang::ICIS_NoInit); 441 RD->addDecl(FD); 442 RD->completeDefinition(); 443 444 // Create an export type iff we have a valid usrData type 445 clang::QualType T = Ctx.getTagDeclType(RD); 446 slangAssert(!T.isNull()); 447 448 RSExportType *ET = RSExportType::Create(Context, T.getTypePtr()); 449 450 if (ET == NULL) { 451 fprintf(stderr, "Failed to export the function %s. There's at least " 452 "one parameter whose type is not supported by the " 453 "reflection\n", FE->getName().c_str()); 454 return NULL; 455 } 456 457 slangAssert((ET->getClass() == RSExportType::ExportClassRecord) && 458 "Parameter packet must be a record"); 459 460 FE->mParamPacketType = static_cast<RSExportRecordType *>(ET); 461 } 462 } 463 464 if (FE->mIn) { 465 const clang::Type *T = FE->mIn->getType().getCanonicalType().getTypePtr(); 466 FE->mInType = RSExportType::Create(Context, T); 467 if (FE->mKernel) { 468 slangAssert(FE->mInType); 469 } 470 } 471 472 if (FE->mKernel && FE->mReturn) { 473 const clang::Type *T = FE->mResultType.getTypePtr(); 474 FE->mOutType = RSExportType::Create(Context, T); 475 slangAssert(FE->mOutType); 476 } else if (FE->mOut) { 477 const clang::Type *T = FE->mOut->getType().getCanonicalType().getTypePtr(); 478 FE->mOutType = RSExportType::Create(Context, T); 479 } 480 481 return FE; 482 } 483 484 RSExportForEach *RSExportForEach::CreateDummyRoot(RSContext *Context) { 485 slangAssert(Context); 486 llvm::StringRef Name = "root"; 487 RSExportForEach *FE = new RSExportForEach(Context, Name); 488 FE->mDummyRoot = true; 489 return FE; 490 } 491 492 bool RSExportForEach::isGraphicsRootRSFunc(int targetAPI, 493 const clang::FunctionDecl *FD) { 494 if (FD->hasAttr<clang::KernelAttr>()) { 495 return false; 496 } 497 498 if (!isRootRSFunc(FD)) { 499 return false; 500 } 501 502 if (FD->getNumParams() == 0) { 503 // Graphics root function 504 return true; 505 } 506 507 // Check for legacy graphics root function (with single parameter). 508 if ((targetAPI < SLANG_ICS_TARGET_API) && (FD->getNumParams() == 1)) { 509 const clang::QualType &IntType = FD->getASTContext().IntTy; 510 if (FD->getResultType().getCanonicalType() == IntType) { 511 return true; 512 } 513 } 514 515 return false; 516 } 517 518 bool RSExportForEach::isRSForEachFunc(int targetAPI, 519 clang::DiagnosticsEngine *DiagEngine, 520 const clang::FunctionDecl *FD) { 521 slangAssert(DiagEngine && FD); 522 bool hasKernelAttr = FD->hasAttr<clang::KernelAttr>(); 523 524 if (FD->getStorageClass() == clang::SC_Static) { 525 if (hasKernelAttr) { 526 DiagEngine->Report( 527 clang::FullSourceLoc(FD->getLocation(), 528 DiagEngine->getSourceManager()), 529 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 530 "Invalid use of attribute kernel with " 531 "static function declaration: %0")) 532 << FD->getName(); 533 } 534 return false; 535 } 536 537 // Anything tagged as a kernel is definitely used with ForEach. 538 if (hasKernelAttr) { 539 return true; 540 } 541 542 if (isGraphicsRootRSFunc(targetAPI, FD)) { 543 return false; 544 } 545 546 // Check if first parameter is a pointer (which is required for ForEach). 547 unsigned int numParams = FD->getNumParams(); 548 549 if (numParams > 0) { 550 const clang::ParmVarDecl *PVD = FD->getParamDecl(0); 551 clang::QualType QT = PVD->getType().getCanonicalType(); 552 553 if (QT->isPointerType()) { 554 return true; 555 } 556 557 // Any non-graphics root() is automatically a ForEach candidate. 558 // At this point, however, we know that it is not going to be a valid 559 // compute root() function (due to not having a pointer parameter). We 560 // still want to return true here, so that we can issue appropriate 561 // diagnostics. 562 if (isRootRSFunc(FD)) { 563 return true; 564 } 565 } 566 567 return false; 568 } 569 570 bool 571 RSExportForEach::validateSpecialFuncDecl(int targetAPI, 572 clang::DiagnosticsEngine *DiagEngine, 573 clang::FunctionDecl const *FD) { 574 slangAssert(DiagEngine && FD); 575 bool valid = true; 576 const clang::ASTContext &C = FD->getASTContext(); 577 const clang::QualType &IntType = FD->getASTContext().IntTy; 578 579 if (isGraphicsRootRSFunc(targetAPI, FD)) { 580 if ((targetAPI < SLANG_ICS_TARGET_API) && (FD->getNumParams() == 1)) { 581 // Legacy graphics root function 582 const clang::ParmVarDecl *PVD = FD->getParamDecl(0); 583 clang::QualType QT = PVD->getType().getCanonicalType(); 584 if (QT != IntType) { 585 DiagEngine->Report( 586 clang::FullSourceLoc(PVD->getLocation(), 587 DiagEngine->getSourceManager()), 588 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 589 "invalid parameter type for legacy " 590 "graphics root() function: %0")) 591 << PVD->getType(); 592 valid = false; 593 } 594 } 595 596 // Graphics root function, so verify that it returns an int 597 if (FD->getResultType().getCanonicalType() != IntType) { 598 DiagEngine->Report( 599 clang::FullSourceLoc(FD->getLocation(), 600 DiagEngine->getSourceManager()), 601 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 602 "root() is required to return " 603 "an int for graphics usage")); 604 valid = false; 605 } 606 } else if (isInitRSFunc(FD) || isDtorRSFunc(FD)) { 607 if (FD->getNumParams() != 0) { 608 DiagEngine->Report( 609 clang::FullSourceLoc(FD->getLocation(), 610 DiagEngine->getSourceManager()), 611 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 612 "%0(void) is required to have no " 613 "parameters")) << FD->getName(); 614 valid = false; 615 } 616 617 if (FD->getResultType().getCanonicalType() != C.VoidTy) { 618 DiagEngine->Report( 619 clang::FullSourceLoc(FD->getLocation(), 620 DiagEngine->getSourceManager()), 621 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, 622 "%0(void) is required to have a void " 623 "return type")) << FD->getName(); 624 valid = false; 625 } 626 } else { 627 slangAssert(false && "must be called on root, init or .rs.dtor function!"); 628 } 629 630 return valid; 631 } 632 633 } // namespace slang 634