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 void RSExportReduce::analyzeHalter(StateOfAnalyzeTranslationUnit &S) { 604 if (!S.FnHalter) // halter is always optional 605 return; 606 607 // Must return bool 608 const clang::QualType ReturnTy = S.FnHalter->getReturnType().getCanonicalType(); 609 if (!ReturnTy->isBooleanType()) { 610 S.RSC.ReportError(S.FnHalter->getLocation(), 611 "%0 must return bool not '%1'") 612 << S.DiagnosticDescription(KeyHalter, mNameHalter) << ReturnTy.getAsString(); 613 S.Ok = false; 614 } 615 616 // Must have exactly one parameter 617 if (S.FnHalter->getNumParams() != 1) { 618 S.RSC.ReportError(S.FnHalter->getLocation(), 619 "%0 must take exactly 1 parameter (found %1)") 620 << S.DiagnosticDescription(KeyHalter, mNameHalter) 621 << S.FnHalter->getNumParams(); 622 S.Ok = false; 623 return; 624 } 625 626 // Parameter must not be a special parameter 627 const clang::ParmVarDecl *const FnHalterParam = S.FnHalter->getParamDecl(0); 628 if (isSpecialKernelParameter(FnHalterParam->getName())) { 629 S.RSC.ReportError(S.FnHalter->getLocation(), 630 "%0 cannot take special parameter '%1'") 631 << S.DiagnosticDescription(KeyHalter, mNameHalter) 632 << FnHalterParam->getName(); 633 S.Ok = false; 634 return; 635 } 636 637 // Parameter must be same type as first accumulator parameter 638 639 if (S.FnAccumulatorParamFirstTy.isNull() || !S.FnAccumulatorParamFirstTy->isPointerType()) { 640 // We're already in an error situation. We could compare against 641 // the initializer parameter type or the first combiner parameter 642 // type instead of the first accumulator parameter type (we'd have 643 // to check for the availability of a parameter type there, too), 644 // but it does not seem worth the effort. 645 slangAssert(!S.Ok); 646 return; 647 } 648 649 const clang::QualType FnHalterParamTy = FnHalterParam->getType().getCanonicalType(); 650 if (!FnHalterParamTy->isPointerType() || 651 !S.FnHalter->getASTContext().hasSameUnqualifiedType( 652 S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType(), 653 FnHalterParamTy->getPointeeType().getCanonicalType())) { 654 // <halter> parameter '<baz>' (type '<tbaz>') 655 // and accumulator <goo>() parameter '<gaz>' (type '<tgaz>') must be pointers to the same type 656 S.RSC.ReportError(S.FnHalter->getLocation(), 657 "%0 parameter '%1' (type '%2') and %3 %4() parameter '%5' (type '%6')" 658 " must be pointers to the same type") 659 << S.DiagnosticDescription(KeyHalter, mNameHalter) 660 << FnHalterParam->getName() << FnHalterParamTy.getAsString() 661 << KeyAccumulator << mNameAccumulator 662 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString(); 663 S.Ok = false; 664 return; 665 } 666 667 // Parameter must point to const-qualified 668 checkPointeeConstQualified(S, FN_IDENT_HALTER, mNameHalter, FnHalterParam, true); 669 } 670 671 void RSExportReduce::analyzeResultType(StateOfAnalyzeTranslationUnit &S) { 672 if (!(S.FnAccumulatorOk && S.FnOutConverterOk)) { 673 // No idea what the result type is 674 slangAssert(!S.Ok); 675 return; 676 } 677 678 struct ResultInfoType { 679 const clang::QualType QType; 680 clang::VarDecl *const Decl; 681 const char *FnKey; 682 const std::string &FnName; 683 std::function<std::string ()> UnlessOutConverter; 684 } ResultInfo = 685 S.FnOutConverter 686 ? ResultInfoType({ S.FnOutConverterParamFirstTy, S.FnOutConverterParamFirst, 687 KeyOutConverter, mNameOutConverter, 688 []() { return std::string(""); }}) 689 : ResultInfoType({ S.FnAccumulatorParamFirstTy, S.FnAccumulatorParamFirst, 690 KeyAccumulator, mNameAccumulator, 691 []() { return std::string(" unless ") + KeyOutConverter + " is provided"; }}); 692 const clang::QualType PointeeQType = ResultInfo.QType->getPointeeType(); 693 694 if (PointeeQType->isPointerType()) { 695 S.RSC.ReportError(ResultInfo.Decl->getLocation(), 696 "%0 parameter '%1' (type '%2') must not point to a pointer%3") 697 << S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName) 698 << ResultInfo.Decl->getName() << ResultInfo.QType.getAsString() 699 << ResultInfo.UnlessOutConverter(); 700 } else if (PointeeQType->isIncompleteType()) { 701 S.RSC.ReportError(ResultInfo.Decl->getLocation(), 702 "%0 parameter '%1' (type '%2') must not point to an incomplete type%3") 703 << S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName) 704 << ResultInfo.Decl->getName() << ResultInfo.QType.getAsString() 705 << ResultInfo.UnlessOutConverter(); 706 } else if (HasRSObjectType(PointeeQType.getTypePtr())) { 707 S.RSC.ReportError(ResultInfo.Decl->getLocation(), 708 "%0 parameter '%1' (type '%2') must not point to data containing an object type%3") 709 << S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName) 710 << ResultInfo.Decl->getName() << ResultInfo.QType.getAsString() 711 << ResultInfo.UnlessOutConverter(); 712 } else if (RSExportType::ValidateType(&S.RSC, S.ASTC, PointeeQType, 713 ResultInfo.Decl, ResultInfo.Decl->getLocStart(), 714 S.RSC.getTargetAPI(), 715 false /* IsFilterscript */, 716 true /* IsExtern */)) { 717 // TODO: Better diagnostics on validation or creation failure? 718 if ((mResultType = RSExportType::Create(&S.RSC, PointeeQType.getTypePtr(), 719 NotLegacyKernelArgument, ResultInfo.Decl)) != nullptr) { 720 const RSExportType *CheckType = mResultType; 721 const char *ArrayErrorPhrase = ""; 722 if (mResultType->getClass() == RSExportType::ExportClassConstantArray) { 723 CheckType = static_cast<const RSExportConstantArrayType *>(mResultType)->getElementType(); 724 ArrayErrorPhrase = "n array of"; 725 } 726 switch (CheckType->getClass()) { 727 case RSExportType::ExportClassMatrix: 728 // Not supported for now -- what does a matrix result type mean? 729 S.RSC.ReportError(ResultInfo.Decl->getLocation(), 730 "%0 parameter '%1' (type '%2') must not point to a%3 matrix type%4") 731 << S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName) 732 << ResultInfo.Decl->getName() << ResultInfo.QType.getAsString() 733 << ArrayErrorPhrase 734 << ResultInfo.UnlessOutConverter(); 735 mResultType = nullptr; 736 break; 737 default: 738 // All's well 739 break; 740 } 741 } 742 } 743 744 if (mResultType) 745 S.RSC.insertExportReduceResultType(mResultType); 746 else 747 S.Ok = false; 748 } 749 750 bool RSExportReduce::analyzeTranslationUnit() { 751 752 RSContext &RSC = *getRSContext(); 753 clang::Preprocessor &PP = RSC.getPreprocessor(); 754 755 StateOfAnalyzeTranslationUnit S( 756 RSC, PP, RSC.getASTContext(), 757 [&RSC, &PP, this] (const char *Key, const std::string &Name) { 758 std::ostringstream Description; 759 Description 760 << Key << " " << Name << "()" 761 << " for '#pragma rs " << KeyReduce << "(" << mNameReduce << ")'" 762 << " (" << mLocation.printToString(PP.getSourceManager()) << ")"; 763 return Description.str(); 764 }); 765 766 S.FnInitializer = lookupFunction(S, KeyInitializer, mNameInitializer); 767 S.FnAccumulator = lookupFunction(S, KeyAccumulator, mNameAccumulator); 768 S.FnCombiner = lookupFunction(S, KeyCombiner, mNameCombiner); 769 S.FnOutConverter = lookupFunction(S, KeyOutConverter, mNameOutConverter); 770 S.FnHalter = lookupFunction(S, KeyHalter, mNameHalter); 771 772 if (!S.Ok) 773 return false; 774 775 analyzeInitializer(S); 776 analyzeAccumulator(S); 777 analyzeCombiner(S); 778 analyzeOutConverter(S); 779 analyzeHalter(S); 780 analyzeResultType(S); 781 782 return S.Ok; 783 } 784 785 } // namespace slang 786