1//===- SemaSYCL.cpp - Semantic Analysis for SYCL constructs ---------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8// This implements Semantic Analysis for SYCL constructs.
9//===----------------------------------------------------------------------===//
10
11#include "clang/Sema/SemaSYCL.h"
12#include "TreeTransform.h"
13#include "clang/AST/Mangle.h"
14#include "clang/AST/SYCLKernelInfo.h"
15#include "clang/AST/StmtSYCL.h"
16#include "clang/AST/SubobjectVisitor.h"
17#include "clang/AST/TypeOrdering.h"
18#include "clang/Basic/Diagnostic.h"
19#include "clang/Sema/Attr.h"
20#include "clang/Sema/ParsedAttr.h"
21#include "clang/Sema/Sema.h"
22
23using namespace clang;
24
25// -----------------------------------------------------------------------------
26// SYCL device specific diagnostics implementation
27// -----------------------------------------------------------------------------
28
29SemaSYCL::SemaSYCL(Sema &S) : SemaBase(S) {}
30
31Sema::SemaDiagnosticBuilder SemaSYCL::DiagIfDeviceCode(SourceLocation Loc,
32 unsigned DiagID) {
33 assert(getLangOpts().SYCLIsDevice &&
34 "Device diagnostics Should only be issued during device compilation");
35 SemaDiagnosticBuilder::Kind DiagKind = SemaDiagnosticBuilder::K_Nop;
36 FunctionDecl *FD = SemaRef.getCurFunctionDecl(/*AllowLambda=*/true);
37 if (FD) {
38 Sema::FunctionEmissionStatus FES = SemaRef.getEmissionStatus(Decl: FD);
39 switch (FES) {
40 case Sema::FunctionEmissionStatus::Emitted:
41 DiagKind = SemaDiagnosticBuilder::K_ImmediateWithCallStack;
42 break;
43 case Sema::FunctionEmissionStatus::Unknown:
44 case Sema::FunctionEmissionStatus::TemplateDiscarded:
45 DiagKind = SemaDiagnosticBuilder::K_Deferred;
46 break;
47 case Sema::FunctionEmissionStatus::OMPDiscarded:
48 llvm_unreachable("OMPDiscarded unexpected in SYCL device compilation");
49 case Sema::FunctionEmissionStatus::CUDADiscarded:
50 llvm_unreachable("CUDADiscarded unexpected in SYCL device compilation");
51 }
52 }
53 return SemaDiagnosticBuilder(DiagKind, Loc, DiagID, FD, SemaRef);
54}
55
56static bool isZeroSizedArray(SemaSYCL &S, QualType Ty) {
57 if (const auto *CAT = S.getASTContext().getAsConstantArrayType(T: Ty))
58 return CAT->isZeroSize();
59 return false;
60}
61
62void SemaSYCL::deepTypeCheckForDevice(SourceLocation UsedAt,
63 llvm::DenseSet<QualType> Visited,
64 ValueDecl *DeclToCheck) {
65 assert(getLangOpts().SYCLIsDevice &&
66 "Should only be called during SYCL compilation");
67 // Emit notes only for the first discovered declaration of unsupported type
68 // to avoid mess of notes. This flag is to track that error already happened.
69 bool NeedToEmitNotes = true;
70
71 auto Check = [&](QualType TypeToCheck, const ValueDecl *D) {
72 bool ErrorFound = false;
73 if (isZeroSizedArray(S&: *this, Ty: TypeToCheck)) {
74 DiagIfDeviceCode(Loc: UsedAt, DiagID: diag::err_typecheck_zero_array_size) << 1;
75 ErrorFound = true;
76 }
77 // Checks for other types can also be done here.
78 if (ErrorFound) {
79 if (NeedToEmitNotes) {
80 if (auto *FD = dyn_cast<FieldDecl>(Val: D))
81 DiagIfDeviceCode(Loc: FD->getLocation(),
82 DiagID: diag::note_illegal_field_declared_here)
83 << FD->getType()->isPointerType() << FD->getType();
84 else
85 DiagIfDeviceCode(Loc: D->getLocation(), DiagID: diag::note_declared_at);
86 }
87 }
88
89 return ErrorFound;
90 };
91
92 // In case we have a Record used do the DFS for a bad field.
93 SmallVector<const ValueDecl *, 4> StackForRecursion;
94 StackForRecursion.push_back(Elt: DeclToCheck);
95
96 // While doing DFS save how we get there to emit a nice set of notes.
97 SmallVector<const FieldDecl *, 4> History;
98 History.push_back(Elt: nullptr);
99
100 do {
101 const ValueDecl *Next = StackForRecursion.pop_back_val();
102 if (!Next) {
103 assert(!History.empty());
104 // Found a marker, we have gone up a level.
105 History.pop_back();
106 continue;
107 }
108 QualType NextTy = Next->getType();
109
110 if (!Visited.insert(V: NextTy).second)
111 continue;
112
113 auto EmitHistory = [&]() {
114 // The first element is always nullptr.
115 for (uint64_t Index = 1; Index < History.size(); ++Index) {
116 DiagIfDeviceCode(Loc: History[Index]->getLocation(),
117 DiagID: diag::note_within_field_of_type)
118 << History[Index]->getType();
119 }
120 };
121
122 if (Check(NextTy, Next)) {
123 if (NeedToEmitNotes)
124 EmitHistory();
125 NeedToEmitNotes = false;
126 }
127
128 // In case pointer/array/reference type is met get pointee type, then
129 // proceed with that type.
130 while (NextTy->isAnyPointerType() || NextTy->isArrayType() ||
131 NextTy->isReferenceType()) {
132 if (NextTy->isArrayType())
133 NextTy = QualType{NextTy->getArrayElementTypeNoTypeQual(), 0};
134 else
135 NextTy = NextTy->getPointeeType();
136 if (Check(NextTy, Next)) {
137 if (NeedToEmitNotes)
138 EmitHistory();
139 NeedToEmitNotes = false;
140 }
141 }
142
143 if (const auto *RecDecl = NextTy->getAsRecordDecl()) {
144 if (auto *NextFD = dyn_cast<FieldDecl>(Val: Next))
145 History.push_back(Elt: NextFD);
146 // When nullptr is discovered, this means we've gone back up a level, so
147 // the history should be cleaned.
148 StackForRecursion.push_back(Elt: nullptr);
149 llvm::append_range(C&: StackForRecursion, R: RecDecl->fields());
150 }
151 } while (!StackForRecursion.empty());
152}
153
154ExprResult SemaSYCL::BuildUniqueStableNameExpr(SourceLocation OpLoc,
155 SourceLocation LParen,
156 SourceLocation RParen,
157 TypeSourceInfo *TSI) {
158 return SYCLUniqueStableNameExpr::Create(Ctx: getASTContext(), OpLoc, LParen,
159 RParen, TSI);
160}
161
162ExprResult SemaSYCL::ActOnUniqueStableNameExpr(SourceLocation OpLoc,
163 SourceLocation LParen,
164 SourceLocation RParen,
165 ParsedType ParsedTy) {
166 TypeSourceInfo *TSI = nullptr;
167 QualType Ty = SemaRef.GetTypeFromParser(Ty: ParsedTy, TInfo: &TSI);
168
169 if (Ty.isNull())
170 return ExprError();
171 if (!TSI)
172 TSI = getASTContext().getTrivialTypeSourceInfo(T: Ty, Loc: LParen);
173
174 return BuildUniqueStableNameExpr(OpLoc, LParen, RParen, TSI);
175}
176
177void SemaSYCL::handleKernelAttr(Decl *D, const ParsedAttr &AL) {
178 // The 'sycl_kernel' attribute applies only to function templates.
179 const auto *FD = cast<FunctionDecl>(Val: D);
180 const FunctionTemplateDecl *FT = FD->getDescribedFunctionTemplate();
181 assert(FT && "Function template is expected");
182
183 // Function template must have at least two template parameters.
184 const TemplateParameterList *TL = FT->getTemplateParameters();
185 if (TL->size() < 2) {
186 Diag(Loc: FT->getLocation(), DiagID: diag::warn_sycl_kernel_num_of_template_params);
187 return;
188 }
189
190 // Template parameters must be typenames.
191 for (unsigned I = 0; I < 2; ++I) {
192 const NamedDecl *TParam = TL->getParam(Idx: I);
193 if (isa<NonTypeTemplateParmDecl>(Val: TParam)) {
194 Diag(Loc: FT->getLocation(),
195 DiagID: diag::warn_sycl_kernel_invalid_template_param_type);
196 return;
197 }
198 }
199
200 // Function must have at least one argument.
201 if (getFunctionOrMethodNumParams(D) != 1) {
202 Diag(Loc: FT->getLocation(), DiagID: diag::warn_sycl_kernel_num_of_function_params);
203 return;
204 }
205
206 // Function must return void.
207 QualType RetTy = getFunctionOrMethodResultType(D);
208 if (!RetTy->isVoidType()) {
209 Diag(Loc: FT->getLocation(), DiagID: diag::warn_sycl_kernel_return_type);
210 return;
211 }
212
213 handleSimpleAttribute<SYCLKernelAttr>(S&: *this, D, CI: AL);
214}
215
216void SemaSYCL::handleKernelEntryPointAttr(Decl *D, const ParsedAttr &AL) {
217 ParsedType PT = AL.getTypeArg();
218 TypeSourceInfo *TSI = nullptr;
219 (void)SemaRef.GetTypeFromParser(Ty: PT, TInfo: &TSI);
220 assert(TSI && "no type source info for attribute argument");
221 D->addAttr(A: ::new (SemaRef.Context)
222 SYCLKernelEntryPointAttr(SemaRef.Context, AL, TSI));
223}
224
225void SemaSYCL::CheckDeviceUseOfDecl(NamedDecl *ND, SourceLocation Loc) {
226 assert(getLangOpts().SYCLIsDevice &&
227 "Should only be called during SYCL device compilation");
228
229 // Function declarations with the sycl_kernel_entry_point attribute cannot
230 // be ODR-used in a potentially evaluated context.
231 if (FunctionDecl *FD = dyn_cast<FunctionDecl>(Val: ND)) {
232 if (const auto *SKEPAttr = FD->getAttr<SYCLKernelEntryPointAttr>()) {
233 if (SemaRef.currentEvaluationContext().isPotentiallyEvaluated()) {
234 DiagIfDeviceCode(Loc, DiagID: diag::err_sycl_entry_point_device_use)
235 << FD << SKEPAttr;
236 DiagIfDeviceCode(Loc: SKEPAttr->getLocation(), DiagID: diag::note_attribute) << FD;
237 }
238 }
239 }
240}
241
242// Given a potentially qualified type, SourceLocationForUserDeclaredType()
243// returns the source location of the canonical declaration of the unqualified
244// desugared user declared type, if any. For non-user declared types, an
245// invalid source location is returned. The intended usage of this function
246// is to identify an appropriate source location, if any, for a
247// "entity declared here" diagnostic note.
248static SourceLocation SourceLocationForUserDeclaredType(QualType QT) {
249 SourceLocation Loc;
250 const Type *T = QT->getUnqualifiedDesugaredType();
251 if (const TagType *TT = dyn_cast<TagType>(Val: T))
252 Loc = TT->getDecl()->getLocation();
253 else if (const auto *ObjCIT = dyn_cast<ObjCInterfaceType>(Val: T))
254 Loc = ObjCIT->getDecl()->getLocation();
255 return Loc;
256}
257
258static bool CheckSYCLKernelName(Sema &S, SourceLocation Loc,
259 QualType KernelName) {
260 assert(!KernelName->isDependentType());
261
262 if (!KernelName->isStructureOrClassType()) {
263 // SYCL 2020 section 5.2, "Naming of kernels", only requires that the
264 // kernel name be a C++ typename. However, the definition of "kernel name"
265 // in the glossary states that a kernel name is a class type. Neither
266 // section explicitly states whether the kernel name type can be
267 // cv-qualified. For now, kernel name types are required to be class types
268 // and that they may be cv-qualified. The following issue requests
269 // clarification from the SYCL WG.
270 // https://github.com/KhronosGroup/SYCL-Docs/issues/568
271 S.Diag(Loc, DiagID: diag::warn_sycl_kernel_name_not_a_class_type) << KernelName;
272 SourceLocation DeclTypeLoc = SourceLocationForUserDeclaredType(QT: KernelName);
273 if (DeclTypeLoc.isValid())
274 S.Diag(Loc: DeclTypeLoc, DiagID: diag::note_entity_declared_at) << KernelName;
275 return true;
276 }
277
278 return false;
279}
280
281void SemaSYCL::CheckSYCLExternalFunctionDecl(FunctionDecl *FD) {
282 const auto *SEAttr = FD->getAttr<SYCLExternalAttr>();
283 assert(SEAttr && "Missing sycl_external attribute");
284 if (!FD->isInvalidDecl() && !FD->isTemplated()) {
285 if (!FD->isExternallyVisible())
286 if (!FD->isFunctionTemplateSpecialization() ||
287 FD->getTemplateSpecializationInfo()->isExplicitSpecialization())
288 Diag(Loc: SEAttr->getLocation(), DiagID: diag::err_sycl_external_invalid_linkage)
289 << SEAttr;
290 }
291 if (FD->isDeletedAsWritten()) {
292 Diag(Loc: SEAttr->getLocation(),
293 DiagID: diag::err_sycl_external_invalid_deleted_function)
294 << SEAttr;
295 }
296}
297
298void SemaSYCL::CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD) {
299 // Ensure that all attributes present on the declaration are consistent
300 // and warn about any redundant ones.
301 SYCLKernelEntryPointAttr *SKEPAttr = nullptr;
302 for (auto *SAI : FD->specific_attrs<SYCLKernelEntryPointAttr>()) {
303 if (!SKEPAttr) {
304 SKEPAttr = SAI;
305 continue;
306 }
307 if (!getASTContext().hasSameType(T1: SAI->getKernelName(),
308 T2: SKEPAttr->getKernelName())) {
309 Diag(Loc: SAI->getLocation(), DiagID: diag::err_sycl_entry_point_invalid_redeclaration)
310 << SKEPAttr << SAI->getKernelName() << SKEPAttr->getKernelName();
311 Diag(Loc: SKEPAttr->getLocation(), DiagID: diag::note_previous_attribute);
312 SAI->setInvalidAttr();
313 } else {
314 Diag(Loc: SAI->getLocation(),
315 DiagID: diag::warn_sycl_entry_point_redundant_declaration)
316 << SAI;
317 Diag(Loc: SKEPAttr->getLocation(), DiagID: diag::note_previous_attribute);
318 }
319 }
320 assert(SKEPAttr && "Missing sycl_kernel_entry_point attribute");
321
322 // Ensure the kernel name type is valid.
323 if (!SKEPAttr->getKernelName()->isDependentType() &&
324 CheckSYCLKernelName(S&: SemaRef, Loc: SKEPAttr->getLocation(),
325 KernelName: SKEPAttr->getKernelName()))
326 SKEPAttr->setInvalidAttr();
327
328 // Ensure that an attribute present on the previous declaration
329 // matches the one on this declaration.
330 FunctionDecl *PrevFD = FD->getPreviousDecl();
331 if (PrevFD && !PrevFD->isInvalidDecl()) {
332 const auto *PrevSKEPAttr = PrevFD->getAttr<SYCLKernelEntryPointAttr>();
333 if (PrevSKEPAttr && !PrevSKEPAttr->isInvalidAttr()) {
334 if (!getASTContext().hasSameType(T1: SKEPAttr->getKernelName(),
335 T2: PrevSKEPAttr->getKernelName())) {
336 Diag(Loc: SKEPAttr->getLocation(),
337 DiagID: diag::err_sycl_entry_point_invalid_redeclaration)
338 << SKEPAttr << SKEPAttr->getKernelName()
339 << PrevSKEPAttr->getKernelName();
340 Diag(Loc: PrevSKEPAttr->getLocation(), DiagID: diag::note_previous_decl) << PrevFD;
341 SKEPAttr->setInvalidAttr();
342 }
343 }
344 }
345
346 if (isa<CXXConstructorDecl>(Val: FD)) {
347 Diag(Loc: SKEPAttr->getLocation(), DiagID: diag::err_sycl_entry_point_invalid)
348 << SKEPAttr << diag::InvalidSKEPReason::Constructor;
349 SKEPAttr->setInvalidAttr();
350 }
351 if (isa<CXXDestructorDecl>(Val: FD)) {
352 Diag(Loc: SKEPAttr->getLocation(), DiagID: diag::err_sycl_entry_point_invalid)
353 << SKEPAttr << diag::InvalidSKEPReason::Destructor;
354 SKEPAttr->setInvalidAttr();
355 }
356 if (const auto *MD = dyn_cast<CXXMethodDecl>(Val: FD)) {
357 if (MD->isExplicitObjectMemberFunction()) {
358 Diag(Loc: SKEPAttr->getLocation(), DiagID: diag::err_sycl_entry_point_invalid)
359 << SKEPAttr << diag::InvalidSKEPReason::ExplicitObjectFn;
360 SKEPAttr->setInvalidAttr();
361 }
362 }
363
364 if (FD->isVariadic()) {
365 Diag(Loc: SKEPAttr->getLocation(), DiagID: diag::err_sycl_entry_point_invalid)
366 << SKEPAttr << diag::InvalidSKEPReason::VariadicFn;
367 SKEPAttr->setInvalidAttr();
368 }
369
370 if (FD->isDefaulted()) {
371 Diag(Loc: SKEPAttr->getLocation(), DiagID: diag::err_sycl_entry_point_invalid)
372 << SKEPAttr << diag::InvalidSKEPReason::DefaultedFn;
373 SKEPAttr->setInvalidAttr();
374 } else if (FD->isDeleted()) {
375 Diag(Loc: SKEPAttr->getLocation(), DiagID: diag::err_sycl_entry_point_invalid)
376 << SKEPAttr << diag::InvalidSKEPReason::DeletedFn;
377 SKEPAttr->setInvalidAttr();
378 }
379
380 if (FD->isConsteval()) {
381 Diag(Loc: SKEPAttr->getLocation(), DiagID: diag::err_sycl_entry_point_invalid)
382 << SKEPAttr << diag::InvalidSKEPReason::ConstevalFn;
383 SKEPAttr->setInvalidAttr();
384 } else if (FD->isConstexpr()) {
385 Diag(Loc: SKEPAttr->getLocation(), DiagID: diag::err_sycl_entry_point_invalid)
386 << SKEPAttr << diag::InvalidSKEPReason::ConstexprFn;
387 SKEPAttr->setInvalidAttr();
388 }
389
390 if (FD->isNoReturn()) {
391 Diag(Loc: SKEPAttr->getLocation(), DiagID: diag::err_sycl_entry_point_invalid)
392 << SKEPAttr << diag::InvalidSKEPReason::NoreturnFn;
393 SKEPAttr->setInvalidAttr();
394 }
395
396 if (FD->getReturnType()->isUndeducedType()) {
397 Diag(Loc: SKEPAttr->getLocation(),
398 DiagID: diag::err_sycl_entry_point_deduced_return_type)
399 << SKEPAttr;
400 SKEPAttr->setInvalidAttr();
401 } else if (!FD->getReturnType()->isDependentType() &&
402 !FD->getReturnType()->isVoidType()) {
403 Diag(Loc: SKEPAttr->getLocation(), DiagID: diag::err_sycl_entry_point_return_type)
404 << SKEPAttr;
405 SKEPAttr->setInvalidAttr();
406 }
407
408 if (!FD->isInvalidDecl() && !FD->isTemplated() &&
409 !SKEPAttr->isInvalidAttr()) {
410 const SYCLKernelInfo *SKI =
411 getASTContext().findSYCLKernelInfo(T: SKEPAttr->getKernelName());
412 if (SKI) {
413 if (!declaresSameEntity(D1: FD, D2: SKI->getKernelEntryPointDecl())) {
414 // FIXME: This diagnostic should include the origin of the kernel
415 // FIXME: names; not just the locations of the conflicting declarations.
416 Diag(Loc: FD->getLocation(), DiagID: diag::err_sycl_kernel_name_conflict)
417 << SKEPAttr;
418 Diag(Loc: SKI->getKernelEntryPointDecl()->getLocation(),
419 DiagID: diag::note_previous_declaration);
420 SKEPAttr->setInvalidAttr();
421 }
422 } else {
423 getASTContext().registerSYCLEntryPointFunction(FD);
424 }
425 }
426}
427
428ExprResult SemaSYCL::BuildSYCLKernelLaunchIdExpr(FunctionDecl *FD,
429 QualType KNT) {
430 // The current context must be the function definition context to ensure
431 // that name lookup is performed within the correct scope.
432 assert(SemaRef.CurContext == FD && "The current declaration context does not "
433 "match the requested function context");
434
435 // An appropriate source location is required to emit diagnostics if
436 // lookup fails to produce an overload set. The desired location is the
437 // start of the function body, but that is not yet available since the
438 // body of the function has not yet been set when this function is called.
439 // The general location of the function is used instead.
440 SourceLocation Loc = FD->getLocation();
441
442 ASTContext &Ctx = SemaRef.getASTContext();
443 IdentifierInfo &SYCLKernelLaunchID =
444 Ctx.Idents.get(Name: "sycl_kernel_launch", TokenCode: tok::TokenKind::identifier);
445
446 // Establish a code synthesis context for the implicit name lookup of
447 // a template named 'sycl_kernel_launch'. In the event of an error, this
448 // ensures an appropriate diagnostic note is issued to explain why the
449 // lookup was performed.
450 Sema::CodeSynthesisContext CSC;
451 CSC.Kind = Sema::CodeSynthesisContext::SYCLKernelLaunchLookup;
452 CSC.Entity = FD;
453 Sema::ScopedCodeSynthesisContext ScopedCSC(SemaRef, CSC);
454
455 // Perform ordinary name lookup for a function or variable template that
456 // accepts a single type template argument.
457 LookupResult Result(SemaRef, &SYCLKernelLaunchID, Loc,
458 Sema::LookupOrdinaryName);
459 CXXScopeSpec EmptySS;
460 if (SemaRef.LookupTemplateName(R&: Result, S: SemaRef.getCurScope(), SS&: EmptySS,
461 /*ObjectType*/ QualType(),
462 /*EnteringContext*/ false,
463 RequiredTemplate: Sema::TemplateNameIsRequired))
464 return ExprError();
465 if (Result.isAmbiguous())
466 return ExprError();
467
468 TemplateArgumentListInfo TALI{Loc, Loc};
469 TemplateArgument KNTA = TemplateArgument(KNT);
470 TemplateArgumentLoc TAL =
471 SemaRef.getTrivialTemplateArgumentLoc(Arg: KNTA, NTTPType: QualType(), Loc);
472 TALI.addArgument(Loc: TAL);
473
474 ExprResult IdExpr;
475 if (SemaRef.isPotentialImplicitMemberAccess(SS: EmptySS, R&: Result,
476 /*IsAddressOfOperand*/ false)) {
477 // The lookup result allows for a possible implicit member access that
478 // would require an implicit or explicit 'this' argument.
479 IdExpr = SemaRef.BuildPossibleImplicitMemberExpr(
480 SS: EmptySS, TemplateKWLoc: SourceLocation(), R&: Result, TemplateArgs: &TALI, S: SemaRef.getCurScope());
481 } else {
482 IdExpr = SemaRef.BuildTemplateIdExpr(SS: EmptySS, TemplateKWLoc: SourceLocation(), R&: Result,
483 /*RequiresADL*/ true, TemplateArgs: &TALI);
484 }
485
486 // The resulting expression may be invalid if, for example, 'FD' is a
487 // non-static member function and sycl_kernel_launch lookup selects a
488 // member function (which would require a 'this' argument which is
489 // not available).
490 if (IdExpr.isInvalid())
491 return ExprError();
492
493 return IdExpr;
494}
495
496namespace {
497
498// Constructs the arguments to be passed for the SYCL kernel launch call.
499// The first argument is a string literal that contains the SYCL kernel
500// name. The remaining arguments are the parameters of 'FD' passed as
501// move-elligible xvalues. Returns true on error and false otherwise.
502bool BuildSYCLKernelLaunchCallArgs(Sema &SemaRef, FunctionDecl *FD,
503 const SYCLKernelInfo *SKI,
504 SmallVectorImpl<Expr *> &Args,
505 SourceLocation Loc) {
506 // The current context must be the function definition context to ensure
507 // that parameter references occur within the correct scope.
508 assert(SemaRef.CurContext == FD && "The current declaration context does not "
509 "match the requested function context");
510
511 // Prepare a string literal that contains the kernel name.
512 ASTContext &Ctx = SemaRef.getASTContext();
513 const std::string &KernelName = SKI->GetKernelName();
514 QualType KernelNameCharTy = Ctx.CharTy.withConst();
515 llvm::APInt KernelNameSize(Ctx.getTypeSize(T: Ctx.getSizeType()),
516 KernelName.size() + 1);
517 QualType KernelNameArrayTy = Ctx.getConstantArrayType(
518 EltTy: KernelNameCharTy, ArySize: KernelNameSize, SizeExpr: nullptr, ASM: ArraySizeModifier::Normal, IndexTypeQuals: 0);
519 Expr *KernelNameExpr =
520 StringLiteral::Create(Ctx, Str: KernelName, Kind: StringLiteralKind::Ordinary,
521 /*Pascal*/ false, Ty: KernelNameArrayTy, Locs: Loc);
522 Args.push_back(Elt: KernelNameExpr);
523
524 // Forward all parameters of 'FD' to the SYCL kernel launch function as if
525 // by std::move().
526 for (ParmVarDecl *PVD : FD->parameters()) {
527 QualType ParamType = PVD->getOriginalType().getNonReferenceType();
528 ExprResult E = SemaRef.BuildDeclRefExpr(D: PVD, Ty: ParamType, VK: VK_LValue, Loc);
529 if (E.isInvalid())
530 return true;
531 if (!PVD->getType()->isLValueReferenceType())
532 E = ImplicitCastExpr::Create(Context: SemaRef.Context, T: E.get()->getType(), Kind: CK_NoOp,
533 Operand: E.get(), BasePath: nullptr, Cat: VK_XValue,
534 FPO: FPOptionsOverride());
535 if (E.isInvalid())
536 return true;
537 Args.push_back(Elt: E.get());
538 }
539
540 return false;
541}
542
543// Constructs the SYCL kernel launch call.
544StmtResult BuildSYCLKernelLaunchCallStmt(Sema &SemaRef, FunctionDecl *FD,
545 const SYCLKernelInfo *SKI,
546 Expr *IdExpr, SourceLocation Loc) {
547 SmallVector<Stmt *> Stmts;
548 // IdExpr may be null if name lookup failed.
549 if (IdExpr) {
550 llvm::SmallVector<Expr *, 12> Args;
551
552 // Establish a code synthesis context for construction of the arguments
553 // for the implicit call to 'sycl_kernel_launch'.
554 {
555 Sema::CodeSynthesisContext CSC;
556 CSC.Kind = Sema::CodeSynthesisContext::SYCLKernelLaunchLookup;
557 CSC.Entity = FD;
558 Sema::ScopedCodeSynthesisContext ScopedCSC(SemaRef, CSC);
559
560 if (BuildSYCLKernelLaunchCallArgs(SemaRef, FD, SKI, Args, Loc))
561 return StmtError();
562 }
563
564 // Establish a code synthesis context for the implicit call to
565 // 'sycl_kernel_launch'.
566 {
567 Sema::CodeSynthesisContext CSC;
568 CSC.Kind = Sema::CodeSynthesisContext::SYCLKernelLaunchOverloadResolution;
569 CSC.Entity = FD;
570 CSC.CallArgs = Args.data();
571 CSC.NumCallArgs = Args.size();
572 Sema::ScopedCodeSynthesisContext ScopedCSC(SemaRef, CSC);
573
574 ExprResult LaunchResult =
575 SemaRef.BuildCallExpr(S: SemaRef.getCurScope(), Fn: IdExpr, LParenLoc: Loc, ArgExprs: Args, RParenLoc: Loc);
576 if (LaunchResult.isInvalid())
577 return StmtError();
578
579 Stmts.push_back(Elt: SemaRef.MaybeCreateExprWithCleanups(SubExpr: LaunchResult).get());
580 }
581 }
582
583 return CompoundStmt::Create(C: SemaRef.getASTContext(), Stmts,
584 FPFeatures: FPOptionsOverride(), LB: Loc, RB: Loc);
585}
586
587// The body of a function declared with the [[sycl_kernel_entry_point]]
588// attribute is cloned and transformed to substitute references to the original
589// function parameters with references to replacement variables that stand in
590// for SYCL kernel parameters or local variables that reconstitute a decomposed
591// SYCL kernel argument.
592class OutlinedFunctionDeclBodyInstantiator
593 : public TreeTransform<OutlinedFunctionDeclBodyInstantiator> {
594public:
595 using ParmDeclMap = llvm::DenseMap<ParmVarDecl *, VarDecl *>;
596
597 OutlinedFunctionDeclBodyInstantiator(Sema &S, ParmDeclMap &M,
598 FunctionDecl *FD)
599 : TreeTransform<OutlinedFunctionDeclBodyInstantiator>(S), SemaRef(S),
600 MapRef(M), FD(FD) {}
601
602 // A new set of AST nodes is always required.
603 bool AlwaysRebuild() { return true; }
604
605 // Transform ParmVarDecl references to the supplied replacement variables.
606 ExprResult TransformDeclRefExpr(DeclRefExpr *DRE) {
607 const ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(Val: DRE->getDecl());
608 if (PVD) {
609 ParmDeclMap::iterator I = MapRef.find(Val: PVD);
610 if (I != MapRef.end()) {
611 VarDecl *VD = I->second;
612 assert(SemaRef.getASTContext().hasSameUnqualifiedType(
613 PVD->getType().getNonReferenceType(), VD->getType()));
614 assert(!VD->getType().isMoreQualifiedThan(
615 PVD->getType().getNonReferenceType(), SemaRef.getASTContext()));
616 VD->setIsUsed();
617 return DeclRefExpr::Create(
618 Context: SemaRef.getASTContext(), QualifierLoc: DRE->getQualifierLoc(),
619 TemplateKWLoc: DRE->getTemplateKeywordLoc(), D: VD, RefersToEnclosingVariableOrCapture: false, NameInfo: DRE->getNameInfo(),
620 T: DRE->getType(), VK: DRE->getValueKind());
621 }
622 }
623 return DRE;
624 }
625
626 // Diagnose CXXThisExpr in a potentially evaluated expression.
627 ExprResult TransformCXXThisExpr(CXXThisExpr *CTE) {
628 if (SemaRef.currentEvaluationContext().isPotentiallyEvaluated()) {
629 SemaRef.Diag(Loc: CTE->getExprLoc(), DiagID: diag::err_sycl_entry_point_invalid_this)
630 << (CTE->isImplicitCXXThis() ? /* implicit */ 1 : /* empty */ 0)
631 << FD->getAttr<SYCLKernelEntryPointAttr>();
632 }
633 return CTE;
634 }
635
636private:
637 Sema &SemaRef;
638 ParmDeclMap &MapRef;
639 FunctionDecl *FD;
640};
641
642OutlinedFunctionDecl *BuildSYCLKernelEntryPointOutline(Sema &SemaRef,
643 FunctionDecl *FD,
644 CompoundStmt *Body) {
645 using ParmDeclMap = OutlinedFunctionDeclBodyInstantiator::ParmDeclMap;
646 ParmDeclMap ParmMap;
647
648 OutlinedFunctionDecl *OFD = OutlinedFunctionDecl::Create(
649 C&: SemaRef.getASTContext(), DC: FD, NumParams: FD->getNumParams());
650 unsigned i = 0;
651 for (ParmVarDecl *PVD : FD->parameters()) {
652 ImplicitParamDecl *IPD = ImplicitParamDecl::Create(
653 C&: SemaRef.getASTContext(), DC: OFD, IdLoc: SourceLocation(), Id: PVD->getIdentifier(),
654 T: PVD->getType().getNonReferenceType(), ParamKind: ImplicitParamKind::Other);
655 OFD->setParam(i, P: IPD);
656 ParmMap[PVD] = IPD;
657 ++i;
658 }
659
660 OutlinedFunctionDeclBodyInstantiator OFDBodyInstantiator(SemaRef, ParmMap,
661 FD);
662 Stmt *OFDBody = OFDBodyInstantiator.TransformStmt(S: Body).get();
663 OFD->setBody(OFDBody);
664 OFD->setNothrow();
665
666 return OFD;
667}
668
669class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
670 SemaSYCL &SemaSYCLRef;
671 bool IsValid = true;
672 using ObjectAccess =
673 llvm::PointerUnion<const ParmVarDecl *, const CXXBaseSpecifier *,
674 const FieldDecl *>;
675 SmallVector<ObjectAccess, 4> ObjectAccessPath;
676
677 void emitObjectAccessPathNotes() {
678 for (auto Parent : llvm::reverse(C&: ObjectAccessPath)) {
679 if (auto *FD = Parent.dyn_cast<const FieldDecl *>()) {
680 const CXXRecordDecl *ParentRD = cast<CXXRecordDecl>(Val: FD->getParent());
681 if (ParentRD->isLambda()) {
682 SemaSYCLRef.Diag(Loc: ParentRD->getLocation(), DiagID: diag::note_within_capture)
683 << ParentRD->getCapture(I: FD->getFieldIndex())->getCapturedVar();
684 } else {
685 SemaSYCLRef.Diag(Loc: ParentRD->getLocation(),
686 DiagID: diag::note_within_field_of_type)
687 << ParentRD;
688 }
689 } else if (auto *BS = Parent.dyn_cast<const CXXBaseSpecifier *>()) {
690 CXXRecordDecl *RD = BS->getType()->getAsCXXRecordDecl();
691 assert(RD);
692 SemaSYCLRef.Diag(Loc: BS->getBeginLoc(), DiagID: diag::note_within_base_of_type)
693 << RD;
694 } else {
695 auto *Param = cast<const ParmVarDecl *>(Val&: Parent);
696 SemaSYCLRef.Diag(Loc: Param->getBeginLoc(), DiagID: diag::note_within_param_of_type)
697 << Param << Param->getType();
698 }
699 }
700 }
701
702public:
703 KernelParamsChecker(SemaSYCL &SR, SourceLocation Loc)
704 : ConstSubobjectVisitor<KernelParamsChecker>(SR.getASTContext()),
705 SemaSYCLRef(SR) {}
706
707 void checkParameter(const ParmVarDecl *PVD) {
708 ObjectAccessPath.push_back(Elt: PVD);
709 // Check the immediate type of the parameter.
710 if (checkType(Ty: PVD->getType())) {
711 // If type checking wasn't short circuited, visit subobjects to check
712 // them.
713 visit(QT: PVD->getType());
714 }
715 ObjectAccessPath.pop_back();
716 assert(ObjectAccessPath.empty());
717 }
718
719 bool visitBaseSpecifierPre(const CXXBaseSpecifier *BS) {
720 ObjectAccessPath.push_back(Elt: BS);
721 return checkType(Ty: BS->getType());
722 }
723
724 bool visitFieldDeclPre(const FieldDecl *FD) {
725 ObjectAccessPath.push_back(Elt: FD);
726 return checkType(Ty: FD->getType());
727 }
728
729 // Returns true if subobjects should be visited and false otherwise.
730 bool checkType(QualType Ty) {
731 if (Ty->isReferenceType()) {
732 auto DirectParent = ObjectAccessPath.back();
733 // Reference cannot be a base, so just assume we came via a FieldDecl.
734 if (isa<const ParmVarDecl *>(Val: DirectParent)) {
735 // If reference is a kernel parameter, there is nothing to do. We allow
736 // references in direct kernel parameters for better performance of the
737 // host code and we eliminate them when building actual kernel.
738 return true;
739 }
740
741 auto *DirectFieldParent = cast<const FieldDecl *>(Val&: DirectParent);
742 SemaSYCLRef.Diag(Loc: DirectFieldParent->getLocation(),
743 DiagID: diag::err_bad_kernel_param_type)
744 << DirectFieldParent->getType();
745 emitObjectAccessPathNotes();
746
747 // Don't visit the type of the reference since any further invalid
748 // kernel parameter types contained within the referenced type
749 // might not be relevant once the programmer addresses the
750 // invalid use of a reference.
751 IsValid = false;
752 return false;
753 }
754 return true;
755 }
756
757 void visitFieldDeclPost(const FieldDecl *FD) { ObjectAccessPath.pop_back(); }
758 void visitBaseSpecifierPost(const CXXBaseSpecifier *BS) {
759 ObjectAccessPath.pop_back();
760 }
761
762 bool isInvalid() { return !IsValid; }
763};
764
765bool verifyKernelParams(FunctionDecl *FD, SemaSYCL &SemaSYCLRef) {
766 KernelParamsChecker KAC(SemaSYCLRef, FD->getLocation());
767 for (auto Param : FD->parameters())
768 KAC.checkParameter(PVD: Param);
769 return KAC.isInvalid();
770}
771
772} // unnamed namespace
773
774StmtResult SemaSYCL::BuildSYCLKernelCallStmt(FunctionDecl *FD,
775 CompoundStmt *Body,
776 Expr *LaunchIdExpr) {
777 assert(!FD->isInvalidDecl());
778 assert(!FD->isTemplated());
779 assert(FD->hasPrototype());
780 // The current context must be the function definition context to ensure
781 // that name lookup and parameter and local variable creation are performed
782 // within the correct scope.
783 assert(SemaRef.CurContext == FD && "The current declaration context does not "
784 "match the requested function context");
785
786 const auto *SKEPAttr = FD->getAttr<SYCLKernelEntryPointAttr>();
787 assert(SKEPAttr && "Missing sycl_kernel_entry_point attribute");
788 assert(!SKEPAttr->isInvalidAttr() &&
789 "sycl_kernel_entry_point attribute is invalid");
790
791 // Ensure that the kernel name was previously registered and that the
792 // stored declaration matches.
793 const SYCLKernelInfo &SKI =
794 getASTContext().getSYCLKernelInfo(T: SKEPAttr->getKernelName());
795 assert(declaresSameEntity(SKI.getKernelEntryPointDecl(), FD) &&
796 "SYCL kernel name conflict");
797 if (verifyKernelParams(FD, SemaSYCLRef&: *this))
798 return StmtError();
799
800 // Build the outline of the synthesized device entry point function.
801 OutlinedFunctionDecl *OFD =
802 BuildSYCLKernelEntryPointOutline(SemaRef, FD, Body);
803 assert(OFD);
804
805 // Build the host kernel launch statement. An appropriate source location
806 // is required to emit diagnostics.
807 SourceLocation Loc = Body->getLBracLoc();
808 StmtResult LaunchResult =
809 BuildSYCLKernelLaunchCallStmt(SemaRef, FD, SKI: &SKI, IdExpr: LaunchIdExpr, Loc);
810 if (LaunchResult.isInvalid())
811 return StmtError();
812
813 Stmt *NewBody =
814 new (getASTContext()) SYCLKernelCallStmt(Body, LaunchResult.get(), OFD);
815
816 return NewBody;
817}
818
819StmtResult SemaSYCL::BuildUnresolvedSYCLKernelCallStmt(CompoundStmt *Body,
820 Expr *LaunchIdExpr) {
821 return UnresolvedSYCLKernelCallStmt::Create(C: SemaRef.getASTContext(), CS: Body,
822 IdExpr: LaunchIdExpr);
823}
824