1 /* 2 * Copyright 2011, 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/Decl.h" 23 #include "clang/AST/TypeLoc.h" 24 25 #include "llvm/DerivedTypes.h" 26 #include "llvm/Target/TargetData.h" 27 28 #include "slang_assert.h" 29 #include "slang_rs_context.h" 30 #include "slang_rs_export_type.h" 31 #include "slang_version.h" 32 33 namespace slang { 34 35 namespace { 36 37 static void ReportNameError(clang::Diagnostic *Diags, 38 const clang::ParmVarDecl *PVD) { 39 slangAssert(Diags && PVD); 40 const clang::SourceManager &SM = Diags->getSourceManager(); 41 42 Diags->Report(clang::FullSourceLoc(PVD->getLocation(), SM), 43 Diags->getCustomDiagID(clang::Diagnostic::Error, 44 "Duplicate parameter entry (by position/name): '%0'")) 45 << PVD->getName(); 46 return; 47 } 48 49 } // namespace 50 51 // This function takes care of additional validation and construction of 52 // parameters related to forEach_* reflection. 53 bool RSExportForEach::validateAndConstructParams( 54 RSContext *Context, const clang::FunctionDecl *FD) { 55 slangAssert(Context && FD); 56 bool valid = true; 57 clang::ASTContext &C = Context->getASTContext(); 58 clang::Diagnostic *Diags = Context->getDiagnostics(); 59 60 if (!isRootRSFunc(FD)) { 61 slangAssert(false && "must be called on compute root function!"); 62 } 63 64 numParams = FD->getNumParams(); 65 slangAssert(numParams > 0); 66 67 // Compute root functions are required to return a void type for now 68 if (FD->getResultType().getCanonicalType() != C.VoidTy) { 69 Diags->Report( 70 clang::FullSourceLoc(FD->getLocation(), Diags->getSourceManager()), 71 Diags->getCustomDiagID(clang::Diagnostic::Error, 72 "compute root() is required to return a " 73 "void type")); 74 valid = false; 75 } 76 77 // Validate remaining parameter types 78 // TODO(all): Add support for LOD/face when we have them 79 80 size_t i = 0; 81 const clang::ParmVarDecl *PVD = FD->getParamDecl(i); 82 clang::QualType QT = PVD->getType().getCanonicalType(); 83 84 // Check for const T1 *in 85 if (QT->isPointerType() && QT->getPointeeType().isConstQualified()) { 86 mIn = PVD; 87 i++; // advance parameter pointer 88 } 89 90 // Check for T2 *out 91 if (i < numParams) { 92 PVD = FD->getParamDecl(i); 93 QT = PVD->getType().getCanonicalType(); 94 if (QT->isPointerType() && !QT->getPointeeType().isConstQualified()) { 95 mOut = PVD; 96 i++; // advance parameter pointer 97 } 98 } 99 100 if (!mIn && !mOut) { 101 Diags->Report( 102 clang::FullSourceLoc(FD->getLocation(), 103 Diags->getSourceManager()), 104 Diags->getCustomDiagID(clang::Diagnostic::Error, 105 "Compute root() must have at least one " 106 "parameter for in or out")); 107 valid = false; 108 } 109 110 // Check for T3 *usrData 111 if (i < numParams) { 112 PVD = FD->getParamDecl(i); 113 QT = PVD->getType().getCanonicalType(); 114 if (QT->isPointerType() && QT->getPointeeType().isConstQualified()) { 115 mUsrData = PVD; 116 i++; // advance parameter pointer 117 } 118 } 119 120 while (i < numParams) { 121 PVD = FD->getParamDecl(i); 122 QT = PVD->getType().getCanonicalType(); 123 124 if (QT.getUnqualifiedType() != C.UnsignedIntTy) { 125 Diags->Report( 126 clang::FullSourceLoc(PVD->getLocation(), 127 Diags->getSourceManager()), 128 Diags->getCustomDiagID(clang::Diagnostic::Error, 129 "Unexpected root() parameter '%0' " 130 "of type '%1'")) 131 << PVD->getName() << PVD->getType().getAsString(); 132 valid = false; 133 } else { 134 llvm::StringRef ParamName = PVD->getName(); 135 if (ParamName.equals("x")) { 136 if (mX) { 137 ReportNameError(Diags, PVD); 138 valid = false; 139 } else if (mY) { 140 // Can't go back to X after skipping Y 141 ReportNameError(Diags, PVD); 142 valid = false; 143 } else { 144 mX = PVD; 145 } 146 } else if (ParamName.equals("y")) { 147 if (mY) { 148 ReportNameError(Diags, PVD); 149 valid = false; 150 } else { 151 mY = PVD; 152 } 153 } else { 154 if (!mX && !mY) { 155 mX = PVD; 156 } else if (!mY) { 157 mY = PVD; 158 } else { 159 Diags->Report( 160 clang::FullSourceLoc(PVD->getLocation(), 161 Diags->getSourceManager()), 162 Diags->getCustomDiagID(clang::Diagnostic::Error, 163 "Unexpected root() parameter '%0' " 164 "of type '%1'")) 165 << PVD->getName() << PVD->getType().getAsString(); 166 valid = false; 167 } 168 } 169 } 170 171 i++; 172 } 173 174 mMetadataEncoding = 0; 175 if (valid) { 176 // Set up the bitwise metadata encoding for runtime argument passing. 177 mMetadataEncoding |= (mIn ? 0x01 : 0); 178 mMetadataEncoding |= (mOut ? 0x02 : 0); 179 mMetadataEncoding |= (mUsrData ? 0x04 : 0); 180 mMetadataEncoding |= (mX ? 0x08 : 0); 181 mMetadataEncoding |= (mY ? 0x10 : 0); 182 } 183 184 if (Context->getTargetAPI() < SLANG_ICS_TARGET_API) { 185 // APIs before ICS cannot skip between parameters. It is ok, however, for 186 // them to omit further parameters (i.e. skipping X is ok if you skip Y). 187 if (mMetadataEncoding != 0x1f && // In, Out, UsrData, X, Y 188 mMetadataEncoding != 0x0f && // In, Out, UsrData, X 189 mMetadataEncoding != 0x07 && // In, Out, UsrData 190 mMetadataEncoding != 0x03 && // In, Out 191 mMetadataEncoding != 0x01) { // In 192 Diags->Report( 193 clang::FullSourceLoc(FD->getLocation(), 194 Diags->getSourceManager()), 195 Diags->getCustomDiagID(clang::Diagnostic::Error, 196 "Compute root() targeting SDK levels %0-%1 " 197 "may not skip parameters")) 198 << SLANG_MINIMUM_TARGET_API << (SLANG_ICS_TARGET_API-1); 199 valid = false; 200 } 201 } 202 203 204 return valid; 205 } 206 207 RSExportForEach *RSExportForEach::Create(RSContext *Context, 208 const clang::FunctionDecl *FD) { 209 slangAssert(Context && FD); 210 llvm::StringRef Name = FD->getName(); 211 RSExportForEach *FE; 212 213 slangAssert(!Name.empty() && "Function must have a name"); 214 215 FE = new RSExportForEach(Context, Name, FD); 216 217 if (!FE->validateAndConstructParams(Context, FD)) { 218 return NULL; 219 } 220 221 clang::ASTContext &Ctx = Context->getASTContext(); 222 223 std::string Id(DUMMY_RS_TYPE_NAME_PREFIX"helper_foreach_param:"); 224 Id.append(FE->getName()).append(DUMMY_RS_TYPE_NAME_POSTFIX); 225 226 // Extract the usrData parameter (if we have one) 227 if (FE->mUsrData) { 228 const clang::ParmVarDecl *PVD = FE->mUsrData; 229 clang::QualType QT = PVD->getType().getCanonicalType(); 230 slangAssert(QT->isPointerType() && 231 QT->getPointeeType().isConstQualified()); 232 233 const clang::ASTContext &C = Context->getASTContext(); 234 if (QT->getPointeeType().getCanonicalType().getUnqualifiedType() == 235 C.VoidTy) { 236 // In the case of using const void*, we can't reflect an appopriate 237 // Java type, so we fall back to just reflecting the ain/aout parameters 238 FE->mUsrData = NULL; 239 } else { 240 clang::RecordDecl *RD = 241 clang::RecordDecl::Create(Ctx, clang::TTK_Struct, 242 Ctx.getTranslationUnitDecl(), 243 clang::SourceLocation(), 244 clang::SourceLocation(), 245 &Ctx.Idents.get(Id)); 246 247 llvm::StringRef ParamName = PVD->getName(); 248 clang::FieldDecl *FD = 249 clang::FieldDecl::Create(Ctx, 250 RD, 251 clang::SourceLocation(), 252 clang::SourceLocation(), 253 PVD->getIdentifier(), 254 QT->getPointeeType(), 255 NULL, 256 /* BitWidth = */ NULL, 257 /* Mutable = */ false, 258 /* HasInit = */ false); 259 RD->addDecl(FD); 260 RD->completeDefinition(); 261 262 // Create an export type iff we have a valid usrData type 263 clang::QualType T = Ctx.getTagDeclType(RD); 264 slangAssert(!T.isNull()); 265 266 RSExportType *ET = RSExportType::Create(Context, T.getTypePtr()); 267 268 if (ET == NULL) { 269 fprintf(stderr, "Failed to export the function %s. There's at least " 270 "one parameter whose type is not supported by the " 271 "reflection\n", FE->getName().c_str()); 272 return NULL; 273 } 274 275 slangAssert((ET->getClass() == RSExportType::ExportClassRecord) && 276 "Parameter packet must be a record"); 277 278 FE->mParamPacketType = static_cast<RSExportRecordType *>(ET); 279 } 280 } 281 282 if (FE->mIn) { 283 const clang::Type *T = FE->mIn->getType().getCanonicalType().getTypePtr(); 284 FE->mInType = RSExportType::Create(Context, T); 285 } 286 287 if (FE->mOut) { 288 const clang::Type *T = FE->mOut->getType().getCanonicalType().getTypePtr(); 289 FE->mOutType = RSExportType::Create(Context, T); 290 } 291 292 return FE; 293 } 294 295 bool RSExportForEach::isRSForEachFunc(int targetAPI, 296 const clang::FunctionDecl *FD) { 297 // We currently support only compute root() being exported via forEach 298 if (!isRootRSFunc(FD)) { 299 return false; 300 } 301 302 if (FD->getNumParams() == 0) { 303 // Graphics compute function 304 return false; 305 } 306 307 // Handle legacy graphics root functions. 308 if ((targetAPI < SLANG_ICS_TARGET_API) && (FD->getNumParams() == 1)) { 309 const clang::ParmVarDecl *PVD = FD->getParamDecl(0); 310 clang::QualType QT = PVD->getType().getCanonicalType(); 311 const clang::QualType &IntType = FD->getASTContext().IntTy; 312 if ((FD->getResultType().getCanonicalType() == IntType) && 313 (QT == IntType)) { 314 return false; 315 } 316 } 317 318 return true; 319 } 320 321 bool RSExportForEach::validateSpecialFuncDecl(int targetAPI, 322 clang::Diagnostic *Diags, 323 const clang::FunctionDecl *FD) { 324 slangAssert(Diags && FD); 325 bool valid = true; 326 const clang::ASTContext &C = FD->getASTContext(); 327 328 if (isRootRSFunc(FD)) { 329 unsigned int numParams = FD->getNumParams(); 330 if (numParams == 0) { 331 // Graphics root function, so verify that it returns an int 332 if (FD->getResultType().getCanonicalType() != C.IntTy) { 333 Diags->Report( 334 clang::FullSourceLoc(FD->getLocation(), Diags->getSourceManager()), 335 Diags->getCustomDiagID(clang::Diagnostic::Error, 336 "root(void) is required to return " 337 "an int for graphics usage")); 338 valid = false; 339 } 340 } else if ((targetAPI < SLANG_ICS_TARGET_API) && (numParams == 1)) { 341 // Legacy graphics root function 342 // This has already been validated in isRSForEachFunc(). 343 } else { 344 slangAssert(false && 345 "Should not call validateSpecialFuncDecl() on compute root()"); 346 } 347 } else if (isInitRSFunc(FD) || isDtorRSFunc(FD)) { 348 if (FD->getNumParams() != 0) { 349 Diags->Report( 350 clang::FullSourceLoc(FD->getLocation(), Diags->getSourceManager()), 351 Diags->getCustomDiagID(clang::Diagnostic::Error, 352 "%0(void) is required to have no " 353 "parameters")) << FD->getName(); 354 valid = false; 355 } 356 357 if (FD->getResultType().getCanonicalType() != C.VoidTy) { 358 Diags->Report( 359 clang::FullSourceLoc(FD->getLocation(), Diags->getSourceManager()), 360 Diags->getCustomDiagID(clang::Diagnostic::Error, 361 "%0(void) is required to have a void " 362 "return type")) << FD->getName(); 363 valid = false; 364 } 365 } else { 366 slangAssert(false && "must be called on root, init or .rs.dtor function!"); 367 } 368 369 return valid; 370 } 371 372 } // namespace slang 373