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