[SYCL] AST support for SYCL kernel entry point functions. (#122379)

A SYCL kernel entry point function is a non-member function or a static
member function declared with the `sycl_kernel_entry_point` attribute.
Such functions define a pattern for an offload kernel entry point
function to be generated to enable execution of a SYCL kernel on a
device. A SYCL library implementation orchestrates the invocation of
these functions with corresponding SYCL kernel arguments in response to
calls to SYCL kernel invocation functions specified by the SYCL 2020
specification.

The offload kernel entry point function (sometimes referred to as the
SYCL kernel caller function) is generated from the SYCL kernel entry
point function by a transformation of the function parameters followed
by a transformation of the function body to replace references to the
original parameters with references to the transformed ones. Exactly how
parameters are transformed will be explained in a future change that
implements non-trivial transformations. For now, it suffices to state
that a given parameter of the SYCL kernel entry point function may be
transformed to multiple parameters of the offload kernel entry point as
needed to satisfy offload kernel argument passing requirements.
Parameters that are decomposed in this way are reconstituted as local
variables in the body of the generated offload kernel entry point
function.

For example, given the following SYCL kernel entry point function
definition:
```
template<typename KernelNameType, typename KernelType>
[[clang::sycl_kernel_entry_point(KernelNameType)]]
void sycl_kernel_entry_point(KernelType kernel) {
  kernel();
}
```

and the following call:
```
struct Kernel {
  int dm1;
  int dm2;
  void operator()() const;
};
Kernel k;
sycl_kernel_entry_point<class kernel_name>(k);
```

the corresponding offload kernel entry point function that is generated
might look as follows (assuming `Kernel` is a type that requires
decomposition):
```
void offload_kernel_entry_point_for_kernel_name(int dm1, int dm2) {
  Kernel kernel{dm1, dm2};
  kernel();
}
```

Other details of the generated offload kernel entry point function, such
as its name and calling convention, are implementation details that need
not be reflected in the AST and may differ across target devices. For
that reason, only the transformation described above is represented in
the AST; other details will be filled in during code generation.

These transformations are represented using new AST nodes introduced
with this change. `OutlinedFunctionDecl` holds a sequence of
`ImplicitParamDecl` nodes and a sequence of statement nodes that
correspond to the transformed parameters and function body.
`SYCLKernelCallStmt` wraps the original function body and associates it
with an `OutlinedFunctionDecl` instance. For the example above, the AST
generated for the `sycl_kernel_entry_point<kernel_name>` specialization
would look as follows:
```
FunctionDecl 'sycl_kernel_entry_point<kernel_name>(Kernel)'
  TemplateArgument type 'kernel_name'
  TemplateArgument type 'Kernel'
  ParmVarDecl kernel 'Kernel'
  SYCLKernelCallStmt
    CompoundStmt
      <original statements>
    OutlinedFunctionDecl
      ImplicitParamDecl 'dm1' 'int'
      ImplicitParamDecl 'dm2' 'int'
      CompoundStmt
        VarDecl 'kernel' 'Kernel'
          <initialization of 'kernel' with 'dm1' and 'dm2'>
        <transformed statements with redirected references of 'kernel'>
```

Any ODR-use of the SYCL kernel entry point function will (with future
changes) suffice for the offload kernel entry point to be emitted. An
actual call to the SYCL kernel entry point function will result in a
call to the function. However, evaluation of a `SYCLKernelCallStmt`
statement is a no-op, so such calls will have no effect other than to
trigger emission of the offload kernel entry point.

Additionally, as a related change inspired by code review feedback,
these changes disallow use of the `sycl_kernel_entry_point` attribute
with functions defined with a _function-try-block_. The SYCL 2020
specification prohibits the use of C++ exceptions in device functions.
Even if exceptions were not prohibited, it is unclear what the semantics
would be for an exception that escapes the SYCL kernel entry point
function; the boundary between host and device code could be an implicit
noexcept boundary that results in program termination if violated, or
the exception could perhaps be propagated to host code via the SYCL
library. Pending support for C++ exceptions in device code and clear
semantics for handling them at the host-device boundary, this change
makes use of the `sycl_kernel_entry_point` attribute with a function
defined with a _function-try-block_ an error.
This commit is contained in:
Tom Honermann 2025-01-22 16:39:08 -05:00 committed by GitHub
parent 2656928d0c
commit 8fb42300a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 737 additions and 12 deletions

View File

@ -158,8 +158,8 @@ public:
ConstStmtVisitor<Derived>::Visit(S);
// Some statements have custom mechanisms for dumping their children.
if (isa<DeclStmt>(S) || isa<GenericSelectionExpr>(S) ||
isa<RequiresExpr>(S) || isa<OpenACCWaitConstruct>(S))
if (isa<DeclStmt, GenericSelectionExpr, RequiresExpr,
OpenACCWaitConstruct, SYCLKernelCallStmt>(S))
return;
if (Traversal == TK_IgnoreUnlessSpelledInSource &&
@ -585,6 +585,12 @@ public:
void VisitTopLevelStmtDecl(const TopLevelStmtDecl *D) { Visit(D->getStmt()); }
void VisitOutlinedFunctionDecl(const OutlinedFunctionDecl *D) {
for (const ImplicitParamDecl *Parameter : D->parameters())
Visit(Parameter);
Visit(D->getBody());
}
void VisitCapturedDecl(const CapturedDecl *D) { Visit(D->getBody()); }
void VisitOMPThreadPrivateDecl(const OMPThreadPrivateDecl *D) {
@ -815,6 +821,12 @@ public:
Visit(Node->getCapturedDecl());
}
void VisitSYCLKernelCallStmt(const SYCLKernelCallStmt *Node) {
Visit(Node->getOriginalStmt());
if (Traversal != TK_IgnoreUnlessSpelledInSource)
Visit(Node->getOutlinedFunctionDecl());
}
void VisitOMPExecutableDirective(const OMPExecutableDirective *Node) {
for (const auto *C : Node->clauses())
Visit(C);

View File

@ -4688,6 +4688,83 @@ public:
}
};
/// Represents a partial function definition.
///
/// An outlined function declaration contains the parameters and body of
/// a function independent of other function definition concerns such
/// as function name, type, and calling convention. Such declarations may
/// be used to hold a parameterized and transformed sequence of statements
/// used to generate a target dependent function definition without losing
/// association with the original statements. See SYCLKernelCallStmt as an
/// example.
class OutlinedFunctionDecl final
: public Decl,
public DeclContext,
private llvm::TrailingObjects<OutlinedFunctionDecl, ImplicitParamDecl *> {
private:
/// The number of parameters to the outlined function.
unsigned NumParams;
/// The body of the outlined function.
llvm::PointerIntPair<Stmt *, 1, bool> BodyAndNothrow;
explicit OutlinedFunctionDecl(DeclContext *DC, unsigned NumParams);
ImplicitParamDecl *const *getParams() const {
return getTrailingObjects<ImplicitParamDecl *>();
}
ImplicitParamDecl **getParams() {
return getTrailingObjects<ImplicitParamDecl *>();
}
public:
friend class ASTDeclReader;
friend class ASTDeclWriter;
friend TrailingObjects;
static OutlinedFunctionDecl *Create(ASTContext &C, DeclContext *DC,
unsigned NumParams);
static OutlinedFunctionDecl *
CreateDeserialized(ASTContext &C, GlobalDeclID ID, unsigned NumParams);
Stmt *getBody() const override;
void setBody(Stmt *B);
bool isNothrow() const;
void setNothrow(bool Nothrow = true);
unsigned getNumParams() const { return NumParams; }
ImplicitParamDecl *getParam(unsigned i) const {
assert(i < NumParams);
return getParams()[i];
}
void setParam(unsigned i, ImplicitParamDecl *P) {
assert(i < NumParams);
getParams()[i] = P;
}
// Range interface to parameters.
using parameter_const_iterator = const ImplicitParamDecl *const *;
using parameter_const_range = llvm::iterator_range<parameter_const_iterator>;
parameter_const_range parameters() const {
return {param_begin(), param_end()};
}
parameter_const_iterator param_begin() const { return getParams(); }
parameter_const_iterator param_end() const { return getParams() + NumParams; }
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == OutlinedFunction; }
static DeclContext *castToDeclContext(const OutlinedFunctionDecl *D) {
return static_cast<DeclContext *>(const_cast<OutlinedFunctionDecl *>(D));
}
static OutlinedFunctionDecl *castFromDeclContext(const DeclContext *DC) {
return static_cast<OutlinedFunctionDecl *>(const_cast<DeclContext *>(DC));
}
};
/// Represents the body of a CapturedStmt, and serves as its DeclContext.
class CapturedDecl final
: public Decl,

