1 /* 2 * Copyright 2015, 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_reduce.h" 18 19 #include <algorithm> 20 #include <sstream> 21 #include <string> 22 23 #include "clang/AST/ASTContext.h" 24 25 #include "slang_assert.h" 26 #include "slang_rs_context.h" 27 #include "slang_rs_export_type.h" 28 #include "slang_rs_object_ref_count.h" 29 #include "slang_rs_special_kernel_param.h" 30 #include "slang_version.h" 31 32 #include "bcinfo/MetadataExtractor.h" 33 34 namespace slang { 35 36 const char RSExportReduce::KeyReduce[] = "reduce"; 37 const char RSExportReduce::KeyInitializer[] = "initializer"; 38 const char RSExportReduce::KeyAccumulator[] = "accumulator"; 39 const char RSExportReduce::KeyCombiner[] = "combiner"; 40 const char RSExportReduce::KeyOutConverter[] = "outconverter"; 41 const char RSExportReduce::KeyHalter[] = "halter"; 42 43 bool RSExportReduce::matchName(const llvm::StringRef &Candidate) const { 44 return 45 Candidate.equals(mNameInitializer) || 46 Candidate.equals(mNameAccumulator) || 47 Candidate.equals(mNameCombiner) || 48 Candidate.equals(mNameOutConverter) || 49 Candidate.equals(mNameHalter); 50 } 51 52 RSExportReduce *RSExportReduce::Create(RSContext *Context, 53 const clang::SourceLocation Location, 54 const llvm::StringRef &NameReduce, 55 const llvm::StringRef &NameInitializer, 56 const llvm::StringRef &NameAccumulator, 57 const llvm::StringRef &NameCombiner, 58 const llvm::StringRef &NameOutConverter, 59 const llvm::StringRef &NameHalter) { 60 slangAssert(Context); 61 RSExportReduce *RNE = new RSExportReduce(Context, 62 Location, 63 NameReduce, 64 NameInitializer, 65 NameAccumulator, 66 NameCombiner, 67 NameOutConverter, 68 NameHalter); 69 70 return RNE; 71 } 72 73 const char *RSExportReduce::getKey(FnIdent Kind) { 74 switch (Kind) { 75 default: 76 slangAssert(!"Unknown FnIdent"); 77 // and fall through 78 case FN_IDENT_INITIALIZER: 79 return KeyInitializer; 80 case FN_IDENT_ACCUMULATOR: 81 return KeyAccumulator; 82 case FN_IDENT_COMBINER: 83 return KeyCombiner; 84 case FN_IDENT_OUT_CONVERTER: 85 return KeyOutConverter; 86 case FN_IDENT_HALTER: 87 return KeyHalter; 88 } 89 } 90 91 // This data is needed during analyzeTranslationUnit() but not afterwards. 92 // Breaking it out into a struct makes it easy for analyzeTranslationUnit() 93 // to call a number of helper functions that all need access to this data. 94 struct RSExportReduce::StateOfAnalyzeTranslationUnit { 95 96 typedef std::function<std::string (const char *Key, const std::string &Name)> DiagnosticDescriptionType; 97 98 StateOfAnalyzeTranslationUnit( 99 RSContext &anRSContext, 100 clang::Preprocessor &aPP, 101 clang::ASTContext &anASTContext, 102 const DiagnosticDescriptionType &aDiagnosticDescription) : 103 104 RSC(anRSContext), 105 PP(aPP), 106 ASTC(anASTContext), 107 DiagnosticDescription(aDiagnosticDescription), 108 109 Ok(true), 110 111 FnInitializer(nullptr), 112 FnAccumulator(nullptr), 113 FnCombiner(nullptr), 114 FnOutConverter(nullptr), 115 FnHalter(nullptr), 116 117 FnInitializerParam(nullptr), 118 FnInitializerParamTy(), 119 120 FnAccumulatorOk(true), 121 FnAccumulatorParamFirst(nullptr), 122 FnAccumulatorParamFirstTy(), 123 FnAccumulatorIndexOfFirstSpecialParameter(0), 124 125 FnOutConverterOk(true), 126 FnOutConverterParamFirst(nullptr), 127 FnOutConverterParamFirstTy() 128 { } 129 130 /*-- Convenience ------------------------------------------*/ 131 132 RSContext &RSC; 133 clang::Preprocessor &PP; 134 clang::ASTContext &ASTC; 135 const DiagnosticDescriptionType DiagnosticDescription; 136 137 /*-- Actual state -----------------------------------------*/ 138 139 bool Ok; 140 141 clang::FunctionDecl *FnInitializer; 142 clang::FunctionDecl *FnAccumulator; 143 clang::FunctionDecl *FnCombiner; 144 clang::FunctionDecl *FnOutConverter; 145 clang::FunctionDecl *FnHalter; 146 147 clang::ParmVarDecl *FnInitializerParam; 148 clang::QualType FnInitializerParamTy; 149 150 bool FnAccumulatorOk; 151 clang::ParmVarDecl *FnAccumulatorParamFirst; 152 clang::QualType FnAccumulatorParamFirstTy; 153 size_t FnAccumulatorIndexOfFirstSpecialParameter; 154 155 bool FnOutConverterOk; // also true if no outconverter 156 clang::ParmVarDecl *FnOutConverterParamFirst; 157 clang::QualType FnOutConverterParamFirstTy; 158 }; 159 160 // does update S.Ok 161 clang::FunctionDecl *RSExportReduce::lookupFunction(StateOfAnalyzeTranslationUnit &S, 162 const char *Kind, const llvm::StringRef &Name) { 163 if (Name.empty()) 164 return nullptr; 165 166 clang::TranslationUnitDecl *TUDecl = getRSContext()->getASTContext().getTranslationUnitDecl(); 167 slangAssert(TUDecl); 168 169 clang::FunctionDecl *Ret = nullptr; 170 const clang::IdentifierInfo *II = S.PP.getIdentifierInfo(Name); 171 if (II) { 172 for (auto Decl : TUDecl->lookup(II)) { 173 clang::FunctionDecl *FDecl = Decl->getAsFunction(); 174 if (!FDecl || !FDecl->isThisDeclarationADefinition()) 175 continue; 176 if (Ret) { 177 S.RSC.ReportError(mLocation, 178 "duplicate function definition for '%0(%1)' for '#pragma rs %2(%3)' (%4, %5)") 179 << Kind << Name << KeyReduce << mNameReduce 180 << Ret->getLocation().printToString(S.PP.getSourceManager()) 181 << FDecl->getLocation().printToString(S.PP.getSourceManager()); 182 S.Ok = false; 183 return nullptr; 184 } 185 Ret = FDecl; 186 } 187 } 188 if (!Ret) { 189 // Either the identifier lookup failed, or we never found the function definition. 190 S.RSC.ReportError(mLocation, 191 "could not find function definition for '%0(%1)' for '#pragma rs %2(%3)'") 192 << Kind << Name << KeyReduce << mNameReduce; 193 S.Ok = false; 194 return nullptr; 195 } 196 if (Ret) { 197 // Must have internal linkage 198 if (Ret->getFormalLinkage() != clang::InternalLinkage) { 199 S.RSC.ReportError(Ret->getLocation(), "%0 must be static") 200 << S.DiagnosticDescription(Kind, Name); 201 S.Ok = false; 202 } 203 } 204 if (Ret == nullptr) 205 S.Ok = false; 206 return Ret; 207 } 208 209 // updates S.Ok; and, depending on Kind, possibly S.FnAccumulatorOk or S.FnOutConverterOk 210 void RSExportReduce::notOk(StateOfAnalyzeTranslationUnit &S, FnIdent Kind) { 211 S.Ok = false; 212 if (Kind == FN_IDENT_ACCUMULATOR) { 213 S.FnAccumulatorOk = false; 214 } else if (Kind == FN_IDENT_OUT_CONVERTER) { 215 S.FnOutConverterOk = false; 216 } 217 } 218 219 // updates S.Ok; and, depending on Kind, possibly S.FnAccumulatorOk or S.FnOutConverterOk 220 void RSExportReduce::checkVoidReturn(StateOfAnalyzeTranslationUnit &S, 221 FnIdent Kind, clang::FunctionDecl *Fn) { 222 slangAssert(Fn); 223 const clang::QualType ReturnTy = Fn->getReturnType().getCanonicalType(); 224 if (!ReturnTy->isVoidType()) { 225 S.RSC.ReportError(Fn->getLocation(), 226 "%0 must return void not '%1'") 227 << S.DiagnosticDescription(getKey(Kind), Fn->getName()) << ReturnTy.getAsString(); 228 notOk(S, Kind); 229 } 230 } 231 232 // updates S.Ok; and, depending on Kind, possibly S.FnAccumulatorOk or S.FnOutConverterOk 233 void RSExportReduce::checkPointeeConstQualified(StateOfAnalyzeTranslationUnit &S, 234 FnIdent Kind, const llvm::StringRef &Name, 235 const clang::ParmVarDecl *Param, bool ExpectedQualification) { 236 const clang::QualType ParamQType = Param->getType(); 237 slangAssert(ParamQType->isPointerType()); 238 const clang::QualType PointeeQType = ParamQType->getPointeeType(); 239 if (PointeeQType.isConstQualified() != ExpectedQualification) { 240 S.RSC.ReportError(Param->getLocation(), 241 "%0 parameter '%1' (type '%2') must%3 point to const-qualified type") 242 << S.DiagnosticDescription(getKey(Kind), Name) 243 << Param->getName() << ParamQType.getAsString() 244 << (ExpectedQualification ? "" : " not"); 245 notOk(S, Kind); 246 } 247 } 248 249 // Process "void mNameInitializer(compType *accum)" 250 void RSExportReduce::analyzeInitializer(StateOfAnalyzeTranslationUnit &S) { 251 if (!S.FnInitializer) // initializer is always optional 252 return; 253 254 // Must return void 255 checkVoidReturn(S, FN_IDENT_INITIALIZER, S.FnInitializer); 256 257 // Must have exactly one parameter 258 if (S.FnInitializer->getNumParams() != 1) { 259 S.RSC.ReportError(S.FnInitializer->getLocation(), 260 "%0 must take exactly 1 parameter (found %1)") 261 << S.DiagnosticDescription(KeyInitializer, mNameInitializer) 262 << S.FnInitializer->getNumParams(); 263 S.Ok = false; 264 return; 265 } 266 267 // Parameter must not be a special parameter 268 S.FnInitializerParam = S.FnInitializer->getParamDecl(0); 269 if (isSpecialKernelParameter(S.FnInitializerParam->getName())) { 270 S.RSC.ReportError(S.FnInitializer->getLocation(), 271 "%0 cannot take special parameter '%1'") 272 << S.DiagnosticDescription(KeyInitializer, mNameInitializer) 273 << S.FnInitializerParam->getName(); 274 S.Ok = false; 275 return; 276 } 277 278 // Parameter must be of pointer type 279 S.FnInitializerParamTy = S.FnInitializerParam->getType().getCanonicalType(); 280 if (!S.FnInitializerParamTy->isPointerType()) { 281 S.RSC.ReportError(S.FnInitializer->getLocation(), 282 "%0 parameter '%1' must be of pointer type not '%2'") 283 << S.DiagnosticDescription(KeyInitializer, mNameInitializer) 284 << S.FnInitializerParam->getName() << S.FnInitializerParamTy.getAsString(); 285 S.Ok = false; 286 return; 287 } 288 289 // Parameter must not point to const-qualified 290 checkPointeeConstQualified(S, FN_IDENT_INITIALIZER, mNameInitializer, S.FnInitializerParam, false); 291 } 292 293 // Process "void mNameAccumulator(compType *accum, in1Type in1, , inNType inN[, specialarguments])" 294 void RSExportReduce::analyzeAccumulator(StateOfAnalyzeTranslationUnit &S) { 295 slangAssert(S.FnAccumulator); 296 297 // Must return void 298 checkVoidReturn(S, FN_IDENT_ACCUMULATOR, S.FnAccumulator); 299 300 // Must have initial parameter of same type as initializer parameter 301 // (if there is an initializer), followed by at least 1 input 302 303 if (S.FnAccumulator->getNumParams() < 2) { 304 S.RSC.ReportError(S.FnAccumulator->getLocation(), 305 "%0 must take at least 2 parameters") 306 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator); 307 S.Ok = S.FnAccumulatorOk = false; 308 return; 309 } 310 311 S.FnAccumulatorParamFirst = S.FnAccumulator->getParamDecl(0); 312 S.FnAccumulatorParamFirstTy = S.FnAccumulatorParamFirst->getType().getCanonicalType(); 313 314 // First parameter must be of pointer type 315 if (!S.FnAccumulatorParamFirstTy->isPointerType()) { 316 S.RSC.ReportError(S.FnAccumulator->getLocation(), 317 "%0 parameter '%1' must be of pointer type not '%2'") 318 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator) 319 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString(); 320 S.Ok = S.FnAccumulatorOk = false; 321 return; 322 } 323 324 // If there is an initializer with a pointer-typed parameter (as 325 // opposed to an initializer with a bad parameter list), then 326 // accumulator first parameter must be of same type as initializer 327 // parameter 328 if (S.FnInitializer && 329 !S.FnInitializerParamTy.isNull() && 330 S.FnInitializerParamTy->isPointerType() && 331 !S.FnAccumulator->getASTContext().hasSameUnqualifiedType( 332 S.FnInitializerParamTy->getPointeeType().getCanonicalType(), 333 S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType())) { 334 // <accumulator> parameter '<baz>' (type '<tbaz>') and initializer <goo>() parameter '<gaz>' (type '<tgaz>') 335 // must be pointers to the same type 336 S.RSC.ReportError(S.FnAccumulator->getLocation(), 337 "%0 parameter '%1' (type '%2') and %3 %4() parameter '%5' (type '%6')" 338 " must be pointers to the same type") 339 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator) 340 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString() 341 << KeyInitializer << mNameInitializer 342 << S.FnInitializerParam->getName() << S.FnInitializerParamTy.getAsString(); 343 S.Ok = S.FnAccumulatorOk = false; 344 } 345 346 if (S.FnAccumulatorOk && S.FnAccumulatorParamFirstTy->getPointeeType()->isFunctionType()) { 347 S.RSC.ReportError(S.FnAccumulator->getLocation(), 348 "%0 parameter '%1' (type '%2') must not be pointer to function type") 349 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator) 350 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString(); 351 S.Ok = S.FnAccumulatorOk = false; 352 } 353 354 if (S.FnAccumulatorOk && S.FnAccumulatorParamFirstTy->getPointeeType()->isIncompleteType()) { 355 S.RSC.ReportError(S.FnAccumulator->getLocation(), 356 "%0 parameter '%1' (type '%2') must not be pointer to incomplete type") 357 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator) 358 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString(); 359 S.Ok = S.FnAccumulatorOk = false; 360 } 361 362 if (S.FnAccumulatorOk && 363 HasRSObjectType(S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType().getTypePtr())) { 364 S.RSC.ReportError(S.FnAccumulator->getLocation(), 365 "%0 parameter '%1' (type '%2') must not be pointer to data containing an object type") 366 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator) 367 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString(); 368 S.Ok = S.FnAccumulatorOk = false; 369 } 370 371 // Parameter must not point to const-qualified 372 checkPointeeConstQualified(S, FN_IDENT_ACCUMULATOR, mNameAccumulator, S.FnAccumulatorParamFirst, false); 373 374 // Analyze special parameters 375 S.Ok &= (S.FnAccumulatorOk &= processSpecialKernelParameters( 376 &S.RSC, 377 std::bind(S.DiagnosticDescription, KeyAccumulator, mNameAccumulator), 378 S.FnAccumulator, 379 &S.FnAccumulatorIndexOfFirstSpecialParameter, 380 &mAccumulatorSignatureMetadata)); 381 382 // Must have at least an accumulator and an input. 383 // If we get here we know there are at least 2 arguments; so the only problem case is 384 // where we have an accumulator followed immediately by a special parameter. 385 if (S.FnAccumulatorIndexOfFirstSpecialParameter < 2) { 386 slangAssert(S.FnAccumulatorIndexOfFirstSpecialParameter < S.FnAccumulator->getNumParams()); 387 S.RSC.ReportError(S.FnAccumulator->getLocation(), 388 "%0 must have at least 1 input ('%1' is a special parameter)") 389 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator) 390 << S.FnAccumulator->getParamDecl(S.FnAccumulatorIndexOfFirstSpecialParameter)->getName(); 391 S.Ok = S.FnAccumulatorOk = false; 392 return; 393 } 394 395 if (S.FnAccumulatorOk) { 396 mAccumulatorSignatureMetadata |= bcinfo::MD_SIG_In; 397 mAccumulatorTypeSize = S.ASTC.getTypeSizeInChars(S.FnAccumulatorParamFirstTy->getPointeeType()).getQuantity(); 398 for (size_t ParamIdx = 1; ParamIdx < S.FnAccumulatorIndexOfFirstSpecialParameter; ++ParamIdx) { 399 const clang::ParmVarDecl *const Param = S.FnAccumulator->getParamDecl(ParamIdx); 400 mAccumulatorIns.push_back(Param); 401 const clang::QualType ParamQType = Param->getType().getCanonicalType(); 402 const clang::Type *ParamType = ParamQType.getTypePtr(); 403 404 RSExportType *ParamEType = nullptr; 405 if (ParamQType->isPointerType()) { 406 S.RSC.ReportError(Param->getLocation(), 407 "%0 parameter '%1' (type '%2') must not be a pointer") 408 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator) 409 << Param->getName() << ParamQType.getAsString(); 410 S.Ok = false; 411 } else if (HasRSObjectType(ParamType)) { 412 S.RSC.ReportError(Param->getLocation(), 413 "%0 parameter '%1' (type '%2') must not contain an object type") 414 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator) 415 << Param->getName() << ParamQType.getAsString(); 416 S.Ok = false; 417 } else if (RSExportType::ValidateType(&S.RSC, S.ASTC, ParamQType, Param, Param->getLocStart(), 418 S.RSC.getTargetAPI(), 419 false /* IsFilterscript */, 420 true /* IsExtern */)) { 421 // TODO: Better diagnostics on validation or creation failure? 422 ParamEType = RSExportType::Create(&S.RSC, ParamType, NotLegacyKernelArgument); 423 S.Ok &= (ParamEType != nullptr); 424 } else { 425 S.Ok = false; 426 } 427 mAccumulatorInTypes.push_back(ParamEType); // possibly nullptr 428 } 429 } 430 } 431 432 // Process "void combinename(compType *accum, const compType *val)" 433 void RSExportReduce::analyzeCombiner(StateOfAnalyzeTranslationUnit &S) { 434 if (S.FnCombiner) { 435 // Must return void 436 checkVoidReturn(S, FN_IDENT_COMBINER, S.FnCombiner); 437 438 // Must have exactly two parameters, of same type as first accumulator parameter 439 440 if (S.FnCombiner->getNumParams() != 2) { 441 S.RSC.ReportError(S.FnCombiner->getLocation(), 442 "%0 must take exactly 2 parameters (found %1)") 443 << S.DiagnosticDescription(KeyCombiner, mNameCombiner) 444 << S.FnCombiner->getNumParams(); 445 S.Ok = false; 446 return; 447 } 448 449 if (S.FnAccumulatorParamFirstTy.isNull() || !S.FnAccumulatorParamFirstTy->isPointerType()) { 450 // We're already in an error situation. We could compare 451 // against the initializer parameter type instead of the first 452 // accumulator parameter type (we'd have to check for the 453 // availability of a parameter type there, too), but it does not 454 // seem worth the effort. 455 // 456 // Likewise, we could compare the two combiner parameter types 457 // against each other. 458 slangAssert(!S.Ok); 459 return; 460 } 461 462 for (int ParamIdx = 0; ParamIdx < 2; ++ParamIdx) { 463 const clang::ParmVarDecl *const FnCombinerParam = S.FnCombiner->getParamDecl(ParamIdx); 464 const clang::QualType FnCombinerParamTy = FnCombinerParam->getType().getCanonicalType(); 465 if (!FnCombinerParamTy->isPointerType() || 466 !S.FnCombiner->getASTContext().hasSameUnqualifiedType( 467 S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType(), 468 FnCombinerParamTy->getPointeeType().getCanonicalType())) { 469 // <combiner> parameter '<baz>' (type '<tbaz>') 470 // and accumulator <goo>() parameter '<gaz>' (type '<tgaz>') must be pointers to the same type 471 S.RSC.ReportError(S.FnCombiner->getLocation(), 472 "%0 parameter '%1' (type '%2') and %3 %4() parameter '%5' (type '%6')" 473 " must be pointers to the same type") 474 << S.DiagnosticDescription(KeyCombiner, mNameCombiner) 475 << FnCombinerParam->getName() << FnCombinerParamTy.getAsString() 476 << KeyAccumulator << mNameAccumulator 477 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString(); 478 S.Ok = false; 479 } else { 480 // Check const-qualification 481 checkPointeeConstQualified(S, FN_IDENT_COMBINER, mNameCombiner, FnCombinerParam, ParamIdx==1); 482 } 483 } 484 485 return; 486 } 487 488 // Ensure accumulator properties permit omission of combiner. 489 490 if (!S.FnAccumulatorOk) { 491 // Couldn't fully analyze accumulator, so cannot see whether it permits omission of combiner. 492 return; 493 } 494 495 if (mAccumulatorIns.size() != 1 || 496 S.FnAccumulatorIndexOfFirstSpecialParameter != S.FnAccumulator->getNumParams()) 497 { 498 S.RSC.ReportError(S.FnAccumulator->getLocation(), 499 "%0 must have exactly 1 input" 500 " and no special parameters in order for the %1 to be omitted") 501 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator) 502 << KeyCombiner; 503 S.Ok = false; 504 return; 505 } 506 507 const clang::ParmVarDecl *const FnAccumulatorParamInput = S.FnAccumulator->getParamDecl(1); 508 const clang::QualType FnAccumulatorParamInputTy = FnAccumulatorParamInput->getType().getCanonicalType(); 509 if (!S.FnAccumulator->getASTContext().hasSameUnqualifiedType( 510 S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType(), 511 FnAccumulatorParamInputTy.getCanonicalType())) { 512 S.RSC.ReportError(S.FnAccumulator->getLocation(), 513 "%0 parameter '%1' (type '%2')" 514 " must be pointer to the type of parameter '%3' (type '%4')" 515 " in order for the %5 to be omitted") 516 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator) 517 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString() 518 << FnAccumulatorParamInput->getName() << FnAccumulatorParamInputTy.getAsString() 519 << KeyCombiner; 520 S.Ok = false; 521 } 522 } 523 524 // Process "void outconvertname(resultType *result, const compType *accum)" 525 void RSExportReduce::analyzeOutConverter(StateOfAnalyzeTranslationUnit &S) { 526 if (!S.FnOutConverter) // outconverter is always optional 527 return; 528 529 // Must return void 530 checkVoidReturn(S, FN_IDENT_OUT_CONVERTER, S.FnOutConverter); 531 532 // Must have exactly two parameters 533 if (S.FnOutConverter->getNumParams() != 2) { 534 S.RSC.ReportError(S.FnOutConverter->getLocation(), 535 "%0 must take exactly 2 parameters (found %1)") 536 << S.DiagnosticDescription(KeyOutConverter, mNameOutConverter) 537 << S.FnOutConverter->getNumParams(); 538 S.Ok = S.FnOutConverterOk = false; 539 return; 540 } 541 542 // Parameters must not be special and must be of pointer type; 543 // and second parameter must match first accumulator parameter 544 for (int ParamIdx = 0; ParamIdx < 2; ++ParamIdx) { 545 clang::ParmVarDecl *const FnOutConverterParam = S.FnOutConverter->getParamDecl(ParamIdx); 546 547 if (isSpecialKernelParameter(FnOutConverterParam->getName())) { 548 S.RSC.ReportError(S.FnOutConverter->getLocation(), 549 "%0 cannot take special parameter '%1'") 550 << S.DiagnosticDescription(KeyOutConverter, mNameOutConverter) 551 << FnOutConverterParam->getName(); 552 S.Ok = S.FnOutConverterOk = false; 553 continue; 554 } 555 556 const clang::QualType FnOutConverterParamTy = FnOutConverterParam->getType().getCanonicalType(); 557 558 if (!FnOutConverterParamTy->isPointerType()) { 559 S.RSC.ReportError(S.FnOutConverter->getLocation(), 560 "%0 parameter '%1' must be of pointer type not '%2'") 561 << S.DiagnosticDescription(KeyOutConverter, mNameOutConverter) 562 << FnOutConverterParam->getName() << FnOutConverterParamTy.getAsString(); 563 S.Ok = S.FnOutConverterOk = false; 564 continue; 565 } 566 567 // Check const-qualification 568 checkPointeeConstQualified(S, FN_IDENT_OUT_CONVERTER, mNameOutConverter, FnOutConverterParam, ParamIdx==1); 569 570 if (ParamIdx == 0) { 571 S.FnOutConverterParamFirst = FnOutConverterParam; 572 S.FnOutConverterParamFirstTy = FnOutConverterParamTy; 573 continue; 574 } 575 576 if (S.FnAccumulatorParamFirstTy.isNull() || !S.FnAccumulatorParamFirstTy->isPointerType()) { 577 // We're already in an error situation. We could compare 578 // against the initializer parameter type instead of the first 579 // accumulator parameter type (we'd have to check for the 580 // availability of a parameter type there, too), but it does not 581 // seem worth the effort. 582 slangAssert(!S.Ok); 583 continue; 584 } 585 586 if (!S.FnOutConverter->getASTContext().hasSameUnqualifiedType( 587 S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType(), 588 FnOutConverterParamTy->getPointeeType().getCanonicalType())) { 589 // <outconverter> parameter '<baz>' (type '<tbaz>') 590 // and accumulator <goo>() parameter '<gaz>' (type '<tgaz>') must be pointers to the same type 591 S.RSC.ReportError(S.FnOutConverter->getLocation(), 592 "%0 parameter '%1' (type '%2') and %3 %4() parameter '%5' (type '%6')" 593 " must be pointers to the same type") 594 << S.DiagnosticDescription(KeyOutConverter, mNameOutConverter) 595 << FnOutConverterParam->getName() << FnOutConverterParamTy.getAsString() 596 << KeyAccumulator << mNameAccumulator 597 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString(); 598 S.Ok = S.FnOutConverterOk = false; 599 } 600 } 601 } 602 603 // Process "bool haltername(const compType *accum)" 604 void RSExportReduce::analyzeHalter(StateOfAnalyzeTranslationUnit &S) { 605 if (!S.FnHalter) // halter is always optional 606 return; 607 608 // Must return bool 609 const clang::QualType ReturnTy = S.FnHalter->getReturnType().getCanonicalType(); 610 if (!ReturnTy->isBooleanType()) { 611 S.RSC.ReportError(S.FnHalter->getLocation(), 612 "%0 must return bool not '%1'") 613 << S.DiagnosticDescription(KeyHalter, mNameHalter) << ReturnTy.getAsString(); 614 S.Ok = false; 615 } 616 617 // Must have exactly one parameter 618 if (S.FnHalter->getNumParams() != 1) { 619 S.RSC.ReportError(S.FnHalter->getLocation(), 620 "%0 must take exactly 1 parameter (found %1)") 621 << S.DiagnosticDescription(KeyHalter, mNameHalter) 622 << S.FnHalter->getNumParams(); 623 S.Ok = false; 624 return; 625 } 626 627 // Parameter must not be a special parameter 628 const clang::ParmVarDecl *const FnHalterParam = S.FnHalter->getParamDecl(0); 629 if (isSpecialKernelParameter(FnHalterParam->getName())) { 630 S.RSC.ReportError(S.FnHalter->getLocation(), 631 "%0 cannot take special parameter '%1'") 632 << S.DiagnosticDescription(KeyHalter, mNameHalter) 633 << FnHalterParam->getName(); 634 S.Ok = false; 635 return; 636 } 637 638 // Parameter must be same type as first accumulator parameter 639 640 if (S.FnAccumulatorParamFirstTy.isNull() || !S.FnAccumulatorParamFirstTy->isPointerType()) { 641 // We're already in an error situation. We could compare against 642 // the initializer parameter type or the first combiner parameter 643 // type instead of the first accumulator parameter type (we'd have 644 // to check for the availability of a parameter type there, too), 645 // but it does not seem worth the effort. 646 slangAssert(!S.Ok); 647 return; 648 } 649 650 const clang::QualType FnHalterParamTy = FnHalterParam->getType().getCanonicalType(); 651 if (!FnHalterParamTy->isPointerType() || 652 !S.FnHalter->getASTContext().hasSameUnqualifiedType( 653 S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType(), 654 FnHalterParamTy->getPointeeType().getCanonicalType())) { 655 // <halter> parameter '<baz>' (type '<tbaz>') 656 // and accumulator <goo>() parameter '<gaz>' (type '<tgaz>') must be pointers to the same type 657 S.RSC.ReportError(S.FnHalter->getLocation(), 658 "%0 parameter '%1' (type '%2') and %3 %4() parameter '%5' (type '%6')" 659 " must be pointers to the same type") 660 << S.DiagnosticDescription(KeyHalter, mNameHalter) 661 << FnHalterParam->getName() << FnHalterParamTy.getAsString() 662 << KeyAccumulator << mNameAccumulator 663 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString(); 664 S.Ok = false; 665 return; 666 } 667 668 // Parameter must point to const-qualified 669 checkPointeeConstQualified(S, FN_IDENT_HALTER, mNameHalter, FnHalterParam, true); 670 } 671 672 void RSExportReduce::analyzeResultType(StateOfAnalyzeTranslationUnit &S) { 673 if (!(S.FnAccumulatorOk && S.FnOutConverterOk)) { 674 // No idea what the result type is 675 slangAssert(!S.Ok); 676 return; 677 } 678 679 struct ResultInfoType { 680 const clang::QualType QType; 681 clang::VarDecl *const Decl; 682 const char *FnKey; 683 const std::string &FnName; 684 std::function<std::string ()> UnlessOutConverter; 685 } ResultInfo = 686 S.FnOutConverter 687 ? ResultInfoType({ S.FnOutConverterParamFirstTy, S.FnOutConverterParamFirst, 688 KeyOutConverter, mNameOutConverter, 689 []() { return std::string(""); }}) 690 : ResultInfoType({ S.FnAccumulatorParamFirstTy, S.FnAccumulatorParamFirst, 691 KeyAccumulator, mNameAccumulator, 692 []() { return std::string(" unless ") + KeyOutConverter + " is provided"; }}); 693 const clang::QualType PointeeQType = ResultInfo.QType->getPointeeType(); 694 695 if (PointeeQType->isPointerType()) { 696 S.RSC.ReportError(ResultInfo.Decl->getLocation(), 697 "%0 parameter '%1' (type '%2') must not point to a pointer%3") 698 << S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName) 699 << ResultInfo.Decl->getName() << ResultInfo.QType.getAsString() 700 << ResultInfo.UnlessOutConverter(); 701 } else if (PointeeQType->isIncompleteType()) { 702 S.RSC.ReportError(ResultInfo.Decl->getLocation(), 703 "%0 parameter '%1' (type '%2') must not point to an incomplete type%3") 704 << S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName) 705 << ResultInfo.Decl->getName() << ResultInfo.QType.getAsString() 706 << ResultInfo.UnlessOutConverter(); 707 } else if (HasRSObjectType(PointeeQType.getTypePtr())) { 708 S.RSC.ReportError(ResultInfo.Decl->getLocation(), 709 "%0 parameter '%1' (type '%2') must not point to data containing an object type%3") 710 << S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName) 711 << ResultInfo.Decl->getName() << ResultInfo.QType.getAsString() 712 << ResultInfo.UnlessOutConverter(); 713 } else if (RSExportType::ValidateType(&S.RSC, S.ASTC, PointeeQType, 714 ResultInfo.Decl, ResultInfo.Decl->getLocStart(), 715 S.RSC.getTargetAPI(), 716 false /* IsFilterscript */, 717 true /* IsExtern */)) { 718 // TODO: Better diagnostics on validation or creation failure? 719 if ((mResultType = RSExportType::Create(&S.RSC, PointeeQType.getTypePtr(), 720 NotLegacyKernelArgument, ResultInfo.Decl)) != nullptr) { 721 const RSExportType *CheckType = mResultType; 722 const char *ArrayErrorPhrase = ""; 723 if (mResultType->getClass() == RSExportType::ExportClassConstantArray) { 724 CheckType = static_cast<const RSExportConstantArrayType *>(mResultType)->getElementType(); 725 ArrayErrorPhrase = "n array of"; 726 } 727 switch (CheckType->getClass()) { 728 case RSExportType::ExportClassMatrix: 729 // Not supported for now -- what does a matrix result type mean? 730 S.RSC.ReportError(ResultInfo.Decl->getLocation(), 731 "%0 parameter '%1' (type '%2') must not point to a%3 matrix type%4") 732 << S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName) 733 << ResultInfo.Decl->getName() << ResultInfo.QType.getAsString() 734 << ArrayErrorPhrase 735 << ResultInfo.UnlessOutConverter(); 736 mResultType = nullptr; 737 break; 738 default: 739 // All's well 740 break; 741 } 742 } 743 } 744 745 if (mResultType) 746 S.RSC.insertExportReduceResultType(mResultType); 747 else 748 S.Ok = false; 749 } 750 751 bool RSExportReduce::analyzeTranslationUnit() { 752 753 RSContext &RSC = *getRSContext(); 754 clang::Preprocessor &PP = RSC.getPreprocessor(); 755 756 StateOfAnalyzeTranslationUnit S( 757 RSC, PP, RSC.getASTContext(), 758 [&PP, this] (const char *Key, const std::string &Name) { 759 std::ostringstream Description; 760 Description 761 << Key << " " << Name << "()" 762 << " for '#pragma rs " << KeyReduce << "(" << mNameReduce << ")'" 763 << " (" << mLocation.printToString(PP.getSourceManager()) << ")"; 764 return Description.str(); 765 }); 766 767 S.FnInitializer = lookupFunction(S, KeyInitializer, mNameInitializer); 768 S.FnAccumulator = lookupFunction(S, KeyAccumulator, mNameAccumulator); 769 S.FnCombiner = lookupFunction(S, KeyCombiner, mNameCombiner); 770 S.FnOutConverter = lookupFunction(S, KeyOutConverter, mNameOutConverter); 771 S.FnHalter = lookupFunction(S, KeyHalter, mNameHalter); 772 773 if (!S.Ok) 774 return false; 775 776 analyzeInitializer(S); 777 analyzeAccumulator(S); 778 analyzeCombiner(S); 779 analyzeOutConverter(S); 780 analyzeHalter(S); 781 analyzeResultType(S); 782 783 return S.Ok; 784 } 785 786 } // namespace slang 787