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