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 // 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