View File

@ -37,6 +37,7 @@
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtOpenACC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtSYCL.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h"
@ -1581,6 +1582,11 @@ DEF_TRAVERSE_DECL(BlockDecl, {
ShouldVisitChildren = false;
})
DEF_TRAVERSE_DECL(OutlinedFunctionDecl, {
TRY_TO(TraverseStmt(D->getBody()));
ShouldVisitChildren = false;
})
DEF_TRAVERSE_DECL(CapturedDecl, {
TRY_TO(TraverseStmt(D->getBody()));
ShouldVisitChildren = false;
@ -2904,6 +2910,14 @@ DEF_TRAVERSE_STMT(SEHFinallyStmt, {})
DEF_TRAVERSE_STMT(SEHLeaveStmt, {})
DEF_TRAVERSE_STMT(CapturedStmt, { TRY_TO(TraverseDecl(S->getCapturedDecl())); })
DEF_TRAVERSE_STMT(SYCLKernelCallStmt, {
if (getDerived().shouldVisitImplicitCode()) {
TRY_TO(TraverseStmt(S->getOriginalStmt()));
TRY_TO(TraverseDecl(S->getOutlinedFunctionDecl()));
ShouldVisitChildren = false;
}
})
DEF_TRAVERSE_STMT(CXXOperatorCallExpr, {})
DEF_TRAVERSE_STMT(CXXRewrittenBinaryOperator, {
if (!getDerived().shouldVisitImplicitCode()) {

View File

@ -0,0 +1,94 @@
//===- StmtSYCL.h - Classes for SYCL kernel calls ---------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// This file defines SYCL AST classes used to represent calls to SYCL kernels.
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_AST_STMTSYCL_H
#define LLVM_CLANG_AST_STMTSYCL_H
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Stmt.h"
#include "clang/Basic/SourceLocation.h"
namespace clang {
//===----------------------------------------------------------------------===//
// AST classes for SYCL kernel calls.
//===----------------------------------------------------------------------===//
/// SYCLKernelCallStmt represents the transformation that is applied to the body
/// of a function declared with the sycl_kernel_entry_point attribute. The body
/// of such a function specifies the statements to be executed on a SYCL device
/// to invoke a SYCL kernel with a particular set of kernel arguments. The
/// SYCLKernelCallStmt associates an original statement (the compound statement
/// that is the function body) with an OutlinedFunctionDecl that holds the
/// kernel parameters and the transformed body. During code generation, the
/// OutlinedFunctionDecl is used to emit an offload kernel entry point suitable
/// for invocation from a SYCL library implementation. If executed, the
/// SYCLKernelCallStmt behaves as a no-op; no code generation is performed for
/// it.
class SYCLKernelCallStmt : public Stmt {
friend class ASTStmtReader;
friend class ASTStmtWriter;
private:
Stmt *OriginalStmt = nullptr;
OutlinedFunctionDecl *OFDecl = nullptr;
public:
/// Construct a SYCL kernel call statement.
SYCLKernelCallStmt(CompoundStmt *CS, OutlinedFunctionDecl *OFD)
: Stmt(SYCLKernelCallStmtClass), OriginalStmt(CS), OFDecl(OFD) {}
/// Construct an empty SYCL kernel call statement.
SYCLKernelCallStmt(EmptyShell Empty) : Stmt(SYCLKernelCallStmtClass, Empty) {}
/// Retrieve the model statement.
CompoundStmt *getOriginalStmt() { return cast<CompoundStmt>(OriginalStmt); }
const CompoundStmt *getOriginalStmt() const {
return cast<CompoundStmt>(OriginalStmt);
}
void setOriginalStmt(CompoundStmt *CS) { OriginalStmt = CS; }
/// Retrieve the outlined function declaration.
OutlinedFunctionDecl *getOutlinedFunctionDecl() { return OFDecl; }
const OutlinedFunctionDecl *getOutlinedFunctionDecl() const { return OFDecl; }
/// Set the outlined function declaration.
void setOutlinedFunctionDecl(OutlinedFunctionDecl *OFD) { OFDecl = OFD; }
SourceLocation getBeginLoc() const LLVM_READONLY {
return getOriginalStmt()->getBeginLoc();
}
SourceLocation getEndLoc() const LLVM_READONLY {
return getOriginalStmt()->getEndLoc();
}
SourceRange getSourceRange() const LLVM_READONLY {
return getOriginalStmt()->getSourceRange();
}
static bool classof(const Stmt *T) {
return T->getStmtClass() == SYCLKernelCallStmtClass;
}
child_range children() {
return child_range(&OriginalStmt, &OriginalStmt + 1);
}
const_child_range children() const {
return const_child_range(&OriginalStmt, &OriginalStmt + 1);
}
};
} // end namespace clang
#endif // LLVM_CLANG_AST_STMTSYCL_H

View File

@ -22,6 +22,7 @@
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtOpenACC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtSYCL.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h"

View File

@ -487,6 +487,7 @@ following requirements.
* Is not a C variadic function.
* Is not a coroutine.
* Is not defined as deleted or as defaulted.
* Is not defined with a function try block.
* Is not declared with the ``constexpr`` or ``consteval`` specifiers.
* Is not declared with the ``[[noreturn]]`` attribute.

View File

@ -101,6 +101,7 @@ def Friend : DeclNode<Decl>;
def FriendTemplate : DeclNode<Decl>;
def StaticAssert : DeclNode<Decl>;
def Block : DeclNode<Decl, "blocks">, DeclContext;
def OutlinedFunction : DeclNode<Decl>, DeclContext;
def Captured : DeclNode<Decl>, DeclContext;
def Import : DeclNode<Decl>;
def OMPThreadPrivate : DeclNode<Decl>;

View File

@ -12457,7 +12457,8 @@ def err_sycl_entry_point_invalid : Error<
"'sycl_kernel_entry_point' attribute cannot be applied to a"
" %select{non-static member function|variadic function|deleted function|"
"defaulted function|constexpr function|consteval function|"
"function declared with the 'noreturn' attribute|coroutine}0">;
"function declared with the 'noreturn' attribute|coroutine|"
"function defined with a function try block}0">;
def err_sycl_entry_point_invalid_redeclaration : Error<
"'sycl_kernel_entry_point' kernel name argument does not match prior"
" declaration%diff{: $ vs $|}0,1">;

View File

@ -24,6 +24,7 @@ def SwitchCase : StmtNode<Stmt, 1>;
def CaseStmt : StmtNode<SwitchCase>;
def DefaultStmt : StmtNode<SwitchCase>;
def CapturedStmt : StmtNode<Stmt>;
def SYCLKernelCallStmt : StmtNode<Stmt>;
// Statements that might produce a value (for example, as the last non-null
// statement in a GNU statement-expression).

View File

@ -65,6 +65,7 @@ public:
void handleKernelEntryPointAttr(Decl *D, const ParsedAttr &AL);
void CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD);
StmtResult BuildSYCLKernelCallStmt(FunctionDecl *FD, CompoundStmt *Body);
};
} // namespace clang

View File

@ -627,7 +627,10 @@ enum class TemplateSubstitutionKind : char {
#define EMPTY(DERIVED, BASE)
#define LIFETIMEEXTENDEDTEMPORARY(DERIVED, BASE)
// Decls which use special-case instantiation code.
// Decls which never appear inside a template.
#define OUTLINEDFUNCTION(DERIVED, BASE)
// Decls which use special-case instantiation code.
#define BLOCK(DERIVED, BASE)
#define CAPTURED(DERIVED, BASE)
#define IMPLICITPARAM(DERIVED, BASE)

View File

@ -1316,6 +1316,9 @@ enum DeclCode {
/// A BlockDecl record.
DECL_BLOCK,
/// A OutlinedFunctionDecl record.
DECL_OUTLINEDFUNCTION,
/// A CapturedDecl record.
DECL_CAPTURED,
@ -1600,6 +1603,9 @@ enum StmtCode {
/// A CapturedStmt record.
STMT_CAPTURED,
/// A SYCLKernelCallStmt record.
STMT_SYCLKERNELCALL,
/// A GCC-style AsmStmt record.
STMT_GCCASM,

View File

@ -76,6 +76,7 @@
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtOpenACC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtSYCL.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h"

View File

@ -5459,6 +5459,35 @@ BlockDecl *BlockDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
return new (C, ID) BlockDecl(nullptr, SourceLocation());
}
OutlinedFunctionDecl::OutlinedFunctionDecl(DeclContext *DC, unsigned NumParams)
: Decl(OutlinedFunction, DC, SourceLocation()),
DeclContext(OutlinedFunction), NumParams(NumParams),
BodyAndNothrow(nullptr, false) {}
OutlinedFunctionDecl *OutlinedFunctionDecl::Create(ASTContext &C,
DeclContext *DC,
unsigned NumParams) {
return new (C, DC, additionalSizeToAlloc<ImplicitParamDecl *>(NumParams))
OutlinedFunctionDecl(DC, NumParams);
}
OutlinedFunctionDecl *
OutlinedFunctionDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
unsigned NumParams) {
return new (C, ID, additionalSizeToAlloc<ImplicitParamDecl *>(NumParams))
OutlinedFunctionDecl(nullptr, NumParams);
}
Stmt *OutlinedFunctionDecl::getBody() const {
return BodyAndNothrow.getPointer();
}
void OutlinedFunctionDecl::setBody(Stmt *B) { BodyAndNothrow.setPointer(B); }
bool OutlinedFunctionDecl::isNothrow() const { return BodyAndNothrow.getInt(); }
void OutlinedFunctionDecl::setNothrow(bool Nothrow) {
BodyAndNothrow.setInt(Nothrow);
}
CapturedDecl::CapturedDecl(DeclContext *DC, unsigned NumParams)
: Decl(Captured, DC, SourceLocation()), DeclContext(Captured),
NumParams(NumParams), ContextParam(0), BodyAndNothrow(nullptr, false) {}

View File

@ -966,6 +966,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
case PragmaDetectMismatch:
case Block:
case Captured:
case OutlinedFunction:
case TranslationUnit:
case ExternCContext:
case Decomposition:
@ -1245,6 +1246,8 @@ template <class T> static Decl *getNonClosureContext(T *D) {
return getNonClosureContext(BD->getParent());
if (auto *CD = dyn_cast<CapturedDecl>(D))
return getNonClosureContext(CD->getParent());
if (auto *OFD = dyn_cast<OutlinedFunctionDecl>(D))
return getNonClosureContext(OFD->getParent());
return nullptr;
}
@ -1437,6 +1440,7 @@ DeclContext *DeclContext::getPrimaryContext() {
case Decl::TopLevelStmt:
case Decl::Block:
case Decl::Captured:
case Decl::OutlinedFunction:
case Decl::OMPDeclareReduction:
case Decl::OMPDeclareMapper:
case Decl::RequiresExprBody:

View File

@ -25,6 +25,7 @@
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtOpenACC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtSYCL.h"
#include "clang/AST/Type.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/LLVM.h"

View File

@ -30,6 +30,7 @@
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtSYCL.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/Type.h"
@ -582,6 +583,10 @@ void StmtPrinter::VisitCapturedStmt(CapturedStmt *Node) {
PrintStmt(Node->getCapturedDecl()->getBody());
}
void StmtPrinter::VisitSYCLKernelCallStmt(SYCLKernelCallStmt *Node) {
PrintStmt(Node->getOutlinedFunctionDecl()->getBody());
}
void StmtPrinter::VisitObjCAtTryStmt(ObjCAtTryStmt *Node) {
Indent() << "@try";
if (auto *TS = dyn_cast<CompoundStmt>(Node->getTryBody())) {

View File

@ -392,6 +392,10 @@ void StmtProfiler::VisitCapturedStmt(const CapturedStmt *S) {
VisitStmt(S);
}
void StmtProfiler::VisitSYCLKernelCallStmt(const SYCLKernelCallStmt *S) {
VisitStmt(S);
}
void StmtProfiler::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S) {
VisitStmt(S);
}

View File

@ -97,6 +97,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
case Decl::Friend:
case Decl::FriendTemplate:
case Decl::Block:
case Decl::OutlinedFunction:
case Decl::Captured:
case Decl::UsingShadow:
case Decl::ConstructorUsingShadow:

View File

@ -114,6 +114,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) {
case Stmt::DefaultStmtClass:
case Stmt::CaseStmtClass:
case Stmt::SEHLeaveStmtClass:
case Stmt::SYCLKernelCallStmtClass:
llvm_unreachable("should have emitted these statements as simple");
#define STMT(Type, Base)
@ -528,6 +529,23 @@ bool CodeGenFunction::EmitSimpleStmt(const Stmt *S,
case Stmt::SEHLeaveStmtClass:
EmitSEHLeaveStmt(cast<SEHLeaveStmt>(*S));
break;
case Stmt::SYCLKernelCallStmtClass:
// SYCL kernel call statements are generated as wrappers around the body
// of functions declared with the sycl_kernel_entry_point attribute. Such
// functions are used to specify how a SYCL kernel (a function object) is
// to be invoked; the SYCL kernel call statement contains a transformed
// variation of the function body and is used to generate a SYCL kernel
// caller function; a function that serves as the device side entry point
// used to execute the SYCL kernel. The sycl_kernel_entry_point attributed
// function is invoked by host code in order to trigger emission of the
// device side SYCL kernel caller function and to generate metadata needed
// by SYCL run-time library implementations; the function is otherwise
// intended to have no effect. As such, the function body is not evaluated
// as part of the invocation during host compilation (and the function
// should not be called or emitted during device compilation); the SYCL
// kernel call statement is thus handled as a null statement for the
// purpose of code generation.
break;
}
return true;
}

View File

@ -28,6 +28,7 @@
#include "clang/AST/ExprOpenMP.h"
#include "clang/AST/StmtOpenACC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtSYCL.h"
#include "clang/AST/Type.h"
#include "clang/Basic/ABI.h"
#include "clang/Basic/CapturedStmt.h"

View File

@ -15966,7 +15966,8 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
CheckCoroutineWrapper(FD);
}
// Diagnose invalid SYCL kernel entry point function declarations.
// Diagnose invalid SYCL kernel entry point function declarations
// and build SYCLKernelCallStmts for valid ones.
if (FD && !FD->isInvalidDecl() && FD->hasAttr<SYCLKernelEntryPointAttr>()) {
SYCLKernelEntryPointAttr *SKEPAttr =
FD->getAttr<SYCLKernelEntryPointAttr>();
@ -15982,6 +15983,18 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
<< /*coroutine*/ 7;
SKEPAttr->setInvalidAttr();
} else if (Body && isa<CXXTryStmt>(Body)) {
Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
<< /*function defined with a function try block*/ 8;
SKEPAttr->setInvalidAttr();
}
if (Body && !FD->isTemplated() && !SKEPAttr->isInvalidAttr()) {
StmtResult SR =
SYCL().BuildSYCLKernelCallStmt(FD, cast<CompoundStmt>(Body));
if (SR.isInvalid())
return nullptr;
Body = SR.get();
}
}

View File

@ -1427,6 +1427,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
case Stmt::AttributedStmtClass:
case Stmt::BreakStmtClass:
case Stmt::CapturedStmtClass:
case Stmt::SYCLKernelCallStmtClass:
case Stmt::CaseStmtClass:
case Stmt::CompoundStmtClass:
case Stmt::ContinueStmtClass:

View File

@ -9,8 +9,10 @@
//===----------------------------------------------------------------------===//
#include "clang/Sema/SemaSYCL.h"
#include "TreeTransform.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/SYCLKernelInfo.h"
#include "clang/AST/StmtSYCL.h"
#include "clang/AST/TypeOrdering.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Sema/Attr.h"
@ -362,3 +364,93 @@ void SemaSYCL::CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD) {
}
}
}
namespace {
// The body of a function declared with the [[sycl_kernel_entry_point]]
// attribute is cloned and transformed to substitute references to the original
// function parameters with references to replacement variables that stand in
// for SYCL kernel parameters or local variables that reconstitute a decomposed
// SYCL kernel argument.
class OutlinedFunctionDeclBodyInstantiator
: public TreeTransform<OutlinedFunctionDeclBodyInstantiator> {
public:
using ParmDeclMap = llvm::DenseMap<ParmVarDecl *, VarDecl *>;
OutlinedFunctionDeclBodyInstantiator(Sema &S, ParmDeclMap &M)
: TreeTransform<OutlinedFunctionDeclBodyInstantiator>(S), SemaRef(S),
MapRef(M) {}
// A new set of AST nodes is always required.
bool AlwaysRebuild() { return true; }
// Transform ParmVarDecl references to the supplied replacement variables.
ExprResult TransformDeclRefExpr(DeclRefExpr *DRE) {
const ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl());
if (PVD) {
ParmDeclMap::iterator I = MapRef.find(PVD);
if (I != MapRef.end()) {
VarDecl *VD = I->second;
assert(SemaRef.getASTContext().hasSameUnqualifiedType(PVD->getType(),
VD->getType()));
assert(!VD->getType().isMoreQualifiedThan(PVD->getType(),
SemaRef.getASTContext()));
VD->setIsUsed();
return DeclRefExpr::Create(
SemaRef.getASTContext(), DRE->getQualifierLoc(),
DRE->getTemplateKeywordLoc(), VD, false, DRE->getNameInfo(),
DRE->getType(), DRE->getValueKind());
}
}
return DRE;
}
private:
Sema &SemaRef;
ParmDeclMap &MapRef;
};
} // unnamed namespace
StmtResult SemaSYCL::BuildSYCLKernelCallStmt(FunctionDecl *FD,
CompoundStmt *Body) {
assert(!FD->isInvalidDecl());
assert(!FD->isTemplated());
assert(FD->hasPrototype());
const auto *SKEPAttr = FD->getAttr<SYCLKernelEntryPointAttr>();
assert(SKEPAttr && "Missing sycl_kernel_entry_point attribute");
assert(!SKEPAttr->isInvalidAttr() &&
"sycl_kernel_entry_point attribute is invalid");
// Ensure that the kernel name was previously registered and that the
// stored declaration matches.
const SYCLKernelInfo &SKI =
getASTContext().getSYCLKernelInfo(SKEPAttr->getKernelName());
assert(declaresSameEntity(SKI.getKernelEntryPointDecl(), FD) &&
"SYCL kernel name conflict");
using ParmDeclMap = OutlinedFunctionDeclBodyInstantiator::ParmDeclMap;
ParmDeclMap ParmMap;
assert(SemaRef.CurContext == FD);
OutlinedFunctionDecl *OFD =
OutlinedFunctionDecl::Create(getASTContext(), FD, FD->getNumParams());
unsigned i = 0;
for (ParmVarDecl *PVD : FD->parameters()) {
ImplicitParamDecl *IPD = ImplicitParamDecl::Create(
getASTContext(), OFD, SourceLocation(), PVD->getIdentifier(),
PVD->getType(), ImplicitParamKind::Other);
OFD->setParam(i, IPD);
ParmMap[PVD] = IPD;
++i;
}
OutlinedFunctionDeclBodyInstantiator OFDBodyInstantiator(SemaRef, ParmMap);
Stmt *OFDBody = OFDBodyInstantiator.TransformStmt(Body).get();
OFD->setBody(OFDBody);
OFD->setNothrow();
Stmt *NewBody = new (getASTContext()) SYCLKernelCallStmt(Body, OFD);
return NewBody;
}

