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_special_kernel_param.h"
     18 
     19 #include "clang/AST/ASTContext.h"
     20 
     21 #include "bcinfo/MetadataExtractor.h"
     22 
     23 #include "slang_assert.h"
     24 #include "slang_rs_context.h"
     25 #include "slang_version.h"
     26 
     27 namespace {
     28 
     29 enum SpecialParameterKind {
     30   SPK_LOCATION, // 'int' or 'unsigned int'
     31   SPK_CONTEXT,  // rs_kernel_context
     32 };
     33 
     34 struct SpecialParameter {
     35   const char *name;
     36   bcinfo::MetadataSignatureBitval bitval;
     37   SpecialParameterKind kind;
     38   SlangTargetAPI minAPI;
     39 };
     40 
     41 // Table entries are in the order parameters must occur in a kernel parameter list.
     42 const SpecialParameter specialParameterTable[] = {
     43   { "context", bcinfo::MD_SIG_Ctxt, SPK_CONTEXT, SLANG_M_TARGET_API },
     44   { "x", bcinfo::MD_SIG_X, SPK_LOCATION, SLANG_MINIMUM_TARGET_API },
     45   { "y", bcinfo::MD_SIG_Y, SPK_LOCATION, SLANG_MINIMUM_TARGET_API },
     46   { "z", bcinfo::MD_SIG_Z, SPK_LOCATION, SLANG_M_TARGET_API },
     47   { nullptr, bcinfo::MD_SIG_None, SPK_LOCATION, SLANG_MINIMUM_TARGET_API }, // marks end of table
     48 };
     49 
     50 // If the specified name matches the name of an entry in
     51 // specialParameterTable, return the corresponding table index.
     52 // Return -1 if not found.
     53 int lookupSpecialKernelParameter(const llvm::StringRef name) {
     54   for (int i = 0; specialParameterTable[i].name != nullptr; ++i) {
     55     if (name.equals(specialParameterTable[i].name)) {
     56       return i;
     57     }
     58   }
     59 
     60   return -1;
     61 }
     62 
     63 } // end anonymous namespace
     64 
     65 namespace slang {
     66 
     67 // Is the specified name the name of an entry in the specialParameterTable?
     68 bool isSpecialKernelParameter(const llvm::StringRef Name) {
     69   return lookupSpecialKernelParameter(Name) >= 0;
     70 }
     71 
     72 // Return a comma-separated list of names in specialParameterTable
     73 // that are available at the specified API level.
     74 std::string listSpecialKernelParameters(unsigned int api) {
     75   std::string ret;
     76   bool first = true;
     77   for (int i = 0; specialParameterTable[i].name != nullptr; ++i) {
     78     if (specialParameterTable[i].minAPI > api)
     79       continue;
     80     if (first)
     81       first = false;
     82     else
     83       ret += ", ";
     84     ret += "'";
     85     ret += specialParameterTable[i].name;
     86     ret += "'";
     87   }
     88   return ret;
     89 }
     90 
     91 // Process the optional special parameters:
     92 // - Sets *IndexOfFirstSpecialParameter to the index of the first special parameter, or
     93 //     FD->getNumParams() if none are found.
     94 // - Add bits to *SignatureMetadata for the found special parameters.
     95 // Returns true if no errors.
     96 bool processSpecialKernelParameters(
     97   slang::RSContext *Context,
     98   std::function<std::string ()> DiagnosticDescription,
     99   const clang::FunctionDecl *FD,
    100   size_t *IndexOfFirstSpecialParameter,
    101   unsigned int *SignatureMetadata) {
    102   slangAssert(IndexOfFirstSpecialParameter != nullptr);
    103   slangAssert(SignatureMetadata != nullptr);
    104   slangAssert(*SignatureMetadata == 0);
    105   clang::ASTContext &C = Context->getASTContext();
    106 
    107   // Find all special parameters if present.
    108   int LastSpecialParameterIdx = -1; // index into specialParameterTable
    109   int FirstLocationSpecialParameterIdx = -1; // index into specialParameterTable
    110   clang::QualType FirstLocationSpecialParameterType;
    111   size_t NumParams = FD->getNumParams();
    112   *IndexOfFirstSpecialParameter = NumParams;
    113   bool valid = true;
    114   for (size_t i = 0; i < NumParams; i++) {
    115     const clang::ParmVarDecl *PVD = FD->getParamDecl(i);
    116     const llvm::StringRef ParamName = PVD->getName();
    117     const clang::QualType Type = PVD->getType();
    118     const clang::QualType QT = Type.getCanonicalType();
    119     const clang::QualType UT = QT.getUnqualifiedType();
    120     int SpecialParameterIdx = lookupSpecialKernelParameter(ParamName);
    121 
    122     static const char KernelContextUnqualifiedTypeName[] =
    123         "const struct rs_kernel_context_t *";
    124     static const char KernelContextTypeName[] = "rs_kernel_context";
    125 
    126     // If the type is rs_context, it should have been named "context" and classified
    127     // as a special parameter.
    128     if (SpecialParameterIdx < 0 && UT.getAsString() == KernelContextUnqualifiedTypeName) {
    129       Context->ReportError(
    130           PVD->getLocation(),
    131           "The special parameter of type '%0' must be called "
    132           "'context' instead of '%1'.")
    133           << KernelContextTypeName << ParamName;
    134       SpecialParameterIdx = lookupSpecialKernelParameter("context");
    135     }
    136 
    137     // If it's not a special parameter, check that it appears before any special
    138     // parameter.
    139     if (SpecialParameterIdx < 0) {
    140       if (*IndexOfFirstSpecialParameter < NumParams) {
    141         Context->ReportError(PVD->getLocation(),
    142                              "In %0, parameter '%1' cannot "
    143                              "appear after any of the special parameters (%2).")
    144             << DiagnosticDescription()
    145             << ParamName
    146             << listSpecialKernelParameters(Context->getTargetAPI());
    147         valid = false;
    148       }
    149       continue;
    150     }
    151 
    152     const SpecialParameter &SP = specialParameterTable[SpecialParameterIdx];
    153 
    154     // Verify that this special parameter is OK for the current API level.
    155     if (Context->getTargetAPI() < SP.minAPI) {
    156       Context->ReportError(PVD->getLocation(),
    157                            "%0 targeting SDK levels "
    158                            "%1-%2 may not use special parameter '%3'.")
    159           << DiagnosticDescription()
    160           << SLANG_MINIMUM_TARGET_API << (SP.minAPI - 1)
    161           << SP.name;
    162       valid = false;
    163     }
    164 
    165     // Check that the order of the special parameters is correct.
    166     if (SpecialParameterIdx < LastSpecialParameterIdx) {
    167       Context->ReportError(
    168           PVD->getLocation(),
    169           "In %0, special parameter '%1' must "
    170           "be defined before special parameter '%2'.")
    171           << DiagnosticDescription()
    172           << SP.name
    173           << specialParameterTable[LastSpecialParameterIdx].name;
    174       valid = false;
    175     }
    176 
    177     // Validate the data type of the special parameter.
    178     switch (SP.kind) {
    179     case SPK_LOCATION: {
    180       // Location special parameters can only be int or uint.
    181       if (UT != C.UnsignedIntTy && UT != C.IntTy) {
    182         Context->ReportError(PVD->getLocation(),
    183                              "Special parameter '%0' must be of type 'int' or "
    184                              "'unsigned int'. It is of type '%1'.")
    185             << ParamName << Type.getAsString();
    186         valid = false;
    187       }
    188 
    189       // Ensure that all location special parameters have the same type.
    190       if (FirstLocationSpecialParameterIdx >= 0) {
    191         if (Type != FirstLocationSpecialParameterType) {
    192           Context->ReportError(
    193               PVD->getLocation(),
    194               "Special parameters '%0' and '%1' must be of the same type. "
    195               "'%0' is of type '%2' while '%1' is of type '%3'.")
    196               << specialParameterTable[FirstLocationSpecialParameterIdx].name
    197               << SP.name << FirstLocationSpecialParameterType.getAsString()
    198               << Type.getAsString();
    199           valid = false;
    200         }
    201       } else {
    202         FirstLocationSpecialParameterIdx = SpecialParameterIdx;
    203         FirstLocationSpecialParameterType = Type;
    204       }
    205     } break;
    206     case SPK_CONTEXT: {
    207       // Check that variables named "context" are of type rs_context.
    208       if (UT.getAsString() != KernelContextUnqualifiedTypeName) {
    209         Context->ReportError(PVD->getLocation(),
    210                              "Special parameter '%0' must be of type '%1'. "
    211                              "It is of type '%2'.")
    212             << ParamName << KernelContextTypeName
    213             << Type.getAsString();
    214         valid = false;
    215       }
    216     } break;
    217     default:
    218       slangAssert(!"Unexpected special parameter type");
    219     }
    220 
    221     // We should not be invoked if two parameters of the same name are present.
    222     slangAssert(!(*SignatureMetadata & SP.bitval));
    223     *SignatureMetadata |= SP.bitval;
    224 
    225     LastSpecialParameterIdx = SpecialParameterIdx;
    226     // If this is the first time we find a special parameter, save it.
    227     if (*IndexOfFirstSpecialParameter >= NumParams) {
    228       *IndexOfFirstSpecialParameter = i;
    229     }
    230   }
    231   return valid;
    232 }
    233 
    234 } // namespace slang
    235