Home | History | Annotate | Download | only in slang
      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