View File

@ -29,6 +29,7 @@
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtOpenACC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtSYCL.h"
#include "clang/Basic/DiagnosticParse.h"
#include "clang/Basic/OpenMPKinds.h"
#include "clang/Sema/Designator.h"
@ -17057,10 +17058,9 @@ QualType TreeTransform<Derived>::RebuildDependentSizedMatrixType(
AttributeLoc);
}
template<typename Derived>
template <typename Derived>
QualType TreeTransform<Derived>::RebuildFunctionProtoType(
QualType T,
MutableArrayRef<QualType> ParamTypes,
QualType T, MutableArrayRef<QualType> ParamTypes,
const FunctionProtoType::ExtProtoInfo &EPI) {
return SemaRef.BuildFunctionType(T, ParamTypes,
getDerived().getBaseLocation(),
@ -17416,6 +17416,16 @@ TreeTransform<Derived>::TransformCapturedStmt(CapturedStmt *S) {
return getSema().ActOnCapturedRegionEnd(Body.get());
}
template <typename Derived>
StmtResult
TreeTransform<Derived>::TransformSYCLKernelCallStmt(SYCLKernelCallStmt *S) {
// SYCLKernelCallStmt nodes are inserted upon completion of a (non-template)
// function definition or instantiation of a function template specialization
// and will therefore never appear in a dependent context.
llvm_unreachable("SYCL kernel call statement cannot appear in dependent "
"context");
}
template <typename Derived>
ExprResult TreeTransform<Derived>::TransformHLSLOutArgExpr(HLSLOutArgExpr *E) {
// We can transform the base expression and allow argument resolution to fill

View File

@ -338,6 +338,7 @@ serialization::getDefinitiveDeclContext(const DeclContext *DC) {
case Decl::CXXConversion:
case Decl::ObjCMethod:
case Decl::Block:
case Decl::OutlinedFunction:
case Decl::Captured:
// Objective C categories, category implementations, and class
// implementations can only be defined in one place.
@ -439,6 +440,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) {
case Decl::FriendTemplate:
case Decl::StaticAssert:
case Decl::Block:
case Decl::OutlinedFunction:
case Decl::Captured:
case Decl::Import:
case Decl::OMPThreadPrivate:

View File

@ -409,6 +409,7 @@ public:
void VisitFriendTemplateDecl(FriendTemplateDecl *D);
void VisitStaticAssertDecl(StaticAssertDecl *D);
void VisitBlockDecl(BlockDecl *BD);
void VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D);
void VisitCapturedDecl(CapturedDecl *CD);
void VisitEmptyDecl(EmptyDecl *D);
void VisitLifetimeExtendedTemporaryDecl(LifetimeExtendedTemporaryDecl *D);
@ -1795,6 +1796,15 @@ void ASTDeclReader::VisitBlockDecl(BlockDecl *BD) {
BD->setCaptures(Reader.getContext(), captures, capturesCXXThis);
}
void ASTDeclReader::VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D) {
// NumParams is deserialized by OutlinedFunctionDecl::CreateDeserialized().
VisitDecl(D);
for (unsigned I = 0; I < D->NumParams; ++I)
D->setParam(I, readDeclAs<ImplicitParamDecl>());
D->setNothrow(Record.readInt() != 0);
D->setBody(cast_or_null<Stmt>(Record.readStmt()));
}
void ASTDeclReader::VisitCapturedDecl(CapturedDecl *CD) {
VisitDecl(CD);
unsigned ContextParamPos = Record.readInt();
@ -4104,6 +4114,9 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
case DECL_TEMPLATE_PARAM_OBJECT:
D = TemplateParamObjectDecl::CreateDeserialized(Context, ID);
break;
case DECL_OUTLINEDFUNCTION:
D = OutlinedFunctionDecl::CreateDeserialized(Context, ID, Record.readInt());
break;
case DECL_CAPTURED:
D = CapturedDecl::CreateDeserialized(Context, ID, Record.readInt());
break;

View File

@ -33,6 +33,7 @@
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtSYCL.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/Type.h"
@ -528,6 +529,12 @@ void ASTStmtReader::VisitCapturedStmt(CapturedStmt *S) {
}
}
void ASTStmtReader::VisitSYCLKernelCallStmt(SYCLKernelCallStmt *S) {
VisitStmt(S);
S->setOriginalStmt(cast<CompoundStmt>(Record.readSubStmt()));
S->setOutlinedFunctionDecl(readDeclAs<OutlinedFunctionDecl>());
}
void ASTStmtReader::VisitExpr(Expr *E) {
VisitStmt(E);
CurrentUnpackingBits.emplace(Record.readInt());
@ -3112,6 +3119,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
Context, Record[ASTStmtReader::NumStmtFields]);
break;
case STMT_SYCLKERNELCALL:
S = new (Context) SYCLKernelCallStmt(Empty);
break;
case EXPR_CONSTANT:
S = ConstantExpr::CreateEmpty(
Context, static_cast<ConstantResultStorageKind>(

View File

@ -132,6 +132,7 @@ namespace clang {
void VisitFriendTemplateDecl(FriendTemplateDecl *D);
void VisitStaticAssertDecl(StaticAssertDecl *D);
void VisitBlockDecl(BlockDecl *D);
void VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D);
void VisitCapturedDecl(CapturedDecl *D);
void VisitEmptyDecl(EmptyDecl *D);
void VisitLifetimeExtendedTemporaryDecl(LifetimeExtendedTemporaryDecl *D);
@ -1377,6 +1378,16 @@ void ASTDeclWriter::VisitBlockDecl(BlockDecl *D) {
Code = serialization::DECL_BLOCK;
}
void ASTDeclWriter::VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D) {
Record.push_back(D->getNumParams());
VisitDecl(D);
for (unsigned I = 0; I < D->getNumParams(); ++I)
Record.AddDeclRef(D->getParam(I));
Record.push_back(D->isNothrow() ? 1 : 0);
Record.AddStmt(D->getBody());
Code = serialization::DECL_OUTLINEDFUNCTION;
}
void ASTDeclWriter::VisitCapturedDecl(CapturedDecl *CD) {
Record.push_back(CD->getNumParams());
VisitDecl(CD);

View File

@ -609,6 +609,14 @@ void ASTStmtWriter::VisitCapturedStmt(CapturedStmt *S) {
Code = serialization::STMT_CAPTURED;
}
void ASTStmtWriter::VisitSYCLKernelCallStmt(SYCLKernelCallStmt *S) {
VisitStmt(S);
Record.AddStmt(S->getOriginalStmt());
Record.AddDeclRef(S->getOutlinedFunctionDecl());
Code = serialization::STMT_SYCLKERNELCALL;
}
void ASTStmtWriter::VisitExpr(Expr *E) {
VisitStmt(E);

View File

@ -1822,6 +1822,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPParallelGenericLoopDirectiveClass:
case Stmt::OMPTargetParallelGenericLoopDirectiveClass:
case Stmt::CapturedStmtClass:
case Stmt::SYCLKernelCallStmtClass:
case Stmt::OpenACCComputeConstructClass:
case Stmt::OpenACCLoopConstructClass:
case Stmt::OpenACCCombinedConstructClass:

View File

@ -0,0 +1,275 @@
// Tests without serialization:
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown -fsycl-is-device \
// RUN: -ast-dump %s \
// RUN: | FileCheck --match-full-lines %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown -fsycl-is-host \
// RUN: -ast-dump %s \
// RUN: | FileCheck --match-full-lines %s
//
// Tests with serialization:
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown -fsycl-is-device \
// RUN: -emit-pch -o %t %s
// RUN: %clang_cc1 -x c++ -std=c++17 -triple x86_64-unknown-unknown -fsycl-is-device \
// RUN: -include-pch %t -ast-dump-all /dev/null \
// RUN: | sed -e "s/ <undeserialized declarations>//" -e "s/ imported//" \
// RUN: | FileCheck --match-full-lines %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown -fsycl-is-host \
// RUN: -emit-pch -o %t %s
// RUN: %clang_cc1 -x c++ -std=c++17 -triple x86_64-unknown-unknown -fsycl-is-host \
// RUN: -include-pch %t -ast-dump-all /dev/null \
// RUN: | sed -e "s/ <undeserialized declarations>//" -e "s/ imported//" \
// RUN: | FileCheck --match-full-lines %s
// These tests validate the AST body produced for functions declared with the
// sycl_kernel_entry_point attribute.
// CHECK: TranslationUnitDecl {{.*}}
// A unique kernel name type is required for each declared kernel entry point.
template<int> struct KN;
// A unique invocable type for use with each declared kernel entry point.
template<int> struct K {
template<typename... Ts>
void operator()(Ts...) const {}
};
[[clang::sycl_kernel_entry_point(KN<1>)]]
void skep1() {
}
// CHECK: |-FunctionDecl {{.*}} skep1 'void ()'
// CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
// CHECK-NEXT: | | `-CompoundStmt {{.*}}
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<1>
template<typename KNT, typename KT>
[[clang::sycl_kernel_entry_point(KNT)]]
void skep2(KT k) {
k();
}
template
void skep2<KN<2>>(K<2>);
// CHECK: |-FunctionTemplateDecl {{.*}} skep2
// CHECK-NEXT: | |-TemplateTypeParmDecl {{.*}} KNT
// CHECK-NEXT: | |-TemplateTypeParmDecl {{.*}} KT
// CHECK-NEXT: | |-FunctionDecl {{.*}} skep2 'void (KT)'
// CHECK-NEXT: | | |-ParmVarDecl {{.*}} k 'KT'
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | | `-CallExpr {{.*}} '<dependent type>'
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'KT' lvalue ParmVar {{.*}} 'k' 'KT'
// CHECK-NEXT: | | `-SYCLKernelEntryPointAttr {{.*}} KNT
// CHECK-NEXT: | `-FunctionDecl {{.*}} skep2 'void (K<2>)' explicit_instantiation_definition
// CHECK-NEXT: | |-TemplateArgument type 'KN<2>'
// CHECK-NEXT: | | `-RecordType {{.*}} 'KN<2>'
// CHECK-NEXT: | | `-ClassTemplateSpecialization {{.*}} 'KN'
// CHECK-NEXT: | |-TemplateArgument type 'K<2>'
// CHECK-NEXT: | | `-RecordType {{.*}} 'K<2>'
// CHECK-NEXT: | | `-ClassTemplateSpecialization {{.*}} 'K'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} k 'K<2>'
// CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
// CHECK-NEXT: | | | `-ImplicitCastExpr {{.*}} 'const K<2>' lvalue <NoOp>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'K<2>' lvalue ParmVar {{.*}} 'k' 'K<2>'
// CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used k 'K<2>'
// CHECK-NEXT: | | `-CompoundStmt {{.*}}
// CHECK-NEXT: | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
// CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'const K<2>' lvalue <NoOp>
// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'K<2>' lvalue ImplicitParam {{.*}} 'k' 'K<2>'
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<2>
template<typename KNT, typename KT>
[[clang::sycl_kernel_entry_point(KNT)]]
void skep3(KT k) {
k();
}
template<>
[[clang::sycl_kernel_entry_point(KN<3>)]]
void skep3<KN<3>>(K<3> k) {
k();
}
// CHECK: |-FunctionTemplateDecl {{.*}} skep3
// CHECK-NEXT: | |-TemplateTypeParmDecl {{.*}} KNT
// CHECK-NEXT: | |-TemplateTypeParmDecl {{.*}} KT
// CHECK-NEXT: | |-FunctionDecl {{.*}} skep3 'void (KT)'
// CHECK-NEXT: | | |-ParmVarDecl {{.*}} k 'KT'
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | | `-CallExpr {{.*}} '<dependent type>'
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'KT' lvalue ParmVar {{.*}} 'k' 'KT'
// CHECK-NEXT: | | `-SYCLKernelEntryPointAttr {{.*}} KNT
// CHECK-NEXT: | `-Function {{.*}} 'skep3' 'void (K<3>)'
// CHECK-NEXT: |-FunctionDecl {{.*}} skep3 'void (K<3>)' explicit_specialization
// CHECK-NEXT: | |-TemplateArgument type 'KN<3>'
// CHECK-NEXT: | | `-RecordType {{.*}} 'KN<3>'
// CHECK-NEXT: | | `-ClassTemplateSpecialization {{.*}} 'KN'
// CHECK-NEXT: | |-TemplateArgument type 'K<3>'
// CHECK-NEXT: | | `-RecordType {{.*}} 'K<3>'
// CHECK-NEXT: | | `-ClassTemplateSpecialization {{.*}} 'K'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} k 'K<3>'
// CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
// CHECK-NEXT: | | | `-ImplicitCastExpr {{.*}} 'const K<3>' lvalue <NoOp>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'K<3>' lvalue ParmVar {{.*}} 'k' 'K<3>'
// CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used k 'K<3>'
// CHECK-NEXT: | | `-CompoundStmt {{.*}}
// CHECK-NEXT: | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
// CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'const K<3>' lvalue <NoOp>
// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'K<3>' lvalue ImplicitParam {{.*}} 'k' 'K<3>'
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<3>
[[clang::sycl_kernel_entry_point(KN<4>)]]
void skep4(K<4> k, int p1, int p2) {
k(p1, p2);
}
// CHECK: |-FunctionDecl {{.*}} skep4 'void (K<4>, int, int)'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} k 'K<4>'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} p1 'int'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} p2 'int'
// CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'void (*)(int, int) const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'void (int, int) const' lvalue CXXMethod {{.*}} 'operator()' 'void (int, int) const'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'const K<4>' lvalue <NoOp>
// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'K<4>' lvalue ParmVar {{.*}} 'k' 'K<4>'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'p1' 'int'
// CHECK-NEXT: | | | `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'p2' 'int'
// CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used k 'K<4>'
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used p1 'int'
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used p2 'int'
// CHECK-NEXT: | | `-CompoundStmt {{.*}}
// CHECK-NEXT: | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'void (*)(int, int) const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void (int, int) const' lvalue CXXMethod {{.*}} 'operator()' 'void (int, int) const'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'const K<4>' lvalue <NoOp>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'K<4>' lvalue ImplicitParam {{.*}} 'k' 'K<4>'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'int' lvalue ImplicitParam {{.*}} 'p1' 'int'
// CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'int' lvalue ImplicitParam {{.*}} 'p2' 'int'
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<4>
[[clang::sycl_kernel_entry_point(KN<5>)]]
void skep5(int unused1, K<5> k, int unused2, int p, int unused3) {
static int slv = 0;
int lv = 4;
k(slv, 1, p, 3, lv, 5, []{ return 6; });
}
// CHECK: |-FunctionDecl {{.*}} skep5 'void (int, K<5>, int, int, int)'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} unused1 'int'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} used k 'K<5>'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} unused2 'int'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} used p 'int'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} unused3 'int'
// CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK: | | `-OutlinedFunctionDecl {{.*}}
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit unused1 'int'
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used k 'K<5>'
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit unused2 'int'
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used p 'int'
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit unused3 'int'
// CHECK-NEXT: | | `-CompoundStmt {{.*}}
// CHECK-NEXT: | | |-DeclStmt {{.*}}
// CHECK-NEXT: | | | `-VarDecl {{.*}} used slv 'int' static cinit
// CHECK-NEXT: | | | `-IntegerLiteral {{.*}} 'int' 0
// CHECK-NEXT: | | |-DeclStmt {{.*}}
// CHECK-NEXT: | | | `-VarDecl {{.*}} used lv 'int' cinit
// CHECK-NEXT: | | | `-IntegerLiteral {{.*}} 'int' 4
// CHECK-NEXT: | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'void (*)(int, int, int, int, int, int, (lambda {{.*}}) const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void (int, int, int, int, int, int, (lambda {{.*}})) const' lvalue CXXMethod {{.*}} 'operator()' 'void (int, int, int, int, int, int, (lambda {{.*}})) const'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'const K<5>' lvalue <NoOp>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'K<5>' lvalue ImplicitParam {{.*}} 'k' 'K<5>'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'slv' 'int'
// CHECK-NEXT: | | |-IntegerLiteral {{.*}} 'int' 1
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'int' lvalue ImplicitParam {{.*}} 'p' 'int'
// CHECK-NEXT: | | |-IntegerLiteral {{.*}} 'int' 3
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'lv' 'int'
// CHECK-NEXT: | | |-IntegerLiteral {{.*}} 'int' 5
// CHECK-NEXT: | | `-LambdaExpr {{.*}} '(lambda {{.*}})'
// CHECK: | `-SYCLKernelEntryPointAttr {{.*}} KN<5>
struct S6 {
void operator()() const;
};
[[clang::sycl_kernel_entry_point(KN<6>)]]
void skep6(const S6 &k) {
k();
}
// CHECK: |-FunctionDecl {{.*}} skep6 'void (const S6 &)'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} used k 'const S6 &'
// CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'const S6' lvalue ParmVar {{.*}} 'k' 'const S6 &'
// CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used k 'const S6 &'
// CHECK-NEXT: | | `-CompoundStmt {{.*}}
// CHECK-NEXT: | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'const S6' lvalue ImplicitParam {{.*}} 'k' 'const S6 &'
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<6>
// Parameter types are not required to be complete at the point of a
// non-defining declaration.
struct S7;
[[clang::sycl_kernel_entry_point(KN<7>)]]
void skep7(S7 k);
struct S7 {
void operator()() const;
};
[[clang::sycl_kernel_entry_point(KN<7>)]]
void skep7(S7 k) {
k();
}
// CHECK: |-FunctionDecl {{.*}} skep7 'void (S7)'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} k 'S7'
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<7>
// CHECK: |-FunctionDecl {{.*}} prev {{.*}} skep7 'void (S7)'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} used k 'S7'
// CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
// CHECK-NEXT: | | | `-ImplicitCastExpr {{.*}} 'const S7' lvalue <NoOp>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'S7' lvalue ParmVar {{.*}} 'k' 'S7'
// CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used k 'S7'
// CHECK-NEXT: | | `-CompoundStmt {{.*}}
// CHECK-NEXT: | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
// CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'const S7' lvalue <NoOp>
// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'S7' lvalue ImplicitParam {{.*}} 'k' 'S7'
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<7>
void the_end() {}
// CHECK: `-FunctionDecl {{.*}} the_end 'void ()'

View File

@ -143,16 +143,14 @@ void skep6() {
// CHECK: |-FunctionDecl {{.*}} skep6 'void ()'
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<6>
// CHECK-NEXT: |-FunctionDecl {{.*}} prev {{.*}} skep6 'void ()'
// CHECK-NEXT: | |-CompoundStmt {{.*}}
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<6>
// CHECK: | `-SYCLKernelEntryPointAttr {{.*}} KN<6>
// Ensure that matching attributes from the same declaration are ok.
[[clang::sycl_kernel_entry_point(KN<7>), clang::sycl_kernel_entry_point(KN<7>)]]
void skep7() {
}
// CHECK: |-FunctionDecl {{.*}} skep7 'void ()'
// CHECK-NEXT: | |-CompoundStmt {{.*}}
// CHECK-NEXT: | |-SYCLKernelEntryPointAttr {{.*}} KN<7>
// CHECK: | |-SYCLKernelEntryPointAttr {{.*}} KN<7>
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<7>
void the_end() {}

View File

@ -350,3 +350,12 @@ auto bad36 = [] [[clang::sycl_kernel_entry_point(BADKN<36>)]] static {};
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a coroutine}}
auto bad37 = [] [[clang::sycl_kernel_entry_point(BADKN<37>)]] static -> void { co_return; };
#endif
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a function defined with a function try block}}
[[clang::sycl_kernel_entry_point(BADKN<38>)]]
void bad38() try {} catch(...) {}
// expected-error@+2 {{'sycl_kernel_entry_point' attribute cannot be applied to a function defined with a function try block}}
template<typename>
[[clang::sycl_kernel_entry_point(BADKN<39>)]]
void bad39() try {} catch(...) {}

View File

@ -7202,6 +7202,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) {
case Decl::TopLevelStmt:
case Decl::StaticAssert:
case Decl::Block:
case Decl::OutlinedFunction:
case Decl::Captured:
case Decl::OMPCapturedExpr:
case Decl::Label: // FIXME: Is this right??

View File

@ -375,6 +375,10 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
K = CXCursor_UnexposedStmt;
break;
case Stmt::SYCLKernelCallStmtClass:
K = CXCursor_UnexposedStmt;
break;
case Stmt::IntegerLiteralClass:
K = CXCursor_IntegerLiteral;
break;