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/Attr.h"
23 #include "clang/AST/Decl.h"
24 #include "clang/AST/TypeLoc.h"
25 
26 #include "llvm/IR/DerivedTypes.h"
27 
28 #include "bcinfo/MetadataExtractor.h"
29 
30 #include "slang_assert.h"
31 #include "slang_rs_context.h"
32 #include "slang_rs_export_type.h"
33 #include "slang_rs_special_func.h"
34 #include "slang_rs_special_kernel_param.h"
35 #include "slang_version.h"
36 
37 namespace {
38 
39 const size_t RS_KERNEL_INPUT_LIMIT = 8; // see frameworks/base/libs/rs/cpu_ref/rsCpuCoreRuntime.h
40 
isRootRSFunc(const clang::FunctionDecl * FD)41 bool isRootRSFunc(const clang::FunctionDecl *FD) {
42   if (!FD) {
43     return false;
44   }
45   return FD->getName().equals("root");
46 }
47 
48 } // end anonymous namespace
49 
50 namespace slang {
51 
52 // This function takes care of additional validation and construction of
53 // parameters related to forEach_* reflection.
validateAndConstructParams(RSContext * Context,const clang::FunctionDecl * FD)54 bool RSExportForEach::validateAndConstructParams(
55     RSContext *Context, const clang::FunctionDecl *FD) {
56   slangAssert(Context && FD);
57   bool valid = true;
58 
59   numParams = FD->getNumParams();
60 
61   if (Context->getTargetAPI() < SLANG_JB_TARGET_API) {
62     // Before JellyBean, we allowed only one kernel per file.  It must be called "root".
63     if (!isRootRSFunc(FD)) {
64       Context->ReportError(FD->getLocation(),
65                            "Non-root compute kernel %0() is "
66                            "not supported in SDK levels %1-%2")
67           << FD->getName() << SLANG_MINIMUM_TARGET_API
68           << (SLANG_JB_TARGET_API - 1);
69       return false;
70     }
71   }
72 
73   mResultType = FD->getReturnType().getCanonicalType();
74   // Compute kernel functions are defined differently when the
75   // "__attribute__((kernel))" is set.
76   if (FD->hasAttr<clang::RenderScriptKernelAttr>()) {
77     valid &= validateAndConstructKernelParams(Context, FD);
78   } else {
79     valid &= validateAndConstructOldStyleParams(Context, FD);
80   }
81 
82   valid &= setSignatureMetadata(Context, FD);
83   return valid;
84 }
85 
validateAndConstructOldStyleParams(RSContext * Context,const clang::FunctionDecl * FD)86 bool RSExportForEach::validateAndConstructOldStyleParams(
87     RSContext *Context, const clang::FunctionDecl *FD) {
88   slangAssert(Context && FD);
89   // If numParams is 0, we already marked this as a graphics root().
90   slangAssert(numParams > 0);
91 
92   bool valid = true;
93 
94   // Compute kernel functions of this style are required to return a void type.
95   clang::ASTContext &C = Context->getASTContext();
96   if (mResultType != C.VoidTy) {
97     Context->ReportError(FD->getLocation(),
98                          "Compute kernel %0() is required to return a "
99                          "void type")
100         << FD->getName();
101     valid = false;
102   }
103 
104   // Validate remaining parameter types
105 
106   size_t IndexOfFirstSpecialParameter = numParams;
107   valid &= processSpecialParameters(Context, FD, &IndexOfFirstSpecialParameter);
108 
109   // Validate the non-special parameters, which should all be found before the
110   // first special parameter.
111   for (size_t i = 0; i < IndexOfFirstSpecialParameter; i++) {
112     const clang::ParmVarDecl *PVD = FD->getParamDecl(i);
113     clang::QualType QT = PVD->getType().getCanonicalType();
114 
115     if (!QT->isPointerType()) {
116       Context->ReportError(PVD->getLocation(),
117                            "Compute kernel %0() cannot have non-pointer "
118                            "parameters besides special parameters (%1). Parameter '%2' is "
119                            "of type: '%3'")
120           << FD->getName() << listSpecialKernelParameters(Context->getTargetAPI())
121           << PVD->getName() << PVD->getType().getAsString();
122       valid = false;
123       continue;
124     }
125 
126     // The only non-const pointer should be out.
127     if (!QT->getPointeeType().isConstQualified()) {
128       if (mOut == nullptr) {
129         mOut = PVD;
130       } else {
131         Context->ReportError(PVD->getLocation(),
132                              "Compute kernel %0() can only have one non-const "
133                              "pointer parameter. Parameters '%1' and '%2' are "
134                              "both non-const.")
135             << FD->getName() << mOut->getName() << PVD->getName();
136         valid = false;
137       }
138     } else {
139       if (mIns.empty() && mOut == nullptr) {
140         mIns.push_back(PVD);
141       } else if (mUsrData == nullptr) {
142         mUsrData = PVD;
143       } else {
144         Context->ReportError(
145             PVD->getLocation(),
146             "Unexpected parameter '%0' for compute kernel %1()")
147             << PVD->getName() << FD->getName();
148         valid = false;
149       }
150     }
151   }
152 
153   if (mIns.empty() && !mOut) {
154     Context->ReportError(FD->getLocation(),
155                          "Compute kernel %0() must have at least one "
156                          "parameter for in or out")
157         << FD->getName();
158     valid = false;
159   }
160 
161   return valid;
162 }
163 
validateAndConstructKernelParams(RSContext * Context,const clang::FunctionDecl * FD)164 bool RSExportForEach::validateAndConstructKernelParams(
165     RSContext *Context, const clang::FunctionDecl *FD) {
166   slangAssert(Context && FD);
167   bool valid = true;
168   clang::ASTContext &C = Context->getASTContext();
169 
170   if (Context->getTargetAPI() < SLANG_JB_MR1_TARGET_API) {
171     Context->ReportError(FD->getLocation(),
172                          "Compute kernel %0() targeting SDK levels "
173                          "%1-%2 may not use pass-by-value with "
174                          "__attribute__((kernel))")
175         << FD->getName() << SLANG_MINIMUM_TARGET_API
176         << (SLANG_JB_MR1_TARGET_API - 1);
177     return false;
178   }
179 
180   // Denote that we are indeed a pass-by-value kernel.
181   mIsKernelStyle = true;
182   mHasReturnType = (mResultType != C.VoidTy);
183 
184   if (mResultType->isPointerType()) {
185     Context->ReportError(
186         FD->getTypeSpecStartLoc(),
187         "Compute kernel %0() cannot return a pointer type: '%1'")
188         << FD->getName() << mResultType.getAsString();
189     valid = false;
190   }
191 
192   // Validate remaining parameter types
193 
194   size_t IndexOfFirstSpecialParameter = numParams;
195   valid &= processSpecialParameters(Context, FD, &IndexOfFirstSpecialParameter);
196 
197   // Validate the non-special parameters, which should all be found before the
198   // first special.
199   for (size_t i = 0; i < IndexOfFirstSpecialParameter; i++) {
200     const clang::ParmVarDecl *PVD = FD->getParamDecl(i);
201 
202     if (Context->getTargetAPI() >= SLANG_M_TARGET_API || i == 0) {
203       if (i >= RS_KERNEL_INPUT_LIMIT) {
204         Context->ReportError(PVD->getLocation(),
205                              "Invalid parameter '%0' for compute kernel %1(). "
206                              "Kernels targeting SDK levels %2+ may not use "
207                              "more than %3 input parameters.") << PVD->getName() <<
208                              FD->getName() << SLANG_M_TARGET_API <<
209                              int(RS_KERNEL_INPUT_LIMIT);
210 
211       } else {
212         mIns.push_back(PVD);
213       }
214     } else {
215       Context->ReportError(PVD->getLocation(),
216                            "Invalid parameter '%0' for compute kernel %1(). "
217                            "Kernels targeting SDK levels %2-%3 may not use "
218                            "multiple input parameters.") << PVD->getName() <<
219                            FD->getName() << SLANG_MINIMUM_TARGET_API <<
220                            (SLANG_M_TARGET_API - 1);
221       valid = false;
222     }
223     clang::QualType QT = PVD->getType().getCanonicalType();
224     if (QT->isPointerType()) {
225       Context->ReportError(PVD->getLocation(),
226                            "Compute kernel %0() cannot have "
227                            "parameter '%1' of pointer type: '%2'")
228           << FD->getName() << PVD->getName() << PVD->getType().getAsString();
229       valid = false;
230     }
231   }
232 
233   // Check that we have at least one allocation to use for dimensions.
234   if (valid && mIns.empty() && !mHasReturnType && Context->getTargetAPI() < SLANG_M_TARGET_API) {
235     Context->ReportError(FD->getLocation(),
236                          "Compute kernel %0() targeting SDK levels "
237                          "%1-%2 must have at least one "
238                          "input parameter or a non-void return "
239                          "type")
240         << FD->getName() << SLANG_MINIMUM_TARGET_API
241         << (SLANG_M_TARGET_API - 1);
242     valid = false;
243   }
244 
245   return valid;
246 }
247 
248 // Process the optional special parameters:
249 // - Sets *IndexOfFirstSpecialParameter to the index of the first special parameter, or
250 //     FD->getNumParams() if none are found.
251 // - Add bits to mSpecialParameterSignatureMetadata for the found special parameters.
252 // Returns true if no errors.
processSpecialParameters(RSContext * Context,const clang::FunctionDecl * FD,size_t * IndexOfFirstSpecialParameter)253 bool RSExportForEach::processSpecialParameters(
254     RSContext *Context, const clang::FunctionDecl *FD,
255     size_t *IndexOfFirstSpecialParameter) {
256   auto DiagnosticCallback = [FD] {
257     std::ostringstream DiagnosticDescription;
258     DiagnosticDescription << "compute kernel " << FD->getName().str() << "()";
259     return DiagnosticDescription.str();
260   };
261   return slang::processSpecialKernelParameters(Context,
262                                                DiagnosticCallback,
263                                                FD,
264                                                IndexOfFirstSpecialParameter,
265                                                &mSpecialParameterSignatureMetadata);
266 }
267 
setSignatureMetadata(RSContext * Context,const clang::FunctionDecl * FD)268 bool RSExportForEach::setSignatureMetadata(RSContext *Context,
269                                            const clang::FunctionDecl *FD) {
270   mSignatureMetadata = 0;
271   bool valid = true;
272 
273   if (mIsKernelStyle) {
274     slangAssert(mOut == nullptr);
275     slangAssert(mUsrData == nullptr);
276   } else {
277     slangAssert(!mHasReturnType);
278   }
279 
280   // Set up the bitwise metadata encoding for runtime argument passing.
281   const bool HasOut = mOut || mHasReturnType;
282   mSignatureMetadata |= (hasIns() ?       bcinfo::MD_SIG_In     : 0);
283   mSignatureMetadata |= (HasOut ?         bcinfo::MD_SIG_Out    : 0);
284   mSignatureMetadata |= (mUsrData ?       bcinfo::MD_SIG_Usr    : 0);
285   mSignatureMetadata |= (mIsKernelStyle ? bcinfo::MD_SIG_Kernel : 0);  // pass-by-value
286   mSignatureMetadata |= mSpecialParameterSignatureMetadata;
287 
288   if (Context->getTargetAPI() < SLANG_ICS_TARGET_API) {
289     // APIs before ICS cannot skip between parameters. It is ok, however, for
290     // them to omit further parameters (i.e. skipping X is ok if you skip Y).
291     if (mSignatureMetadata != (bcinfo::MD_SIG_In | bcinfo::MD_SIG_Out | bcinfo::MD_SIG_Usr |
292                                bcinfo::MD_SIG_X | bcinfo::MD_SIG_Y) &&
293         mSignatureMetadata != (bcinfo::MD_SIG_In | bcinfo::MD_SIG_Out | bcinfo::MD_SIG_Usr |
294                                bcinfo::MD_SIG_X) &&
295         mSignatureMetadata != (bcinfo::MD_SIG_In | bcinfo::MD_SIG_Out | bcinfo::MD_SIG_Usr) &&
296         mSignatureMetadata != (bcinfo::MD_SIG_In | bcinfo::MD_SIG_Out) &&
297         mSignatureMetadata != (bcinfo::MD_SIG_In)) {
298       Context->ReportError(FD->getLocation(),
299                            "Compute kernel %0() targeting SDK levels "
300                            "%1-%2 may not skip parameters")
301           << FD->getName() << SLANG_MINIMUM_TARGET_API
302           << (SLANG_ICS_TARGET_API - 1);
303       valid = false;
304     }
305   }
306   return valid;
307 }
308 
Create(RSContext * Context,const clang::FunctionDecl * FD)309 RSExportForEach *RSExportForEach::Create(RSContext *Context,
310                                          const clang::FunctionDecl *FD) {
311   slangAssert(Context && FD);
312   llvm::StringRef Name = FD->getName();
313   RSExportForEach *FE;
314 
315   slangAssert(!Name.empty() && "Function must have a name");
316 
317   FE = new RSExportForEach(Context, Name, FD->getLocation());
318   FE->mOrdinal = Context->getNextForEachOrdinal();
319 
320   if (!FE->validateAndConstructParams(Context, FD)) {
321     return nullptr;
322   }
323 
324   clang::ASTContext &Ctx = Context->getASTContext();
325 
326   std::string Id = CreateDummyName("helper_foreach_param", FE->getName());
327 
328   // Construct type information about usrData, inputs, and
329   // outputs. Return null when there is an error exporting types.
330 
331   bool TypeExportError = false;
332 
333   // Extract the usrData parameter (if we have one)
334   if (FE->mUsrData) {
335     const clang::ParmVarDecl *PVD = FE->mUsrData;
336     clang::QualType QT = PVD->getType().getCanonicalType();
337     slangAssert(QT->isPointerType() &&
338                 QT->getPointeeType().isConstQualified());
339 
340     const clang::ASTContext &C = Context->getASTContext();
341     if (QT->getPointeeType().getCanonicalType().getUnqualifiedType() ==
342         C.VoidTy) {
343       // In the case of using const void*, we can't reflect an appopriate
344       // Java type, so we fall back to just reflecting the ain/aout parameters
345       FE->mUsrData = nullptr;
346     } else {
347       clang::RecordDecl *RD =
348           clang::RecordDecl::Create(Ctx, clang::TTK_Struct,
349                                     Ctx.getTranslationUnitDecl(),
350                                     clang::SourceLocation(),
351                                     clang::SourceLocation(),
352                                     &Ctx.Idents.get(Id));
353 
354       clang::FieldDecl *FD =
355           clang::FieldDecl::Create(Ctx,
356                                    RD,
357                                    clang::SourceLocation(),
358                                    clang::SourceLocation(),
359                                    PVD->getIdentifier(),
360                                    QT->getPointeeType(),
361                                    nullptr,
362                                    /* BitWidth = */ nullptr,
363                                    /* Mutable = */ false,
364                                    /* HasInit = */ clang::ICIS_NoInit);
365       RD->addDecl(FD);
366       RD->completeDefinition();
367 
368       // Create an export type iff we have a valid usrData type
369       clang::QualType T = Ctx.getTagDeclType(RD);
370       slangAssert(!T.isNull());
371 
372       RSExportType *ET =
373           RSExportType::Create(Context, T.getTypePtr(), LegacyKernelArgument);
374 
375       if (ET) {
376         slangAssert((ET->getClass() == RSExportType::ExportClassRecord) &&
377                     "Parameter packet must be a record");
378 
379         FE->mParamPacketType = static_cast<RSExportRecordType *>(ET);
380       } else {
381         TypeExportError = true;
382       }
383     }
384   }
385 
386   if (FE->hasIns()) {
387     for (InIter BI = FE->mIns.begin(), EI = FE->mIns.end(); BI != EI; BI++) {
388       const clang::Type *T = (*BI)->getType().getCanonicalType().getTypePtr();
389       ExportKind EK = (FE->mIsKernelStyle ? NotLegacyKernelArgument :
390                                             LegacyKernelArgument);
391       RSExportType *InExportType = RSExportType::Create(Context, T, EK);
392 
393       // It is not an error if we don't export an input type for legacy
394       // kernel arguments. This can happen in the case of a void pointer.
395       // See ReflectionState::addForEachIn().
396       if (FE->mIsKernelStyle && !InExportType) {
397         TypeExportError = true;
398       }
399 
400       FE->mInTypes.push_back(InExportType);
401     }
402   }
403 
404   if (FE->mIsKernelStyle && FE->mHasReturnType) {
405     const clang::Type *ReturnType = FE->mResultType.getTypePtr();
406     FE->mOutType = RSExportType::Create(Context, ReturnType,
407                                         NotLegacyKernelArgument);
408     TypeExportError |= !FE->mOutType;
409   } else if (FE->mOut) {
410     const clang::Type *OutType =
411         FE->mOut->getType().getCanonicalType().getTypePtr();
412     FE->mOutType = RSExportType::Create(Context, OutType, LegacyKernelArgument);
413     // It is not an error if we don't export an output type.
414     // This can happen in the case of a void pointer.
415   }
416 
417   if (TypeExportError) {
418     slangAssert(Context->getDiagnostics()->hasErrorOccurred() &&
419                 "Error exporting type but no diagnostic message issued!");
420     return nullptr;
421   }
422 
423   return FE;
424 }
425 
CreateDummyRoot(RSContext * Context)426 RSExportForEach *RSExportForEach::CreateDummyRoot(RSContext *Context) {
427   slangAssert(Context);
428   llvm::StringRef Name = "root";
429   RSExportForEach *FE = new RSExportForEach(Context, Name, clang::SourceLocation());
430   FE->mDummyRoot = true;
431   return FE;
432 }
433 
isRSForEachFunc(unsigned int targetAPI,const clang::FunctionDecl * FD)434 bool RSExportForEach::isRSForEachFunc(unsigned int targetAPI,
435                                       const clang::FunctionDecl *FD) {
436   if (!FD) {
437     return false;
438   }
439 
440   // Anything tagged as a kernel("") is definitely used with ForEach.
441   if (FD->hasAttr<clang::RenderScriptKernelAttr>()) {
442     return true;
443   }
444 
445   if (RSSpecialFunc::isGraphicsRootRSFunc(targetAPI, FD)) {
446     return false;
447   }
448 
449   // Check if first parameter is a pointer (which is required for ForEach).
450   unsigned int numParams = FD->getNumParams();
451 
452   if (numParams > 0) {
453     const clang::ParmVarDecl *PVD = FD->getParamDecl(0);
454     clang::QualType QT = PVD->getType().getCanonicalType();
455 
456     if (QT->isPointerType()) {
457       return true;
458     }
459 
460     // Any non-graphics root() is automatically a ForEach candidate.
461     // At this point, however, we know that it is not going to be a valid
462     // compute root() function (due to not having a pointer parameter). We
463     // still want to return true here, so that we can issue appropriate
464     // diagnostics.
465     if (isRootRSFunc(FD)) {
466       return true;
467     }
468   }
469 
470   return false;
471 }
472 
getNumInputs(unsigned int targetAPI,const clang::FunctionDecl * FD)473 unsigned RSExportForEach::getNumInputs(unsigned int targetAPI,
474                                        const clang::FunctionDecl *FD) {
475   unsigned numInputs = 0;
476   for (const clang::ParmVarDecl* param : FD->parameters()) {
477     if (!isSpecialKernelParameter(param->getName())) {
478       numInputs++;
479     }
480   }
481 
482   return numInputs;
483 }
484 
485 }  // namespace slang
486