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 "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
54 // This function takes care of additional validation and construction of
55 // parameters related to forEach_* reflection.
validateAndConstructParams(RSContext * Context,const clang::FunctionDecl * FD)56 bool RSExportForEach::validateAndConstructParams(
57 RSContext *Context, const clang::FunctionDecl *FD) {
58 slangAssert(Context && FD);
59 bool valid = true;
60 clang::ASTContext &C = Context->getASTContext();
61 clang::DiagnosticsEngine *DiagEngine = Context->getDiagnostics();
62
63 numParams = FD->getNumParams();
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 mResultType = FD->getResultType().getCanonicalType();
80 // Compute kernel functions are required to return a void type or
81 // be marked explicitly as a kernel. In the case of
82 // "__attribute__((kernel))", we handle validation differently.
83 if (FD->hasAttr<clang::KernelAttr>()) {
84 return validateAndConstructKernelParams(Context, FD);
85 }
86
87 // If numParams is 0, we already marked this as a graphics root().
88 slangAssert(numParams > 0);
89
90 // Compute kernel functions of this type are required to return a void type.
91 if (mResultType != C.VoidTy) {
92 DiagEngine->Report(
93 clang::FullSourceLoc(FD->getLocation(), DiagEngine->getSourceManager()),
94 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
95 "Compute kernel %0() is required to return a "
96 "void type")) << FD->getName();
97 valid = false;
98 }
99
100 // Validate remaining parameter types
101 // TODO(all): Add support for LOD/face when we have them
102
103 size_t i = 0;
104 const clang::ParmVarDecl *PVD = FD->getParamDecl(i);
105 clang::QualType QT = PVD->getType().getCanonicalType();
106
107 // Check for const T1 *in
108 if (QT->isPointerType() && QT->getPointeeType().isConstQualified()) {
109 mIn = PVD;
110 i++; // advance parameter pointer
111 }
112
113 // Check for T2 *out
114 if (i < numParams) {
115 PVD = FD->getParamDecl(i);
116 QT = PVD->getType().getCanonicalType();
117 if (QT->isPointerType() && !QT->getPointeeType().isConstQualified()) {
118 mOut = PVD;
119 i++; // advance parameter pointer
120 }
121 }
122
123 if (!mIn && !mOut) {
124 DiagEngine->Report(
125 clang::FullSourceLoc(FD->getLocation(),
126 DiagEngine->getSourceManager()),
127 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
128 "Compute kernel %0() must have at least one "
129 "parameter for in or out")) << FD->getName();
130 valid = false;
131 }
132
133 // Check for T3 *usrData
134 if (i < numParams) {
135 PVD = FD->getParamDecl(i);
136 QT = PVD->getType().getCanonicalType();
137 if (QT->isPointerType() && QT->getPointeeType().isConstQualified()) {
138 mUsrData = PVD;
139 i++; // advance parameter pointer
140 }
141 }
142
143 while (i < numParams) {
144 PVD = FD->getParamDecl(i);
145 QT = PVD->getType().getCanonicalType();
146
147 if (QT.getUnqualifiedType() != C.UnsignedIntTy) {
148 DiagEngine->Report(
149 clang::FullSourceLoc(PVD->getLocation(),
150 DiagEngine->getSourceManager()),
151 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
152 "Unexpected kernel %0() parameter '%1' "
153 "of type '%2'"))
154 << FD->getName() << PVD->getName() << PVD->getType().getAsString();
155 valid = false;
156 } else {
157 llvm::StringRef ParamName = PVD->getName();
158 if (ParamName.equals("x")) {
159 if (mX) {
160 ReportNameError(DiagEngine, PVD);
161 valid = false;
162 } else if (mY) {
163 // Can't go back to X after skipping Y
164 ReportNameError(DiagEngine, PVD);
165 valid = false;
166 } else {
167 mX = PVD;
168 }
169 } else if (ParamName.equals("y")) {
170 if (mY) {
171 ReportNameError(DiagEngine, PVD);
172 valid = false;
173 } else {
174 mY = PVD;
175 }
176 } else {
177 if (!mX && !mY) {
178 mX = PVD;
179 } else if (!mY) {
180 mY = PVD;
181 } else {
182 DiagEngine->Report(
183 clang::FullSourceLoc(PVD->getLocation(),
184 DiagEngine->getSourceManager()),
185 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
186 "Unexpected kernel %0() parameter '%1' "
187 "of type '%2'"))
188 << FD->getName() << PVD->getName() << PVD->getType().getAsString();
189 valid = false;
190 }
191 }
192 }
193
194 i++;
195 }
196
197 mSignatureMetadata = 0;
198 if (valid) {
199 // Set up the bitwise metadata encoding for runtime argument passing.
200 mSignatureMetadata |= (mIn ? 0x01 : 0);
201 mSignatureMetadata |= (mOut ? 0x02 : 0);
202 mSignatureMetadata |= (mUsrData ? 0x04 : 0);
203 mSignatureMetadata |= (mX ? 0x08 : 0);
204 mSignatureMetadata |= (mY ? 0x10 : 0);
205 }
206
207 if (Context->getTargetAPI() < SLANG_ICS_TARGET_API) {
208 // APIs before ICS cannot skip between parameters. It is ok, however, for
209 // them to omit further parameters (i.e. skipping X is ok if you skip Y).
210 if (mSignatureMetadata != 0x1f && // In, Out, UsrData, X, Y
211 mSignatureMetadata != 0x0f && // In, Out, UsrData, X
212 mSignatureMetadata != 0x07 && // In, Out, UsrData
213 mSignatureMetadata != 0x03 && // In, Out
214 mSignatureMetadata != 0x01) { // In
215 DiagEngine->Report(
216 clang::FullSourceLoc(FD->getLocation(),
217 DiagEngine->getSourceManager()),
218 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
219 "Compute kernel %0() targeting SDK levels "
220 "%1-%2 may not skip parameters"))
221 << FD->getName() << SLANG_MINIMUM_TARGET_API
222 << (SLANG_ICS_TARGET_API - 1);
223 valid = false;
224 }
225 }
226
227 return valid;
228 }
229
230
validateAndConstructKernelParams(RSContext * Context,const clang::FunctionDecl * FD)231 bool RSExportForEach::validateAndConstructKernelParams(RSContext *Context,
232 const clang::FunctionDecl *FD) {
233 slangAssert(Context && FD);
234 bool valid = true;
235 clang::ASTContext &C = Context->getASTContext();
236 clang::DiagnosticsEngine *DiagEngine = Context->getDiagnostics();
237
238 if (Context->getTargetAPI() < SLANG_JB_MR1_TARGET_API) {
239 DiagEngine->Report(
240 clang::FullSourceLoc(FD->getLocation(),
241 DiagEngine->getSourceManager()),
242 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
243 "Compute kernel %0() targeting SDK levels "
244 "%1-%2 may not use pass-by-value with "
245 "__attribute__((kernel))"))
246 << FD->getName() << SLANG_MINIMUM_TARGET_API
247 << (SLANG_JB_MR1_TARGET_API - 1);
248 return false;
249 }
250
251 // Denote that we are indeed a pass-by-value kernel.
252 mKernel = true;
253
254 if (mResultType != C.VoidTy) {
255 mReturn = true;
256 }
257
258 if (mResultType->isPointerType()) {
259 DiagEngine->Report(
260 clang::FullSourceLoc(FD->getTypeSpecStartLoc(),
261 DiagEngine->getSourceManager()),
262 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
263 "Compute kernel %0() cannot return a "
264 "pointer type: '%1'"))
265 << FD->getName() << mResultType.getAsString();
266 valid = false;
267 }
268
269 // Validate remaining parameter types
270 // TODO(all): Add support for LOD/face when we have them
271
272 size_t i = 0;
273 const clang::ParmVarDecl *PVD = NULL;
274 clang::QualType QT;
275
276 if (i < numParams) {
277 PVD = FD->getParamDecl(i);
278 QT = PVD->getType().getCanonicalType();
279
280 if (QT->isPointerType()) {
281 DiagEngine->Report(
282 clang::FullSourceLoc(PVD->getLocation(),
283 DiagEngine->getSourceManager()),
284 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
285 "Compute kernel %0() cannot have "
286 "parameter '%1' of pointer type: '%2'"))
287 << FD->getName() << PVD->getName() << PVD->getType().getAsString();
288 valid = false;
289 } else if (QT.getUnqualifiedType() == C.UnsignedIntTy) {
290 // First parameter is either input or x, y (iff it is uint32_t).
291 llvm::StringRef ParamName = PVD->getName();
292 if (ParamName.equals("x")) {
293 mX = PVD;
294 } else if (ParamName.equals("y")) {
295 mY = PVD;
296 } else {
297 mIn = PVD;
298 }
299 } else {
300 mIn = PVD;
301 }
302
303 i++; // advance parameter pointer
304 }
305
306 // Check that we have at least one allocation to use for dimensions.
307 if (valid && !mIn && !mReturn) {
308 DiagEngine->Report(
309 clang::FullSourceLoc(FD->getLocation(),
310 DiagEngine->getSourceManager()),
311 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
312 "Compute kernel %0() must have at least one "
313 "input parameter or a non-void return "
314 "type")) << FD->getName();
315 valid = false;
316 }
317
318 // TODO: Abstract this block away, since it is duplicate code.
319 while (i < numParams) {
320 PVD = FD->getParamDecl(i);
321 QT = PVD->getType().getCanonicalType();
322
323 if (QT.getUnqualifiedType() != C.UnsignedIntTy) {
324 DiagEngine->Report(
325 clang::FullSourceLoc(PVD->getLocation(),
326 DiagEngine->getSourceManager()),
327 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
328 "Unexpected kernel %0() parameter '%1' "
329 "of type '%2'"))
330 << FD->getName() << PVD->getName() << PVD->getType().getAsString();
331 valid = false;
332 } else {
333 llvm::StringRef ParamName = PVD->getName();
334 if (ParamName.equals("x")) {
335 if (mX) {
336 ReportNameError(DiagEngine, PVD);
337 valid = false;
338 } else if (mY) {
339 // Can't go back to X after skipping Y
340 ReportNameError(DiagEngine, PVD);
341 valid = false;
342 } else {
343 mX = PVD;
344 }
345 } else if (ParamName.equals("y")) {
346 if (mY) {
347 ReportNameError(DiagEngine, PVD);
348 valid = false;
349 } else {
350 mY = PVD;
351 }
352 } else {
353 if (!mX && !mY) {
354 mX = PVD;
355 } else if (!mY) {
356 mY = PVD;
357 } else {
358 DiagEngine->Report(
359 clang::FullSourceLoc(PVD->getLocation(),
360 DiagEngine->getSourceManager()),
361 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
362 "Unexpected kernel %0() parameter '%1' "
363 "of type '%2'"))
364 << FD->getName() << PVD->getName() << PVD->getType().getAsString();
365 valid = false;
366 }
367 }
368 }
369
370 i++; // advance parameter pointer
371 }
372
373 mSignatureMetadata = 0;
374 if (valid) {
375 // Set up the bitwise metadata encoding for runtime argument passing.
376 mSignatureMetadata |= (mIn ? 0x01 : 0);
377 slangAssert(mOut == NULL);
378 mSignatureMetadata |= (mReturn ? 0x02 : 0);
379 slangAssert(mUsrData == NULL);
380 mSignatureMetadata |= (mUsrData ? 0x04 : 0);
381 mSignatureMetadata |= (mX ? 0x08 : 0);
382 mSignatureMetadata |= (mY ? 0x10 : 0);
383 mSignatureMetadata |= (mKernel ? 0x20 : 0); // pass-by-value
384 }
385
386 return valid;
387 }
388
389
Create(RSContext * Context,const clang::FunctionDecl * FD)390 RSExportForEach *RSExportForEach::Create(RSContext *Context,
391 const clang::FunctionDecl *FD) {
392 slangAssert(Context && FD);
393 llvm::StringRef Name = FD->getName();
394 RSExportForEach *FE;
395
396 slangAssert(!Name.empty() && "Function must have a name");
397
398 FE = new RSExportForEach(Context, Name);
399
400 if (!FE->validateAndConstructParams(Context, FD)) {
401 return NULL;
402 }
403
404 clang::ASTContext &Ctx = Context->getASTContext();
405
406 std::string Id(DUMMY_RS_TYPE_NAME_PREFIX"helper_foreach_param:");
407 Id.append(FE->getName()).append(DUMMY_RS_TYPE_NAME_POSTFIX);
408
409 // Extract the usrData parameter (if we have one)
410 if (FE->mUsrData) {
411 const clang::ParmVarDecl *PVD = FE->mUsrData;
412 clang::QualType QT = PVD->getType().getCanonicalType();
413 slangAssert(QT->isPointerType() &&
414 QT->getPointeeType().isConstQualified());
415
416 const clang::ASTContext &C = Context->getASTContext();
417 if (QT->getPointeeType().getCanonicalType().getUnqualifiedType() ==
418 C.VoidTy) {
419 // In the case of using const void*, we can't reflect an appopriate
420 // Java type, so we fall back to just reflecting the ain/aout parameters
421 FE->mUsrData = NULL;
422 } else {
423 clang::RecordDecl *RD =
424 clang::RecordDecl::Create(Ctx, clang::TTK_Struct,
425 Ctx.getTranslationUnitDecl(),
426 clang::SourceLocation(),
427 clang::SourceLocation(),
428 &Ctx.Idents.get(Id));
429
430 clang::FieldDecl *FD =
431 clang::FieldDecl::Create(Ctx,
432 RD,
433 clang::SourceLocation(),
434 clang::SourceLocation(),
435 PVD->getIdentifier(),
436 QT->getPointeeType(),
437 NULL,
438 /* BitWidth = */ NULL,
439 /* Mutable = */ false,
440 /* HasInit = */ clang::ICIS_NoInit);
441 RD->addDecl(FD);
442 RD->completeDefinition();
443
444 // Create an export type iff we have a valid usrData type
445 clang::QualType T = Ctx.getTagDeclType(RD);
446 slangAssert(!T.isNull());
447
448 RSExportType *ET = RSExportType::Create(Context, T.getTypePtr());
449
450 if (ET == NULL) {
451 fprintf(stderr, "Failed to export the function %s. There's at least "
452 "one parameter whose type is not supported by the "
453 "reflection\n", FE->getName().c_str());
454 return NULL;
455 }
456
457 slangAssert((ET->getClass() == RSExportType::ExportClassRecord) &&
458 "Parameter packet must be a record");
459
460 FE->mParamPacketType = static_cast<RSExportRecordType *>(ET);
461 }
462 }
463
464 if (FE->mIn) {
465 const clang::Type *T = FE->mIn->getType().getCanonicalType().getTypePtr();
466 FE->mInType = RSExportType::Create(Context, T);
467 if (FE->mKernel) {
468 slangAssert(FE->mInType);
469 }
470 }
471
472 if (FE->mKernel && FE->mReturn) {
473 const clang::Type *T = FE->mResultType.getTypePtr();
474 FE->mOutType = RSExportType::Create(Context, T);
475 slangAssert(FE->mOutType);
476 } else if (FE->mOut) {
477 const clang::Type *T = FE->mOut->getType().getCanonicalType().getTypePtr();
478 FE->mOutType = RSExportType::Create(Context, T);
479 }
480
481 return FE;
482 }
483
CreateDummyRoot(RSContext * Context)484 RSExportForEach *RSExportForEach::CreateDummyRoot(RSContext *Context) {
485 slangAssert(Context);
486 llvm::StringRef Name = "root";
487 RSExportForEach *FE = new RSExportForEach(Context, Name);
488 FE->mDummyRoot = true;
489 return FE;
490 }
491
isGraphicsRootRSFunc(int targetAPI,const clang::FunctionDecl * FD)492 bool RSExportForEach::isGraphicsRootRSFunc(int targetAPI,
493 const clang::FunctionDecl *FD) {
494 if (FD->hasAttr<clang::KernelAttr>()) {
495 return false;
496 }
497
498 if (!isRootRSFunc(FD)) {
499 return false;
500 }
501
502 if (FD->getNumParams() == 0) {
503 // Graphics root function
504 return true;
505 }
506
507 // Check for legacy graphics root function (with single parameter).
508 if ((targetAPI < SLANG_ICS_TARGET_API) && (FD->getNumParams() == 1)) {
509 const clang::QualType &IntType = FD->getASTContext().IntTy;
510 if (FD->getResultType().getCanonicalType() == IntType) {
511 return true;
512 }
513 }
514
515 return false;
516 }
517
isRSForEachFunc(int targetAPI,clang::DiagnosticsEngine * DiagEngine,const clang::FunctionDecl * FD)518 bool RSExportForEach::isRSForEachFunc(int targetAPI,
519 clang::DiagnosticsEngine *DiagEngine,
520 const clang::FunctionDecl *FD) {
521 slangAssert(DiagEngine && FD);
522 bool hasKernelAttr = FD->hasAttr<clang::KernelAttr>();
523
524 if (FD->getStorageClass() == clang::SC_Static) {
525 if (hasKernelAttr) {
526 DiagEngine->Report(
527 clang::FullSourceLoc(FD->getLocation(),
528 DiagEngine->getSourceManager()),
529 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
530 "Invalid use of attribute kernel with "
531 "static function declaration: %0"))
532 << FD->getName();
533 }
534 return false;
535 }
536
537 // Anything tagged as a kernel is definitely used with ForEach.
538 if (hasKernelAttr) {
539 return true;
540 }
541
542 if (isGraphicsRootRSFunc(targetAPI, FD)) {
543 return false;
544 }
545
546 // Check if first parameter is a pointer (which is required for ForEach).
547 unsigned int numParams = FD->getNumParams();
548
549 if (numParams > 0) {
550 const clang::ParmVarDecl *PVD = FD->getParamDecl(0);
551 clang::QualType QT = PVD->getType().getCanonicalType();
552
553 if (QT->isPointerType()) {
554 return true;
555 }
556
557 // Any non-graphics root() is automatically a ForEach candidate.
558 // At this point, however, we know that it is not going to be a valid
559 // compute root() function (due to not having a pointer parameter). We
560 // still want to return true here, so that we can issue appropriate
561 // diagnostics.
562 if (isRootRSFunc(FD)) {
563 return true;
564 }
565 }
566
567 return false;
568 }
569
570 bool
validateSpecialFuncDecl(int targetAPI,clang::DiagnosticsEngine * DiagEngine,clang::FunctionDecl const * FD)571 RSExportForEach::validateSpecialFuncDecl(int targetAPI,
572 clang::DiagnosticsEngine *DiagEngine,
573 clang::FunctionDecl const *FD) {
574 slangAssert(DiagEngine && FD);
575 bool valid = true;
576 const clang::ASTContext &C = FD->getASTContext();
577 const clang::QualType &IntType = FD->getASTContext().IntTy;
578
579 if (isGraphicsRootRSFunc(targetAPI, FD)) {
580 if ((targetAPI < SLANG_ICS_TARGET_API) && (FD->getNumParams() == 1)) {
581 // Legacy graphics root function
582 const clang::ParmVarDecl *PVD = FD->getParamDecl(0);
583 clang::QualType QT = PVD->getType().getCanonicalType();
584 if (QT != IntType) {
585 DiagEngine->Report(
586 clang::FullSourceLoc(PVD->getLocation(),
587 DiagEngine->getSourceManager()),
588 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
589 "invalid parameter type for legacy "
590 "graphics root() function: %0"))
591 << PVD->getType();
592 valid = false;
593 }
594 }
595
596 // Graphics root function, so verify that it returns an int
597 if (FD->getResultType().getCanonicalType() != IntType) {
598 DiagEngine->Report(
599 clang::FullSourceLoc(FD->getLocation(),
600 DiagEngine->getSourceManager()),
601 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
602 "root() is required to return "
603 "an int for graphics usage"));
604 valid = false;
605 }
606 } else if (isInitRSFunc(FD) || isDtorRSFunc(FD)) {
607 if (FD->getNumParams() != 0) {
608 DiagEngine->Report(
609 clang::FullSourceLoc(FD->getLocation(),
610 DiagEngine->getSourceManager()),
611 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
612 "%0(void) is required to have no "
613 "parameters")) << FD->getName();
614 valid = false;
615 }
616
617 if (FD->getResultType().getCanonicalType() != C.VoidTy) {
618 DiagEngine->Report(
619 clang::FullSourceLoc(FD->getLocation(),
620 DiagEngine->getSourceManager()),
621 DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
622 "%0(void) is required to have a void "
623 "return type")) << FD->getName();
624 valid = false;
625 }
626 } else {
627 slangAssert(false && "must be called on root, init or .rs.dtor function!");
628 }
629
630 return valid;
631 }
632
633 } // namespace slang
634