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