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::KernelAttr>()) {
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);
318 
319   if (!FE->validateAndConstructParams(Context, FD)) {
320     return nullptr;
321   }
322 
323   clang::ASTContext &Ctx = Context->getASTContext();
324 
325   std::string Id = CreateDummyName("helper_foreach_param", FE->getName());
326 
327   // Extract the usrData parameter (if we have one)
328   if (FE->mUsrData) {
329     const clang::ParmVarDecl *PVD = FE->mUsrData;
330     clang::QualType QT = PVD->getType().getCanonicalType();
331     slangAssert(QT->isPointerType() &&
332                 QT->getPointeeType().isConstQualified());
333 
334     const clang::ASTContext &C = Context->getASTContext();
335     if (QT->getPointeeType().getCanonicalType().getUnqualifiedType() ==
336         C.VoidTy) {
337       // In the case of using const void*, we can't reflect an appopriate
338       // Java type, so we fall back to just reflecting the ain/aout parameters
339       FE->mUsrData = nullptr;
340     } else {
341       clang::RecordDecl *RD =
342           clang::RecordDecl::Create(Ctx, clang::TTK_Struct,
343                                     Ctx.getTranslationUnitDecl(),
344                                     clang::SourceLocation(),
345                                     clang::SourceLocation(),
346                                     &Ctx.Idents.get(Id));
347 
348       clang::FieldDecl *FD =
349           clang::FieldDecl::Create(Ctx,
350                                    RD,
351                                    clang::SourceLocation(),
352                                    clang::SourceLocation(),
353                                    PVD->getIdentifier(),
354                                    QT->getPointeeType(),
355                                    nullptr,
356                                    /* BitWidth = */ nullptr,
357                                    /* Mutable = */ false,
358                                    /* HasInit = */ clang::ICIS_NoInit);
359       RD->addDecl(FD);
360       RD->completeDefinition();
361 
362       // Create an export type iff we have a valid usrData type
363       clang::QualType T = Ctx.getTagDeclType(RD);
364       slangAssert(!T.isNull());
365 
366       RSExportType *ET =
367           RSExportType::Create(Context, T.getTypePtr(), LegacyKernelArgument);
368 
369       slangAssert(ET && "Failed to export a kernel");
370 
371       slangAssert((ET->getClass() == RSExportType::ExportClassRecord) &&
372                   "Parameter packet must be a record");
373 
374       FE->mParamPacketType = static_cast<RSExportRecordType *>(ET);
375     }
376   }
377 
378   // Construct type information about inputs and outputs. Return null when
379   // there is an error exporting types.
380 
381   bool TypeExportError = false;
382 
383   if (FE->hasIns()) {
384     for (InIter BI = FE->mIns.begin(), EI = FE->mIns.end(); BI != EI; BI++) {
385       const clang::Type *T = (*BI)->getType().getCanonicalType().getTypePtr();
386       ExportKind EK = (FE->mIsKernelStyle ? NotLegacyKernelArgument :
387                                             LegacyKernelArgument);
388       RSExportType *InExportType = RSExportType::Create(Context, T, EK);
389 
390       // It is not an error if we don't export an input type for legacy
391       // kernel arguments. This can happen in the case of a void pointer.
392       if (FE->mIsKernelStyle && !InExportType) {
393         TypeExportError = true;
394       }
395 
396       FE->mInTypes.push_back(InExportType);
397     }
398   }
399 
400   if (FE->mIsKernelStyle && FE->mHasReturnType) {
401     const clang::Type *ReturnType = FE->mResultType.getTypePtr();
402     FE->mOutType = RSExportType::Create(Context, ReturnType,
403                                         NotLegacyKernelArgument);
404     TypeExportError |= !FE->mOutType;
405   } else if (FE->mOut) {
406     const clang::Type *OutType =
407         FE->mOut->getType().getCanonicalType().getTypePtr();
408     FE->mOutType = RSExportType::Create(Context, OutType, LegacyKernelArgument);
409     // It is not an error if we don't export an output type.
410     // This can happen in the case of a void pointer.
411   }
412 
413   if (TypeExportError) {
414     slangAssert(Context->getDiagnostics()->hasErrorOccurred() &&
415                 "Error exporting type but no diagnostic message issued!");
416     return nullptr;
417   }
418 
419   return FE;
420 }
421 
CreateDummyRoot(RSContext * Context)422 RSExportForEach *RSExportForEach::CreateDummyRoot(RSContext *Context) {
423   slangAssert(Context);
424   llvm::StringRef Name = "root";
425   RSExportForEach *FE = new RSExportForEach(Context, Name);
426   FE->mDummyRoot = true;
427   return FE;
428 }
429 
isRSForEachFunc(unsigned int targetAPI,const clang::FunctionDecl * FD)430 bool RSExportForEach::isRSForEachFunc(unsigned int targetAPI,
431                                       const clang::FunctionDecl *FD) {
432   if (!FD) {
433     return false;
434   }
435 
436   // Anything tagged as a kernel("") is definitely used with ForEach.
437   if (auto *Kernel = FD->getAttr<clang::KernelAttr>()) {
438     return Kernel->getKernelKind().empty();
439   }
440 
441   if (RSSpecialFunc::isGraphicsRootRSFunc(targetAPI, FD)) {
442     return false;
443   }
444 
445   // Check if first parameter is a pointer (which is required for ForEach).
446   unsigned int numParams = FD->getNumParams();
447 
448   if (numParams > 0) {
449     const clang::ParmVarDecl *PVD = FD->getParamDecl(0);
450     clang::QualType QT = PVD->getType().getCanonicalType();
451 
452     if (QT->isPointerType()) {
453       return true;
454     }
455 
456     // Any non-graphics root() is automatically a ForEach candidate.
457     // At this point, however, we know that it is not going to be a valid
458     // compute root() function (due to not having a pointer parameter). We
459     // still want to return true here, so that we can issue appropriate
460     // diagnostics.
461     if (isRootRSFunc(FD)) {
462       return true;
463     }
464   }
465 
466   return false;
467 }
468 
getNumInputs(unsigned int targetAPI,const clang::FunctionDecl * FD)469 unsigned RSExportForEach::getNumInputs(unsigned int targetAPI,
470                                        const clang::FunctionDecl *FD) {
471   unsigned numInputs = 0;
472   for (const clang::ParmVarDecl* param : FD->params()) {
473     if (!isSpecialKernelParameter(param->getName())) {
474       numInputs++;
475     }
476   }
477 
478   return numInputs;
479 }
480 
481 }  // namespace slang
482