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