Home | History | Annotate | Download | only in slang
      1 /*
      2  * Copyright 2011-2012, The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "slang_rs_export_foreach.h"
     18 
     19 #include <string>
     20 
     21 #include "clang/AST/ASTContext.h"
     22 #include "clang/AST/Attr.h"
     23 #include "clang/AST/Decl.h"
     24 #include "clang/AST/TypeLoc.h"
     25 
     26 #include "llvm/IR/DerivedTypes.h"
     27 
     28 #include "bcinfo/MetadataExtractor.h"
     29 
     30 #include "slang_assert.h"
     31 #include "slang_rs_context.h"
     32 #include "slang_rs_export_type.h"
     33 #include "slang_rs_special_func.h"
     34 #include "slang_rs_special_kernel_param.h"
     35 #include "slang_version.h"
     36 
     37 namespace {
     38 
     39 const size_t RS_KERNEL_INPUT_LIMIT = 8; // see frameworks/base/libs/rs/cpu_ref/rsCpuCoreRuntime.h
     40 
     41 bool isRootRSFunc(const clang::FunctionDecl *FD) {
     42   if (!FD) {
     43     return false;
     44   }
     45   return FD->getName().equals("root");
     46 }
     47 
     48 } // end anonymous namespace
     49 
     50 namespace slang {
     51 
     52 // This function takes care of additional validation and construction of
     53 // parameters related to forEach_* reflection.
     54 bool RSExportForEach::validateAndConstructParams(
     55     RSContext *Context, const clang::FunctionDecl *FD) {
     56   slangAssert(Context && FD);
     57   bool valid = true;
     58 
     59   numParams = FD->getNumParams();
     60 
     61   if (Context->getTargetAPI() < SLANG_JB_TARGET_API) {
     62     // Before JellyBean, we allowed only one kernel per file.  It must be called "root".
     63     if (!isRootRSFunc(FD)) {
     64       Context->ReportError(FD->getLocation(),
     65                            "Non-root compute kernel %0() is "
     66                            "not supported in SDK levels %1-%2")
     67           << FD->getName() << SLANG_MINIMUM_TARGET_API
     68           << (SLANG_JB_TARGET_API - 1);
     69       return false;
     70     }
     71   }
     72 
     73   mResultType = FD->getReturnType().getCanonicalType();
     74   // Compute kernel functions are defined differently when the
     75   // "__attribute__((kernel))" is set.
     76   if (FD->hasAttr<clang::RenderScriptKernelAttr>()) {
     77     valid &= validateAndConstructKernelParams(Context, FD);
     78   } else {
     79     valid &= validateAndConstructOldStyleParams(Context, FD);
     80   }
     81 
     82   valid &= setSignatureMetadata(Context, FD);
     83   return valid;
     84 }
     85 
     86 bool RSExportForEach::validateAndConstructOldStyleParams(
     87     RSContext *Context, const clang::FunctionDecl *FD) {
     88   slangAssert(Context && FD);
     89   // If numParams is 0, we already marked this as a graphics root().
     90   slangAssert(numParams > 0);
     91 
     92   bool valid = true;
     93 
     94   // Compute kernel functions of this style are required to return a void type.
     95   clang::ASTContext &C = Context->getASTContext();
     96   if (mResultType != C.VoidTy) {
     97     Context->ReportError(FD->getLocation(),
     98                          "Compute kernel %0() is required to return a "
     99                          "void type")
    100         << FD->getName();
    101     valid = false;
    102   }
    103 
    104   // Validate remaining parameter types
    105 
    106   size_t IndexOfFirstSpecialParameter = numParams;
    107   valid &= processSpecialParameters(Context, FD, &IndexOfFirstSpecialParameter);
    108 
    109   // Validate the non-special parameters, which should all be found before the
    110   // first special parameter.
    111   for (size_t i = 0; i < IndexOfFirstSpecialParameter; i++) {
    112     const clang::ParmVarDecl *PVD = FD->getParamDecl(i);
    113     clang::QualType QT = PVD->getType().getCanonicalType();
    114 
    115     if (!QT->isPointerType()) {
    116       Context->ReportError(PVD->getLocation(),
    117                            "Compute kernel %0() cannot have non-pointer "
    118                            "parameters besides special parameters (%1). Parameter '%2' is "
    119                            "of type: '%3'")
    120           << FD->getName() << listSpecialKernelParameters(Context->getTargetAPI())
    121           << PVD->getName() << PVD->getType().getAsString();
    122       valid = false;
    123       continue;
    124     }
    125 
    126     // The only non-const pointer should be out.
    127     if (!QT->getPointeeType().isConstQualified()) {
    128       if (mOut == nullptr) {
    129         mOut = PVD;
    130       } else {
    131         Context->ReportError(PVD->getLocation(),
    132                              "Compute kernel %0() can only have one non-const "
    133                              "pointer parameter. Parameters '%1' and '%2' are "
    134                              "both non-const.")
    135             << FD->getName() << mOut->getName() << PVD->getName();
    136         valid = false;
    137       }
    138     } else {
    139       if (mIns.empty() && mOut == nullptr) {
    140         mIns.push_back(PVD);
    141       } else if (mUsrData == nullptr) {
    142         mUsrData = PVD;
    143       } else {
    144         Context->ReportError(
    145             PVD->getLocation(),
    146             "Unexpected parameter '%0' for compute kernel %1()")
    147             << PVD->getName() << FD->getName();
    148         valid = false;
    149       }
    150     }
    151   }
    152 
    153   if (mIns.empty() && !mOut) {
    154     Context->ReportError(FD->getLocation(),
    155                          "Compute kernel %0() must have at least one "
    156                          "parameter for in or out")
    157         << FD->getName();
    158     valid = false;
    159   }
    160 
    161   return valid;
    162 }
    163 
    164 bool RSExportForEach::validateAndConstructKernelParams(
    165     RSContext *Context, const clang::FunctionDecl *FD) {
    166   slangAssert(Context && FD);
    167   bool valid = true;
    168   clang::ASTContext &C = Context->getASTContext();
    169 
    170   if (Context->getTargetAPI() < SLANG_JB_MR1_TARGET_API) {
    171     Context->ReportError(FD->getLocation(),
    172                          "Compute kernel %0() targeting SDK levels "
    173                          "%1-%2 may not use pass-by-value with "
    174                          "__attribute__((kernel))")
    175         << FD->getName() << SLANG_MINIMUM_TARGET_API
    176         << (SLANG_JB_MR1_TARGET_API - 1);
    177     return false;
    178   }
    179 
    180   // Denote that we are indeed a pass-by-value kernel.
    181   mIsKernelStyle = true;
    182   mHasReturnType = (mResultType != C.VoidTy);
    183 
    184   if (mResultType->isPointerType()) {
    185     Context->ReportError(
    186         FD->getTypeSpecStartLoc(),
    187         "Compute kernel %0() cannot return a pointer type: '%1'")
    188         << FD->getName() << mResultType.getAsString();
    189     valid = false;
    190   }
    191 
    192   // Validate remaining parameter types
    193 
    194   size_t IndexOfFirstSpecialParameter = numParams;
    195   valid &= processSpecialParameters(Context, FD, &IndexOfFirstSpecialParameter);
    196 
    197   // Validate the non-special parameters, which should all be found before the
    198   // first special.
    199   for (size_t i = 0; i < IndexOfFirstSpecialParameter; i++) {
    200     const clang::ParmVarDecl *PVD = FD->getParamDecl(i);
    201 
    202     if (Context->getTargetAPI() >= SLANG_M_TARGET_API || i == 0) {
    203       if (i >= RS_KERNEL_INPUT_LIMIT) {
    204         Context->ReportError(PVD->getLocation(),
    205                              "Invalid parameter '%0' for compute kernel %1(). "
    206                              "Kernels targeting SDK levels %2+ may not use "
    207                              "more than %3 input parameters.") << PVD->getName() <<
    208                              FD->getName() << SLANG_M_TARGET_API <<
    209                              int(RS_KERNEL_INPUT_LIMIT);
    210 
    211       } else {
    212         mIns.push_back(PVD);
    213       }
    214     } else {
    215       Context->ReportError(PVD->getLocation(),
    216                            "Invalid parameter '%0' for compute kernel %1(). "
    217                            "Kernels targeting SDK levels %2-%3 may not use "
    218                            "multiple input parameters.") << PVD->getName() <<
    219                            FD->getName() << SLANG_MINIMUM_TARGET_API <<
    220                            (SLANG_M_TARGET_API - 1);
    221       valid = false;
    222     }
    223     clang::QualType QT = PVD->getType().getCanonicalType();
    224     if (QT->isPointerType()) {
    225       Context->ReportError(PVD->getLocation(),
    226                            "Compute kernel %0() cannot have "
    227                            "parameter '%1' of pointer type: '%2'")
    228           << FD->getName() << PVD->getName() << PVD->getType().getAsString();
    229       valid = false;
    230     }
    231   }
    232 
    233   // Check that we have at least one allocation to use for dimensions.
    234   if (valid && mIns.empty() && !mHasReturnType && Context->getTargetAPI() < SLANG_M_TARGET_API) {
    235     Context->ReportError(FD->getLocation(),
    236                          "Compute kernel %0() targeting SDK levels "
    237                          "%1-%2 must have at least one "
    238                          "input parameter or a non-void return "
    239                          "type")
    240         << FD->getName() << SLANG_MINIMUM_TARGET_API
    241         << (SLANG_M_TARGET_API - 1);
    242     valid = false;
    243   }
    244 
    245   return valid;
    246 }
    247 
    248 // Process the optional special parameters:
    249 // - Sets *IndexOfFirstSpecialParameter to the index of the first special parameter, or
    250 //     FD->getNumParams() if none are found.
    251 // - Add bits to mSpecialParameterSignatureMetadata for the found special parameters.
    252 // Returns true if no errors.
    253 bool RSExportForEach::processSpecialParameters(
    254     RSContext *Context, const clang::FunctionDecl *FD,
    255     size_t *IndexOfFirstSpecialParameter) {
    256   auto DiagnosticCallback = [FD] {
    257     std::ostringstream DiagnosticDescription;
    258     DiagnosticDescription << "compute kernel " << FD->getName().str() << "()";
    259     return DiagnosticDescription.str();
    260   };
    261   return slang::processSpecialKernelParameters(Context,
    262                                                DiagnosticCallback,
    263                                                FD,
    264                                                IndexOfFirstSpecialParameter,
    265                                                &mSpecialParameterSignatureMetadata);
    266 }
    267 
    268 bool RSExportForEach::setSignatureMetadata(RSContext *Context,
    269                                            const clang::FunctionDecl *FD) {
    270   mSignatureMetadata = 0;
    271   bool valid = true;
    272 
    273   if (mIsKernelStyle) {
    274     slangAssert(mOut == nullptr);
    275     slangAssert(mUsrData == nullptr);
    276   } else {
    277     slangAssert(!mHasReturnType);
    278   }
    279 
    280   // Set up the bitwise metadata encoding for runtime argument passing.
    281   const bool HasOut = mOut || mHasReturnType;
    282   mSignatureMetadata |= (hasIns() ?       bcinfo::MD_SIG_In     : 0);
    283   mSignatureMetadata |= (HasOut ?         bcinfo::MD_SIG_Out    : 0);
    284   mSignatureMetadata |= (mUsrData ?       bcinfo::MD_SIG_Usr    : 0);
    285   mSignatureMetadata |= (mIsKernelStyle ? bcinfo::MD_SIG_Kernel : 0);  // pass-by-value
    286   mSignatureMetadata |= mSpecialParameterSignatureMetadata;
    287 
    288   if (Context->getTargetAPI() < SLANG_ICS_TARGET_API) {
    289     // APIs before ICS cannot skip between parameters. It is ok, however, for
    290     // them to omit further parameters (i.e. skipping X is ok if you skip Y).
    291     if (mSignatureMetadata != (bcinfo::MD_SIG_In | bcinfo::MD_SIG_Out | bcinfo::MD_SIG_Usr |
    292                                bcinfo::MD_SIG_X | bcinfo::MD_SIG_Y) &&
    293         mSignatureMetadata != (bcinfo::MD_SIG_In | bcinfo::MD_SIG_Out | bcinfo::MD_SIG_Usr |
    294                                bcinfo::MD_SIG_X) &&
    295         mSignatureMetadata != (bcinfo::MD_SIG_In | bcinfo::MD_SIG_Out | bcinfo::MD_SIG_Usr) &&
    296         mSignatureMetadata != (bcinfo::MD_SIG_In | bcinfo::MD_SIG_Out) &&
    297         mSignatureMetadata != (bcinfo::MD_SIG_In)) {
    298       Context->ReportError(FD->getLocation(),
    299                            "Compute kernel %0() targeting SDK levels "
    300                            "%1-%2 may not skip parameters")
    301           << FD->getName() << SLANG_MINIMUM_TARGET_API
    302           << (SLANG_ICS_TARGET_API - 1);
    303       valid = false;
    304     }
    305   }
    306   return valid;
    307 }
    308 
    309 RSExportForEach *RSExportForEach::Create(RSContext *Context,
    310                                          const clang::FunctionDecl *FD) {
    311   slangAssert(Context && FD);
    312   llvm::StringRef Name = FD->getName();
    313   RSExportForEach *FE;
    314 
    315   slangAssert(!Name.empty() && "Function must have a name");
    316 
    317   FE = new RSExportForEach(Context, Name, FD->getLocation());
    318   FE->mOrdinal = Context->getNextForEachOrdinal();
    319 
    320   if (!FE->validateAndConstructParams(Context, FD)) {
    321     return nullptr;
    322   }
    323 
    324   clang::ASTContext &Ctx = Context->getASTContext();
    325 
    326   std::string Id = CreateDummyName("helper_foreach_param", FE->getName());
    327 
    328   // Construct type information about usrData, inputs, and
    329   // outputs. Return null when there is an error exporting types.
    330 
    331   bool TypeExportError = false;
    332 
    333   // Extract the usrData parameter (if we have one)
    334   if (FE->mUsrData) {
    335     const clang::ParmVarDecl *PVD = FE->mUsrData;
    336     clang::QualType QT = PVD->getType().getCanonicalType();
    337     slangAssert(QT->isPointerType() &&
    338                 QT->getPointeeType().isConstQualified());
    339 
    340     const clang::ASTContext &C = Context->getASTContext();
    341     if (QT->getPointeeType().getCanonicalType().getUnqualifiedType() ==
    342         C.VoidTy) {
    343       // In the case of using const void*, we can't reflect an appopriate
    344       // Java type, so we fall back to just reflecting the ain/aout parameters
    345       FE->mUsrData = nullptr;
    346     } else {
    347       clang::RecordDecl *RD =
    348           clang::RecordDecl::Create(Ctx, clang::TTK_Struct,
    349                                     Ctx.getTranslationUnitDecl(),
    350                                     clang::SourceLocation(),
    351                                     clang::SourceLocation(),
    352                                     &Ctx.Idents.get(Id));
    353 
    354       clang::FieldDecl *FD =
    355           clang::FieldDecl::Create(Ctx,
    356                                    RD,
    357                                    clang::SourceLocation(),
    358                                    clang::SourceLocation(),
    359                                    PVD->getIdentifier(),
    360                                    QT->getPointeeType(),
    361                                    nullptr,
    362                                    /* BitWidth = */ nullptr,
    363                                    /* Mutable = */ false,
    364                                    /* HasInit = */ clang::ICIS_NoInit);
    365       RD->addDecl(FD);
    366       RD->completeDefinition();
    367 
    368       // Create an export type iff we have a valid usrData type
    369       clang::QualType T = Ctx.getTagDeclType(RD);
    370       slangAssert(!T.isNull());
    371 
    372       RSExportType *ET =
    373           RSExportType::Create(Context, T.getTypePtr(), LegacyKernelArgument);
    374 
    375       if (ET) {
    376         slangAssert((ET->getClass() == RSExportType::ExportClassRecord) &&
    377                     "Parameter packet must be a record");
    378 
    379         FE->mParamPacketType = static_cast<RSExportRecordType *>(ET);
    380       } else {
    381         TypeExportError = true;
    382       }
    383     }
    384   }
    385 
    386   if (FE->hasIns()) {
    387     for (InIter BI = FE->mIns.begin(), EI = FE->mIns.end(); BI != EI; BI++) {
    388       const clang::Type *T = (*BI)->getType().getCanonicalType().getTypePtr();
    389       ExportKind EK = (FE->mIsKernelStyle ? NotLegacyKernelArgument :
    390                                             LegacyKernelArgument);
    391       RSExportType *InExportType = RSExportType::Create(Context, T, EK);
    392 
    393       // It is not an error if we don't export an input type for legacy
    394       // kernel arguments. This can happen in the case of a void pointer.
    395       // See ReflectionState::addForEachIn().
    396       if (FE->mIsKernelStyle && !InExportType) {
    397         TypeExportError = true;
    398       }
    399 
    400       FE->mInTypes.push_back(InExportType);
    401     }
    402   }
    403 
    404   if (FE->mIsKernelStyle && FE->mHasReturnType) {
    405     const clang::Type *ReturnType = FE->mResultType.getTypePtr();
    406     FE->mOutType = RSExportType::Create(Context, ReturnType,
    407                                         NotLegacyKernelArgument);
    408     TypeExportError |= !FE->mOutType;
    409   } else if (FE->mOut) {
    410     const clang::Type *OutType =
    411         FE->mOut->getType().getCanonicalType().getTypePtr();
    412     FE->mOutType = RSExportType::Create(Context, OutType, LegacyKernelArgument);
    413     // It is not an error if we don't export an output type.
    414     // This can happen in the case of a void pointer.
    415   }
    416 
    417   if (TypeExportError) {
    418     slangAssert(Context->getDiagnostics()->hasErrorOccurred() &&
    419                 "Error exporting type but no diagnostic message issued!");
    420     return nullptr;
    421   }
    422 
    423   return FE;
    424 }
    425 
    426 RSExportForEach *RSExportForEach::CreateDummyRoot(RSContext *Context) {
    427   slangAssert(Context);
    428   llvm::StringRef Name = "root";
    429   RSExportForEach *FE = new RSExportForEach(Context, Name, clang::SourceLocation());
    430   FE->mDummyRoot = true;
    431   return FE;
    432 }
    433 
    434 bool RSExportForEach::isRSForEachFunc(unsigned int targetAPI,
    435                                       const clang::FunctionDecl *FD) {
    436   if (!FD) {
    437     return false;
    438   }
    439 
    440   // Anything tagged as a kernel("") is definitely used with ForEach.
    441   if (FD->hasAttr<clang::RenderScriptKernelAttr>()) {
    442     return true;
    443   }
    444 
    445   if (RSSpecialFunc::isGraphicsRootRSFunc(targetAPI, FD)) {
    446     return false;
    447   }
    448 
    449   // Check if first parameter is a pointer (which is required for ForEach).
    450   unsigned int numParams = FD->getNumParams();
    451 
    452   if (numParams > 0) {
    453     const clang::ParmVarDecl *PVD = FD->getParamDecl(0);
    454     clang::QualType QT = PVD->getType().getCanonicalType();
    455 
    456     if (QT->isPointerType()) {
    457       return true;
    458     }
    459 
    460     // Any non-graphics root() is automatically a ForEach candidate.
    461     // At this point, however, we know that it is not going to be a valid
    462     // compute root() function (due to not having a pointer parameter). We
    463     // still want to return true here, so that we can issue appropriate
    464     // diagnostics.
    465     if (isRootRSFunc(FD)) {
    466       return true;
    467     }
    468   }
    469 
    470   return false;
    471 }
    472 
    473 unsigned RSExportForEach::getNumInputs(unsigned int targetAPI,
    474                                        const clang::FunctionDecl *FD) {
    475   unsigned numInputs = 0;
    476   for (const clang::ParmVarDecl* param : FD->parameters()) {
    477     if (!isSpecialKernelParameter(param->getName())) {
    478       numInputs++;
    479     }
    480   }
    481 
    482   return numInputs;
    483 }
    484 
    485 }  // namespace slang
    486