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