• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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 
ReportNameError(clang::DiagnosticsEngine * DiagEngine,clang::ParmVarDecl const * PVD)37 static 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.
validateAndConstructParams(RSContext * Context,const clang::FunctionDecl * FD)55 bool 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   numParams = FD->getNumParams();
63   slangAssert(numParams > 0);
64 
65   if (Context->getTargetAPI() < SLANG_JB_TARGET_API) {
66     if (!isRootRSFunc(FD)) {
67       DiagEngine->Report(
68         clang::FullSourceLoc(FD->getLocation(), DiagEngine->getSourceManager()),
69         DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
70                                     "Non-root compute kernel %0() is "
71                                     "not supported in SDK levels %1-%2"))
72         << FD->getName()
73         << SLANG_MINIMUM_TARGET_API
74         << (SLANG_JB_TARGET_API - 1);
75       return false;
76     }
77   }
78 
79   // Compute kernel functions are required to return a void type for now
80   if (FD->getResultType().getCanonicalType() != C.VoidTy) {
81     DiagEngine->Report(
82       clang::FullSourceLoc(FD->getLocation(), DiagEngine->getSourceManager()),
83       DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
84                                   "Compute kernel %0() is required to return a "
85                                   "void type")) << FD->getName();
86     valid = false;
87   }
88 
89   // Validate remaining parameter types
90   // TODO(all): Add support for LOD/face when we have them
91 
92   size_t i = 0;
93   const clang::ParmVarDecl *PVD = FD->getParamDecl(i);
94   clang::QualType QT = PVD->getType().getCanonicalType();
95 
96   // Check for const T1 *in
97   if (QT->isPointerType() && QT->getPointeeType().isConstQualified()) {
98     mIn = PVD;
99     i++;  // advance parameter pointer
100   }
101 
102   // Check for T2 *out
103   if (i < numParams) {
104     PVD = FD->getParamDecl(i);
105     QT = PVD->getType().getCanonicalType();
106     if (QT->isPointerType() && !QT->getPointeeType().isConstQualified()) {
107       mOut = PVD;
108       i++;  // advance parameter pointer
109     }
110   }
111 
112   if (!mIn && !mOut) {
113     DiagEngine->Report(
114       clang::FullSourceLoc(FD->getLocation(),
115                            DiagEngine->getSourceManager()),
116       DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
117                                   "Compute kernel %0() must have at least one "
118                                   "parameter for in or out")) << FD->getName();
119     valid = false;
120   }
121 
122   // Check for T3 *usrData
123   if (i < numParams) {
124     PVD = FD->getParamDecl(i);
125     QT = PVD->getType().getCanonicalType();
126     if (QT->isPointerType() && QT->getPointeeType().isConstQualified()) {
127       mUsrData = PVD;
128       i++;  // advance parameter pointer
129     }
130   }
131 
132   while (i < numParams) {
133     PVD = FD->getParamDecl(i);
134     QT = PVD->getType().getCanonicalType();
135 
136     if (QT.getUnqualifiedType() != C.UnsignedIntTy) {
137       DiagEngine->Report(
138         clang::FullSourceLoc(PVD->getLocation(),
139                              DiagEngine->getSourceManager()),
140         DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
141                                     "Unexpected kernel %0() parameter '%1' "
142                                     "of type '%2'"))
143         << FD->getName() << PVD->getName() << PVD->getType().getAsString();
144       valid = false;
145     } else {
146       llvm::StringRef ParamName = PVD->getName();
147       if (ParamName.equals("x")) {
148         if (mX) {
149           ReportNameError(DiagEngine, PVD);
150           valid = false;
151         } else if (mY) {
152           // Can't go back to X after skipping Y
153           ReportNameError(DiagEngine, PVD);
154           valid = false;
155         } else {
156           mX = PVD;
157         }
158       } else if (ParamName.equals("y")) {
159         if (mY) {
160           ReportNameError(DiagEngine, PVD);
161           valid = false;
162         } else {
163           mY = PVD;
164         }
165       } else {
166         if (!mX && !mY) {
167           mX = PVD;
168         } else if (!mY) {
169           mY = PVD;
170         } else {
171           DiagEngine->Report(
172             clang::FullSourceLoc(PVD->getLocation(),
173                                  DiagEngine->getSourceManager()),
174             DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
175                                         "Unexpected kernel %0() parameter '%1' "
176                                         "of type '%2'"))
177             << FD->getName() << PVD->getName() << PVD->getType().getAsString();
178           valid = false;
179         }
180       }
181     }
182 
183     i++;
184   }
185 
186   mSignatureMetadata = 0;
187   if (valid) {
188     // Set up the bitwise metadata encoding for runtime argument passing.
189     mSignatureMetadata |= (mIn ?       0x01 : 0);
190     mSignatureMetadata |= (mOut ?      0x02 : 0);
191     mSignatureMetadata |= (mUsrData ?  0x04 : 0);
192     mSignatureMetadata |= (mX ?        0x08 : 0);
193     mSignatureMetadata |= (mY ?        0x10 : 0);
194   }
195 
196   if (Context->getTargetAPI() < SLANG_ICS_TARGET_API) {
197     // APIs before ICS cannot skip between parameters. It is ok, however, for
198     // them to omit further parameters (i.e. skipping X is ok if you skip Y).
199     if (mSignatureMetadata != 0x1f &&  // In, Out, UsrData, X, Y
200         mSignatureMetadata != 0x0f &&  // In, Out, UsrData, X
201         mSignatureMetadata != 0x07 &&  // In, Out, UsrData
202         mSignatureMetadata != 0x03 &&  // In, Out
203         mSignatureMetadata != 0x01) {  // In
204       DiagEngine->Report(
205         clang::FullSourceLoc(FD->getLocation(),
206                              DiagEngine->getSourceManager()),
207         DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
208                                     "Compute kernel %0() targeting SDK levels "
209                                     "%1-%2 may not skip parameters"))
210         << FD->getName() << SLANG_MINIMUM_TARGET_API
211         << (SLANG_ICS_TARGET_API - 1);
212       valid = false;
213     }
214   }
215 
216   return valid;
217 }
218 
Create(RSContext * Context,const clang::FunctionDecl * FD)219 RSExportForEach *RSExportForEach::Create(RSContext *Context,
220                                          const clang::FunctionDecl *FD) {
221   slangAssert(Context && FD);
222   llvm::StringRef Name = FD->getName();
223   RSExportForEach *FE;
224 
225   slangAssert(!Name.empty() && "Function must have a name");
226 
227   FE = new RSExportForEach(Context, Name);
228 
229   if (!FE->validateAndConstructParams(Context, FD)) {
230     return NULL;
231   }
232 
233   clang::ASTContext &Ctx = Context->getASTContext();
234 
235   std::string Id(DUMMY_RS_TYPE_NAME_PREFIX"helper_foreach_param:");
236   Id.append(FE->getName()).append(DUMMY_RS_TYPE_NAME_POSTFIX);
237 
238   // Extract the usrData parameter (if we have one)
239   if (FE->mUsrData) {
240     const clang::ParmVarDecl *PVD = FE->mUsrData;
241     clang::QualType QT = PVD->getType().getCanonicalType();
242     slangAssert(QT->isPointerType() &&
243                 QT->getPointeeType().isConstQualified());
244 
245     const clang::ASTContext &C = Context->getASTContext();
246     if (QT->getPointeeType().getCanonicalType().getUnqualifiedType() ==
247         C.VoidTy) {
248       // In the case of using const void*, we can't reflect an appopriate
249       // Java type, so we fall back to just reflecting the ain/aout parameters
250       FE->mUsrData = NULL;
251     } else {
252       clang::RecordDecl *RD =
253           clang::RecordDecl::Create(Ctx, clang::TTK_Struct,
254                                     Ctx.getTranslationUnitDecl(),
255                                     clang::SourceLocation(),
256                                     clang::SourceLocation(),
257                                     &Ctx.Idents.get(Id));
258 
259       clang::FieldDecl *FD =
260           clang::FieldDecl::Create(Ctx,
261                                    RD,
262                                    clang::SourceLocation(),
263                                    clang::SourceLocation(),
264                                    PVD->getIdentifier(),
265                                    QT->getPointeeType(),
266                                    NULL,
267                                    /* BitWidth = */ NULL,
268                                    /* Mutable = */ false,
269                                    /* HasInit = */ false);
270       RD->addDecl(FD);
271       RD->completeDefinition();
272 
273       // Create an export type iff we have a valid usrData type
274       clang::QualType T = Ctx.getTagDeclType(RD);
275       slangAssert(!T.isNull());
276 
277       RSExportType *ET = RSExportType::Create(Context, T.getTypePtr());
278 
279       if (ET == NULL) {
280         fprintf(stderr, "Failed to export the function %s. There's at least "
281                         "one parameter whose type is not supported by the "
282                         "reflection\n", FE->getName().c_str());
283         return NULL;
284       }
285 
286       slangAssert((ET->getClass() == RSExportType::ExportClassRecord) &&
287                   "Parameter packet must be a record");
288 
289       FE->mParamPacketType = static_cast<RSExportRecordType *>(ET);
290     }
291   }
292 
293   if (FE->mIn) {
294     const clang::Type *T = FE->mIn->getType().getCanonicalType().getTypePtr();
295     FE->mInType = RSExportType::Create(Context, T);
296   }
297 
298   if (FE->mOut) {
299     const clang::Type *T = FE->mOut->getType().getCanonicalType().getTypePtr();
300     FE->mOutType = RSExportType::Create(Context, T);
301   }
302 
303   return FE;
304 }
305 
CreateDummyRoot(RSContext * Context)306 RSExportForEach *RSExportForEach::CreateDummyRoot(RSContext *Context) {
307   slangAssert(Context);
308   llvm::StringRef Name = "root";
309   RSExportForEach *FE = new RSExportForEach(Context, Name);
310   FE->mDummyRoot = true;
311   return FE;
312 }
313 
isGraphicsRootRSFunc(int targetAPI,const clang::FunctionDecl * FD)314 bool RSExportForEach::isGraphicsRootRSFunc(int targetAPI,
315                                            const clang::FunctionDecl *FD) {
316   if (!isRootRSFunc(FD)) {
317     return false;
318   }
319 
320   if (FD->getNumParams() == 0) {
321     // Graphics root function
322     return true;
323   }
324 
325   // Check for legacy graphics root function (with single parameter).
326   if ((targetAPI < SLANG_ICS_TARGET_API) && (FD->getNumParams() == 1)) {
327     const clang::QualType &IntType = FD->getASTContext().IntTy;
328     if (FD->getResultType().getCanonicalType() == IntType) {
329       return true;
330     }
331   }
332 
333   return false;
334 }
335 
isRSForEachFunc(int targetAPI,const clang::FunctionDecl * FD)336 bool RSExportForEach::isRSForEachFunc(int targetAPI,
337     const clang::FunctionDecl *FD) {
338   if (isGraphicsRootRSFunc(targetAPI, FD)) {
339     return false;
340   }
341 
342   // Check if first parameter is a pointer (which is required for ForEach).
343   unsigned int numParams = FD->getNumParams();
344 
345   if (numParams > 0) {
346     const clang::ParmVarDecl *PVD = FD->getParamDecl(0);
347     clang::QualType QT = PVD->getType().getCanonicalType();
348 
349     if (QT->isPointerType()) {
350       return true;
351     }
352 
353     // Any non-graphics root() is automatically a ForEach candidate.
354     // At this point, however, we know that it is not going to be a valid
355     // compute root() function (due to not having a pointer parameter). We
356     // still want to return true here, so that we can issue appropriate
357     // diagnostics.
358     if (isRootRSFunc(FD)) {
359       return true;
360     }
361   }
362 
363   return false;
364 }
365 
366 bool
validateSpecialFuncDecl(int targetAPI,clang::DiagnosticsEngine * DiagEngine,clang::FunctionDecl const * FD)367 RSExportForEach::validateSpecialFuncDecl(int targetAPI,
368                                          clang::DiagnosticsEngine *DiagEngine,
369                                          clang::FunctionDecl const *FD) {
370   slangAssert(DiagEngine && FD);
371   bool valid = true;
372   const clang::ASTContext &C = FD->getASTContext();
373   const clang::QualType &IntType = FD->getASTContext().IntTy;
374 
375   if (isGraphicsRootRSFunc(targetAPI, FD)) {
376     if ((targetAPI < SLANG_ICS_TARGET_API) && (FD->getNumParams() == 1)) {
377       // Legacy graphics root function
378       const clang::ParmVarDecl *PVD = FD->getParamDecl(0);
379       clang::QualType QT = PVD->getType().getCanonicalType();
380       if (QT != IntType) {
381         DiagEngine->Report(
382           clang::FullSourceLoc(PVD->getLocation(),
383                                DiagEngine->getSourceManager()),
384           DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
385                                       "invalid parameter type for legacy "
386                                       "graphics root() function: %0"))
387           << PVD->getType();
388         valid = false;
389       }
390     }
391 
392     // Graphics root function, so verify that it returns an int
393     if (FD->getResultType().getCanonicalType() != IntType) {
394       DiagEngine->Report(
395         clang::FullSourceLoc(FD->getLocation(),
396                              DiagEngine->getSourceManager()),
397         DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
398                                     "root() is required to return "
399                                     "an int for graphics usage"));
400       valid = false;
401     }
402   } else if (isInitRSFunc(FD) || isDtorRSFunc(FD)) {
403     if (FD->getNumParams() != 0) {
404       DiagEngine->Report(
405           clang::FullSourceLoc(FD->getLocation(),
406                                DiagEngine->getSourceManager()),
407           DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
408                                       "%0(void) is required to have no "
409                                       "parameters")) << FD->getName();
410       valid = false;
411     }
412 
413     if (FD->getResultType().getCanonicalType() != C.VoidTy) {
414       DiagEngine->Report(
415           clang::FullSourceLoc(FD->getLocation(),
416                                DiagEngine->getSourceManager()),
417           DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
418                                       "%0(void) is required to have a void "
419                                       "return type")) << FD->getName();
420       valid = false;
421     }
422   } else {
423     slangAssert(false && "must be called on root, init or .rs.dtor function!");
424   }
425 
426   return valid;
427 }
428 
429 }  // namespace slang
430