Reimplement __builtin_unique_stable_name-

The original version of this was reverted, and @rjmcall provided some
advice to architect a new solution.  This is that solution.

This implements a builtin to provide a unique name that is stable across
compilations of this TU for the purposes of implementing the library
component of the unnamed kernel feature of SYCL.  It does this by
running the Itanium mangler with a few modifications.

Because it is somewhat common to wrap non-kernel-related lambdas in
macros that aren't present on the device (such as for logging), this
uniquely generates an ID for all lambdas involved in the naming of a
kernel. It uses the lambda-mangling number to do this, except replaces
this with its own number (starting at 10000 for readabililty reasons)
for lambdas used to name a kernel.

Additionally, this implements itself as constexpr with a slight catch:
if a name would be invalidated by the use of this lambda in a later
kernel invocation, it is diagnosed as an error (see the Sema tests).

Differential Revision: https://reviews.llvm.org/D103112
This commit is contained in:
Erich Keane 2021-04-23 08:22:35 -07:00
parent caf86d2959
commit eba69b59d1
44 changed files with 1117 additions and 38 deletions

View File

@ -1794,7 +1794,7 @@ correctly in any circumstances. It can be used if:
metaprogramming algorithms to be able to specify/detect types generically.
- the generated kernel binary does not contain indirect calls because they
are eliminated using compiler optimizations e.g. devirtualization.
are eliminated using compiler optimizations e.g. devirtualization.
- the selected target supports the function pointer like functionality e.g.
most CPU targets.
@ -2404,6 +2404,40 @@ argument.
int *pb =__builtin_preserve_access_index(&v->c[3].b);
__builtin_preserve_access_index(v->j);
``__builtin_sycl_unique_stable_name``
-------------------------------------
``__builtin_sycl_unique_stable_name()`` is a builtin that takes a type and
produces a string literal containing a unique name for the type that is stable
across split compilations, mainly to support SYCL/Data Parallel C++ language.
In cases where the split compilation needs to share a unique token for a type
across the boundary (such as in an offloading situation), this name can be used
for lookup purposes, such as in the SYCL Integration Header.
The value of this builtin is computed entirely at compile time, so it can be
used in constant expressions. This value encodes lambda functions based on a
stable numbering order in which they appear in their local declaration contexts.
Once this builtin is evaluated in a constexpr context, it is erroneous to use
it in an instantiation which changes its value.
In order to produce the unique name, the current implementation of the bultin
uses Itanium mangling even if the host compilation uses a different name
mangling scheme at runtime. The mangler marks all the lambdas required to name
the SYCL kernel and emits a stable local ordering of the respective lambdas,
starting from ``10000``. The initial value of ``10000`` serves as an obvious
differentiator from ordinary lambda mangling numbers but does not serve any
other purpose and may change in the future. The resulting pattern is
demanglable. When non-lambda types are passed to the builtin, the mangler emits
their usual pattern without any special treatment.
**Syntax**:
.. code-block:: c
// Computes a unique stable name for the given type.
constexpr const char * __builtin_sycl_unique_stable_name( type-id );
Multiprecision Arithmetic Builtins
----------------------------------
@ -2598,7 +2632,7 @@ Guaranteed inlined copy
``__builtin_memcpy_inline`` has been designed as a building block for efficient
``memcpy`` implementations. It is identical to ``__builtin_memcpy`` but also
guarantees not to call any external functions. See LLVM IR `llvm.memcpy.inline
<https://llvm.org/docs/LangRef.html#llvm-memcpy-inline-intrinsic>`_ intrinsic
<https://llvm.org/docs/LangRef.html#llvm-memcpy-inline-intrinsic>`_ intrinsic
for more information.
This is useful to implement a custom version of ``memcpy``, implement a

View File

@ -2355,6 +2355,12 @@ public:
/// If \p T is null pointer, assume the target in ASTContext.
MangleContext *createMangleContext(const TargetInfo *T = nullptr);
/// Creates a device mangle context to correctly mangle lambdas in a mixed
/// architecture compile by setting the lambda mangling number source to the
/// DeviceLambdaManglingNumber. Currently this asserts that the TargetInfo
/// (from the AuxTargetInfo) is a an itanium target.
MangleContext *createDeviceMangleContext(const TargetInfo &T);
void DeepCollectObjCIvars(const ObjCInterfaceDecl *OI, bool leafClass,
SmallVectorImpl<const ObjCIvarDecl*> &Ivars) const;
@ -3158,10 +3164,27 @@ public:
StringRef getCUIDHash() const;
void AddSYCLKernelNamingDecl(const CXXRecordDecl *RD);
bool IsSYCLKernelNamingDecl(const NamedDecl *RD) const;
unsigned GetSYCLKernelNamingIndex(const NamedDecl *RD) const;
/// A SourceLocation to store whether we have evaluated a kernel name already,
/// and where it happened. If so, we need to diagnose an illegal use of the
/// builtin.
llvm::MapVector<const SYCLUniqueStableNameExpr *, std::string>
SYCLUniqueStableNameEvaluatedValues;
private:
/// All OMPTraitInfo objects live in this collection, one per
/// `pragma omp [begin] declare variant` directive.
SmallVector<std::unique_ptr<OMPTraitInfo>, 4> OMPTraitInfoVector;
/// A list of the (right now just lambda decls) declarations required to
/// name all the SYCL kernels in the translation unit, so that we can get the
/// correct kernel name, as well as implement
/// __builtin_sycl_unique_stable_name.
llvm::DenseMap<const DeclContext *,
llvm::SmallPtrSet<const CXXRecordDecl *, 4>>
SYCLKernelNamingTypes;
};
/// Insertion operator for diagnostics.

View File

@ -78,6 +78,7 @@ class MaterializeTemporaryExpr;
class CXXFoldExpr;
class TypeTraitExpr;
class ConceptSpecializationExpr;
class SYCLUniqueStableNameExpr;
class PredefinedExpr;
class CallExpr;
class OffsetOfExpr;
@ -165,6 +166,7 @@ ExprDependence computeDependence(TypeTraitExpr *E);
ExprDependence computeDependence(ConceptSpecializationExpr *E,
bool ValueDependent);
ExprDependence computeDependence(SYCLUniqueStableNameExpr *E);
ExprDependence computeDependence(PredefinedExpr *E);
ExprDependence computeDependence(CallExpr *E, llvm::ArrayRef<Expr *> PreArgs);
ExprDependence computeDependence(OffsetOfExpr *E);

View File

@ -2039,6 +2039,64 @@ public:
}
};
// This represents a use of the __builtin_sycl_unique_stable_name, which takes a
// type-id, and at CodeGen time emits a unique string representation of the
// type in a way that permits us to properly encode information about the SYCL
// kernels.
class SYCLUniqueStableNameExpr final : public Expr {
friend class ASTStmtReader;
SourceLocation OpLoc, LParen, RParen;
TypeSourceInfo *TypeInfo;
SYCLUniqueStableNameExpr(EmptyShell Empty, QualType ResultTy);
SYCLUniqueStableNameExpr(SourceLocation OpLoc, SourceLocation LParen,
SourceLocation RParen, QualType ResultTy,
TypeSourceInfo *TSI);
void setTypeSourceInfo(TypeSourceInfo *Ty) { TypeInfo = Ty; }
void setLocation(SourceLocation L) { OpLoc = L; }
void setLParenLocation(SourceLocation L) { LParen = L; }
void setRParenLocation(SourceLocation L) { RParen = L; }
public:
TypeSourceInfo *getTypeSourceInfo() { return TypeInfo; }
const TypeSourceInfo *getTypeSourceInfo() const { return TypeInfo; }
static SYCLUniqueStableNameExpr *
Create(const ASTContext &Ctx, SourceLocation OpLoc, SourceLocation LParen,
SourceLocation RParen, TypeSourceInfo *TSI);
static SYCLUniqueStableNameExpr *CreateEmpty(const ASTContext &Ctx);
SourceLocation getBeginLoc() const { return getLocation(); }
SourceLocation getEndLoc() const { return RParen; }
SourceLocation getLocation() const { return OpLoc; }
SourceLocation getLParenLocation() const { return LParen; }
SourceLocation getRParenLocation() const { return RParen; }
static bool classof(const Stmt *T) {
return T->getStmtClass() == SYCLUniqueStableNameExprClass;
}
// Iterators
child_range children() {
return child_range(child_iterator(), child_iterator());
}
const_child_range children() const {
return const_child_range(const_child_iterator(), const_child_iterator());
}
// Convenience function to generate the name of the currently stored type.
std::string ComputeName(ASTContext &Context) const;
// Get the generated name of the type. Note that this only works after all
// kernels have been instantiated.
static std::string ComputeName(ASTContext &Context, QualType Ty);
};
/// ParenExpr - This represents a parethesized expression, e.g. "(1)". This
/// AST node is only formed if full location information is requested.
class ParenExpr : public Expr {

View File

@ -263,6 +263,7 @@ public:
void VisitBlockDecl(const BlockDecl *D);
void VisitDeclRefExpr(const DeclRefExpr *DRE);
void VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *E);
void VisitPredefinedExpr(const PredefinedExpr *PE);
void VisitUnaryOperator(const UnaryOperator *UO);
void VisitBinaryOperator(const BinaryOperator *BO);

View File

@ -107,9 +107,6 @@ public:
virtual bool shouldMangleCXXName(const NamedDecl *D) = 0;
virtual bool shouldMangleStringLiteral(const StringLiteral *SL) = 0;
virtual bool isDeviceMangleContext() const { return false; }
virtual void setDeviceMangleContext(bool) {}
virtual bool isUniqueInternalLinkageDecl(const NamedDecl *ND) {
return false;
}
@ -173,6 +170,8 @@ public:
class ItaniumMangleContext : public MangleContext {
public:
using DiscriminatorOverrideTy =
llvm::Optional<unsigned> (*)(ASTContext &, const NamedDecl *);
explicit ItaniumMangleContext(ASTContext &C, DiagnosticsEngine &D)
: MangleContext(C, D, MK_Itanium) {}
@ -195,12 +194,18 @@ public:
virtual void mangleDynamicStermFinalizer(const VarDecl *D, raw_ostream &) = 0;
// This has to live here, otherwise the CXXNameMangler won't have access to
// it.
virtual DiscriminatorOverrideTy getDiscriminatorOverride() const = 0;
static bool classof(const MangleContext *C) {
return C->getKind() == MK_Itanium;
}
static ItaniumMangleContext *create(ASTContext &Context,
DiagnosticsEngine &Diags);
static ItaniumMangleContext *create(ASTContext &Context,
DiagnosticsEngine &Diags,
DiscriminatorOverrideTy Discriminator);
};
class MicrosoftMangleContext : public MangleContext {

View File

@ -2651,6 +2651,9 @@ DEF_TRAVERSE_STMT(ObjCBridgedCastExpr, {
DEF_TRAVERSE_STMT(ObjCAvailabilityCheckExpr, {})
DEF_TRAVERSE_STMT(ParenExpr, {})
DEF_TRAVERSE_STMT(ParenListExpr, {})
DEF_TRAVERSE_STMT(SYCLUniqueStableNameExpr, {
TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
})
DEF_TRAVERSE_STMT(PredefinedExpr, {})
DEF_TRAVERSE_STMT(ShuffleVectorExpr, {})
DEF_TRAVERSE_STMT(ConvertVectorExpr, {})

View File

@ -249,6 +249,7 @@ public:
void VisitCastExpr(const CastExpr *Node);
void VisitImplicitCastExpr(const ImplicitCastExpr *Node);
void VisitDeclRefExpr(const DeclRefExpr *Node);
void VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *Node);
void VisitPredefinedExpr(const PredefinedExpr *Node);
void VisitCharacterLiteral(const CharacterLiteral *Node);
void VisitIntegerLiteral(const IntegerLiteral *Node);

View File

@ -6352,6 +6352,11 @@ def warn_pointer_arith_null_ptr : Warning<
def warn_gnu_null_ptr_arith : Warning<
"arithmetic on a null pointer treated as a cast from integer to pointer is a GNU extension">,
InGroup<NullPointerArithmetic>, DefaultIgnore;
def err_kernel_invalidates_sycl_unique_stable_name
: Error<"kernel instantiation changes the result of an evaluated "
"'__builtin_sycl_unique_stable_name'">;
def note_sycl_unique_stable_name_evaluated_here
: Note<"'__builtin_sycl_unique_stable_name' evaluated here">;
def warn_floatingpoint_eq : Warning<
"comparing floating point with == or != is unsafe">,

View File

@ -444,6 +444,8 @@ public:
bool hasWasmExceptions() const {
return getExceptionHandling() == ExceptionHandlingKind::Wasm;
}
bool isSYCL() const { return SYCLIsDevice || SYCLIsHost; }
};
/// Floating point control options

View File

@ -57,6 +57,7 @@ def CoreturnStmt : StmtNode<Stmt>;
// Expressions
def Expr : StmtNode<ValueStmt, 1>;
def PredefinedExpr : StmtNode<Expr>;
def SYCLUniqueStableNameExpr : StmtNode<Expr>;
def DeclRefExpr : StmtNode<Expr>;
def IntegerLiteral : StmtNode<Expr>;
def FixedPointLiteral : StmtNode<Expr>;

View File

@ -696,11 +696,12 @@ ALIAS("_declspec" , __declspec , KEYMS)
ALIAS("_pascal" , __pascal , KEYBORLAND)
// Clang Extensions.
KEYWORD(__builtin_convertvector , KEYALL)
ALIAS("__char16_t" , char16_t , KEYCXX)
ALIAS("__char32_t" , char32_t , KEYCXX)
KEYWORD(__builtin_bit_cast , KEYALL)
KEYWORD(__builtin_available , KEYALL)
KEYWORD(__builtin_convertvector , KEYALL)
ALIAS("__char16_t" , char16_t , KEYCXX)
ALIAS("__char32_t" , char32_t , KEYCXX)
KEYWORD(__builtin_bit_cast , KEYALL)
KEYWORD(__builtin_available , KEYALL)
KEYWORD(__builtin_sycl_unique_stable_name, KEYSYCL)
// Clang-specific keywords enabled only in testing.
TESTING_KEYWORD(__unknown_anytype , KEYALL)

View File

@ -1800,6 +1800,7 @@ private:
ExprResult ParsePostfixExpressionSuffix(ExprResult LHS);
ExprResult ParseUnaryExprOrTypeTraitExpression();
ExprResult ParseBuiltinPrimaryExpression();
ExprResult ParseSYCLUniqueStableNameExpression();
ExprResult ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
bool &isCastExpr,

View File

@ -911,6 +911,10 @@ public:
OpaqueParser = P;
}
// Does the work necessary to deal with a SYCL kernel lambda. At the moment,
// this just marks the list of lambdas required to name the kernel.
void AddSYCLKernelLambda(const FunctionDecl *FD);
class DelayedDiagnostics;
class DelayedDiagnosticsState {
@ -5190,6 +5194,15 @@ public:
ExprResult ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind);
ExprResult ActOnIntegerConstant(SourceLocation Loc, uint64_t Val);
ExprResult BuildSYCLUniqueStableNameExpr(SourceLocation OpLoc,
SourceLocation LParen,
SourceLocation RParen,
TypeSourceInfo *TSI);
ExprResult ActOnSYCLUniqueStableNameExpr(SourceLocation OpLoc,
SourceLocation LParen,
SourceLocation RParen,
ParsedType ParsedTy);
bool CheckLoopHintExpr(Expr *E, SourceLocation Loc);
ExprResult ActOnNumericConstant(const Token &Tok, Scope *UDLScope = nullptr);

View File

@ -1960,6 +1960,9 @@ enum StmtCode {
// FixedPointLiteral
EXPR_FIXEDPOINT_LITERAL,
// SYCLUniqueStableNameExpr
EXPR_SYCL_UNIQUE_STABLE_NAME,
};
/// The kinds of designators that can occur in a

View File

@ -2458,7 +2458,7 @@ unsigned ASTContext::getPreferredTypeAlign(const Type *T) const {
// The preferred alignment of member pointers is that of a pointer.
if (T->isMemberPointerType())
return getPreferredTypeAlign(getPointerDiffType().getTypePtr());
if (!Target->allowsLargerPreferedTypeAlignment())
return ABIAlign;
@ -11075,6 +11075,33 @@ MangleContext *ASTContext::createMangleContext(const TargetInfo *T) {
llvm_unreachable("Unsupported ABI");
}
MangleContext *ASTContext::createDeviceMangleContext(const TargetInfo &T) {
assert(T.getCXXABI().getKind() != TargetCXXABI::Microsoft &&
"Device mangle context does not support Microsoft mangling.");
switch (T.getCXXABI().getKind()) {
case TargetCXXABI::AppleARM64:
case TargetCXXABI::Fuchsia:
case TargetCXXABI::GenericAArch64:
case TargetCXXABI::GenericItanium:
case TargetCXXABI::GenericARM:
case TargetCXXABI::GenericMIPS:
case TargetCXXABI::iOS:
case TargetCXXABI::WebAssembly:
case TargetCXXABI::WatchOS:
case TargetCXXABI::XL:
return ItaniumMangleContext::create(
*this, getDiagnostics(),
[](ASTContext &, const NamedDecl *ND) -> llvm::Optional<unsigned> {
if (const auto *RD = dyn_cast<CXXRecordDecl>(ND))
return RD->getDeviceLambdaManglingNumber();
return llvm::None;
});
case TargetCXXABI::Microsoft:
return MicrosoftMangleContext::create(*this, getDiagnostics());
}
llvm_unreachable("Unsupported ABI");
}
CXXABI::~CXXABI() = default;
size_t ASTContext::getSideTableAllocatedMemory() const {
@ -11648,3 +11675,89 @@ StringRef ASTContext::getCUIDHash() const {
CUIDHash = llvm::utohexstr(llvm::MD5Hash(LangOpts.CUID), /*LowerCase=*/true);
return CUIDHash;
}
// Get the closest named parent, so we can order the sycl naming decls somewhere
// that mangling is meaningful.
static const DeclContext *GetNamedParent(const CXXRecordDecl *RD) {
const DeclContext *DC = RD->getDeclContext();
while (!isa<NamedDecl, TranslationUnitDecl>(DC))
DC = DC->getParent();
return DC;
}
void ASTContext::AddSYCLKernelNamingDecl(const CXXRecordDecl *RD) {
assert(getLangOpts().isSYCL() && "Only valid for SYCL programs");
RD = RD->getCanonicalDecl();
const DeclContext *DC = GetNamedParent(RD);
assert(RD->getLocation().isValid() &&
"Invalid location on kernel naming decl");
(void)SYCLKernelNamingTypes[DC].insert(RD);
}
bool ASTContext::IsSYCLKernelNamingDecl(const NamedDecl *ND) const {
assert(getLangOpts().isSYCL() && "Only valid for SYCL programs");
const auto *RD = dyn_cast<CXXRecordDecl>(ND);
if (!RD)
return false;
RD = RD->getCanonicalDecl();
const DeclContext *DC = GetNamedParent(RD);
auto Itr = SYCLKernelNamingTypes.find(DC);
if (Itr == SYCLKernelNamingTypes.end())
return false;
return Itr->getSecond().count(RD);
}
// Filters the Decls list to those that share the lambda mangling with the
// passed RD.
static void FilterSYCLKernelNamingDecls(
ASTContext &Ctx, const CXXRecordDecl *RD,
llvm::SmallVectorImpl<const CXXRecordDecl *> &Decls) {
static std::unique_ptr<ItaniumMangleContext> Mangler{
ItaniumMangleContext::create(Ctx, Ctx.getDiagnostics())};
llvm::SmallString<128> LambdaSig;
llvm::raw_svector_ostream Out(LambdaSig);
Mangler->mangleLambdaSig(RD, Out);
llvm::erase_if(Decls, [&LambdaSig](const CXXRecordDecl *LocalRD) {
llvm::SmallString<128> LocalLambdaSig;
llvm::raw_svector_ostream LocalOut(LocalLambdaSig);
Mangler->mangleLambdaSig(LocalRD, LocalOut);
return LambdaSig != LocalLambdaSig;
});
}
unsigned ASTContext::GetSYCLKernelNamingIndex(const NamedDecl *ND) const {
assert(getLangOpts().isSYCL() && "Only valid for SYCL programs");
assert(IsSYCLKernelNamingDecl(ND) &&
"Lambda not involved in mangling asked for a naming index?");
const CXXRecordDecl *RD = cast<CXXRecordDecl>(ND)->getCanonicalDecl();
const DeclContext *DC = GetNamedParent(RD);
auto Itr = SYCLKernelNamingTypes.find(DC);
assert(Itr != SYCLKernelNamingTypes.end() && "Not a valid DeclContext?");
const llvm::SmallPtrSet<const CXXRecordDecl *, 4> &Set = Itr->getSecond();
llvm::SmallVector<const CXXRecordDecl *> Decls{Set.begin(), Set.end()};
// If we are in an itanium situation, the mangling-numbers for a lambda depend
// on the mangled signature, so sort by that. Only TargetCXXABI::Microsoft
// doesn't use the itanium mangler, and just sets the lambda mangling number
// incrementally, with no consideration to the signature.
if (Target->getCXXABI().getKind() != TargetCXXABI::Microsoft)
FilterSYCLKernelNamingDecls(const_cast<ASTContext &>(*this), RD, Decls);
llvm::sort(Decls, [](const CXXRecordDecl *LHS, const CXXRecordDecl *RHS) {
return LHS->getLambdaManglingNumber() < RHS->getLambdaManglingNumber();
});
return llvm::find(Decls, RD) - Decls.begin();
}

View File

@ -556,6 +556,10 @@ ExprDependence clang::computeDependence(RecoveryExpr *E) {
return D;
}
ExprDependence clang::computeDependence(SYCLUniqueStableNameExpr *E) {
return toExprDependence(E->getTypeSourceInfo()->getType()->getDependence());
}
ExprDependence clang::computeDependence(PredefinedExpr *E) {
return toExprDependence(E->getType()->getDependence()) &
~ExprDependence::UnexpandedPack;

View File

@ -504,6 +504,70 @@ SourceLocation DeclRefExpr::getEndLoc() const {
return getNameInfo().getEndLoc();
}
SYCLUniqueStableNameExpr::SYCLUniqueStableNameExpr(SourceLocation OpLoc,
SourceLocation LParen,
SourceLocation RParen,
QualType ResultTy,
TypeSourceInfo *TSI)
: Expr(SYCLUniqueStableNameExprClass, ResultTy, VK_RValue, OK_Ordinary),
OpLoc(OpLoc), LParen(LParen), RParen(RParen) {
setTypeSourceInfo(TSI);
setDependence(computeDependence(this));
}
SYCLUniqueStableNameExpr::SYCLUniqueStableNameExpr(EmptyShell Empty,
QualType ResultTy)
: Expr(SYCLUniqueStableNameExprClass, ResultTy, VK_RValue, OK_Ordinary) {}
SYCLUniqueStableNameExpr *
SYCLUniqueStableNameExpr::Create(const ASTContext &Ctx, SourceLocation OpLoc,
SourceLocation LParen, SourceLocation RParen,
TypeSourceInfo *TSI) {
QualType ResultTy = Ctx.getPointerType(Ctx.CharTy.withConst());
return new (Ctx)
SYCLUniqueStableNameExpr(OpLoc, LParen, RParen, ResultTy, TSI);
}
SYCLUniqueStableNameExpr *
SYCLUniqueStableNameExpr::CreateEmpty(const ASTContext &Ctx) {
QualType ResultTy = Ctx.getPointerType(Ctx.CharTy.withConst());
return new (Ctx) SYCLUniqueStableNameExpr(EmptyShell(), ResultTy);
}
std::string SYCLUniqueStableNameExpr::ComputeName(ASTContext &Context) const {
return SYCLUniqueStableNameExpr::ComputeName(Context,
getTypeSourceInfo()->getType());
}
std::string SYCLUniqueStableNameExpr::ComputeName(ASTContext &Context,
QualType Ty) {
auto MangleCallback = [](ASTContext &Ctx,
const NamedDecl *ND) -> llvm::Optional<unsigned> {
// This replaces the 'lambda number' in the mangling with a unique number
// based on its order in the declaration. To provide some level of visual
// notability (actual uniqueness from normal lambdas isn't necessary, as
// these are used differently), we add 10,000 to the number.
// For example:
// _ZTSZ3foovEUlvE10005_
// Demangles to: typeinfo name for foo()::'lambda10005'()
// Note that the mangler subtracts 2, since with normal lambdas the lambda
// mangling number '0' is an anonymous struct mangle, and '1' is omitted.
// So 10,002 results in the first number being 10,000.
if (Ctx.IsSYCLKernelNamingDecl(ND))
return 10'002 + Ctx.GetSYCLKernelNamingIndex(ND);
return llvm::None;
};
std::unique_ptr<MangleContext> Ctx{ItaniumMangleContext::create(
Context, Context.getDiagnostics(), MangleCallback)};
std::string Buffer;
Buffer.reserve(128);
llvm::raw_string_ostream Out(Buffer);
Ctx->mangleTypeName(Ty, Out);
return Out.str();
}
PredefinedExpr::PredefinedExpr(SourceLocation L, QualType FNTy, IdentKind IK,
StringLiteral *SL)
: Expr(PredefinedExprClass, FNTy, VK_LValue, OK_Ordinary) {
@ -3381,6 +3445,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
case SourceLocExprClass:
case ConceptSpecializationExprClass:
case RequiresExprClass:
case SYCLUniqueStableNameExprClass:
// These never have a side-effect.
return false;

View File

@ -433,6 +433,9 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
case Expr::CoawaitExprClass:
case Expr::CoyieldExprClass:
return ClassifyInternal(Ctx, cast<CoroutineSuspendExpr>(E)->getResumeExpr());
case Expr::SYCLUniqueStableNameExprClass:
return Cl::CL_PRValue;
break;
}
llvm_unreachable("unhandled expression kind in classification");

View File

@ -8669,6 +8669,26 @@ public:
return true;
}
bool VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *E) {
std::string ResultStr = E->ComputeName(Info.Ctx);
Info.Ctx.SYCLUniqueStableNameEvaluatedValues[E] = ResultStr;
QualType CharTy = Info.Ctx.CharTy.withConst();
APInt Size(Info.Ctx.getTypeSize(Info.Ctx.getSizeType()),
ResultStr.size() + 1);
QualType ArrayTy = Info.Ctx.getConstantArrayType(CharTy, Size, nullptr,
ArrayType::Normal, 0);
StringLiteral *SL =
StringLiteral::Create(Info.Ctx, ResultStr, StringLiteral::Ascii,
/*Pascal*/ false, ArrayTy, E->getLocation());
evaluateLValue(SL, Result);
Result.addArray(Info, E, cast<ConstantArrayType>(ArrayTy));
return true;
}
// FIXME: Missing: @protocol, @selector
};
} // end anonymous namespace
@ -10367,7 +10387,8 @@ namespace {
Result = APValue(APValue::UninitArray(), 0,
CAT->getSize().getZExtValue());
if (!Result.hasArrayFiller()) return true;
if (!Result.hasArrayFiller())
return true;
// Zero-initialize all elements.
LValue Subobject = This;
@ -15154,6 +15175,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
case Expr::CoawaitExprClass:
case Expr::DependentCoawaitExprClass:
case Expr::CoyieldExprClass:
case Expr::SYCLUniqueStableNameExprClass:
return ICEDiag(IK_NotICE, E->getBeginLoc());
case Expr::InitListExprClass: {

View File

@ -125,14 +125,16 @@ class ItaniumMangleContextImpl : public ItaniumMangleContext {
typedef std::pair<const DeclContext*, IdentifierInfo*> DiscriminatorKeyTy;
llvm::DenseMap<DiscriminatorKeyTy, unsigned> Discriminator;
llvm::DenseMap<const NamedDecl*, unsigned> Uniquifier;
const DiscriminatorOverrideTy DiscriminatorOverride = nullptr;
bool IsDevCtx = false;
bool NeedsUniqueInternalLinkageNames = false;
public:
explicit ItaniumMangleContextImpl(ASTContext &Context,
DiagnosticsEngine &Diags)
: ItaniumMangleContext(Context, Diags) {}
explicit ItaniumMangleContextImpl(
ASTContext &Context, DiagnosticsEngine &Diags,
DiscriminatorOverrideTy DiscriminatorOverride)
: ItaniumMangleContext(Context, Diags),
DiscriminatorOverride(DiscriminatorOverride) {}
/// @name Mangler Entry Points
/// @{
@ -147,9 +149,6 @@ public:
NeedsUniqueInternalLinkageNames = true;
}
bool isDeviceMangleContext() const override { return IsDevCtx; }
void setDeviceMangleContext(bool IsDev) override { IsDevCtx = IsDev; }
void mangleCXXName(GlobalDecl GD, raw_ostream &) override;
void mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk,
raw_ostream &) override;
@ -246,6 +245,10 @@ public:
return Name;
}
DiscriminatorOverrideTy getDiscriminatorOverride() const override {
return DiscriminatorOverride;
}
/// @}
};
@ -1515,7 +1518,9 @@ void CXXNameMangler::mangleUnqualifiedName(GlobalDecl GD,
// <lambda-sig> ::= <template-param-decl>* <parameter-type>+
// # Parameter types or 'v' for 'void'.
if (const CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(TD)) {
if (Record->isLambda() && Record->getLambdaManglingNumber()) {
if (Record->isLambda() && (Record->getLambdaManglingNumber() ||
Context.getDiscriminatorOverride()(
Context.getASTContext(), Record))) {
assert(!AdditionalAbiTags &&
"Lambda type cannot have additional abi tags");
mangleLambda(Record);
@ -1953,9 +1958,11 @@ void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) {
// if the host-side CXX ABI has different numbering for lambda. In such case,
// if the mangle context is that device-side one, use the device-side lambda
// mangling number for this lambda.
unsigned Number = Context.isDeviceMangleContext()
? Lambda->getDeviceLambdaManglingNumber()
: Lambda->getLambdaManglingNumber();
llvm::Optional<unsigned> DeviceNumber =
Context.getDiscriminatorOverride()(Context.getASTContext(), Lambda);
unsigned Number = DeviceNumber.hasValue() ? *DeviceNumber
: Lambda->getLambdaManglingNumber();
assert(Number > 0 && "Lambda should be mangled as an unnamed class");
if (Number > 1)
mangleNumber(Number - 2);
@ -5026,6 +5033,16 @@ recurse:
Out << "v18co_yield";
mangleExpression(cast<CoawaitExpr>(E)->getOperand());
break;
case Expr::SYCLUniqueStableNameExprClass: {
const auto *USN = cast<SYCLUniqueStableNameExpr>(E);
NotPrimaryExpr();
Out << "u33__builtin_sycl_unique_stable_name";
mangleType(USN->getTypeSourceInfo()->getType());
Out << "E";
break;
}
}
if (AsTemplateArg && !IsPrimaryExpr)
@ -6378,7 +6395,17 @@ void ItaniumMangleContextImpl::mangleLambdaSig(const CXXRecordDecl *Lambda,
Mangler.mangleLambdaSig(Lambda);
}
ItaniumMangleContext *
ItaniumMangleContext::create(ASTContext &Context, DiagnosticsEngine &Diags) {
return new ItaniumMangleContextImpl(Context, Diags);
ItaniumMangleContext *ItaniumMangleContext::create(ASTContext &Context,
DiagnosticsEngine &Diags) {
return new ItaniumMangleContextImpl(
Context, Diags,
[](ASTContext &, const NamedDecl *) -> llvm::Optional<unsigned> {
return llvm::None;
});
}
ItaniumMangleContext *
ItaniumMangleContext::create(ASTContext &Context, DiagnosticsEngine &Diags,
DiscriminatorOverrideTy DiscriminatorOverride) {
return new ItaniumMangleContextImpl(Context, Diags, DiscriminatorOverride);
}

View File

@ -1164,6 +1164,12 @@ void JSONNodeDumper::VisitDeclRefExpr(const DeclRefExpr *DRE) {
}
}
void JSONNodeDumper::VisitSYCLUniqueStableNameExpr(
const SYCLUniqueStableNameExpr *E) {
JOS.attribute("typeSourceInfo",
createQualType(E->getTypeSourceInfo()->getType()));
}
void JSONNodeDumper::VisitPredefinedExpr(const PredefinedExpr *PE) {
JOS.attribute("name", PredefinedExpr::getIdentKindName(PE->getIdentKind()));
}

View File

@ -1081,6 +1081,13 @@ void StmtPrinter::VisitObjCSubscriptRefExpr(ObjCSubscriptRefExpr *Node) {
OS << "]";
}
void StmtPrinter::VisitSYCLUniqueStableNameExpr(
SYCLUniqueStableNameExpr *Node) {
OS << "__builtin_sycl_unique_stable_name(";
Node->getTypeSourceInfo()->getType().print(OS, Policy);
OS << ")";
}
void StmtPrinter::VisitPredefinedExpr(PredefinedExpr *Node) {
OS << PredefinedExpr::getIdentKindName(Node->getIdentKind());
}

View File

@ -1190,6 +1190,12 @@ void StmtProfiler::VisitDeclRefExpr(const DeclRefExpr *S) {
}
}
void StmtProfiler::VisitSYCLUniqueStableNameExpr(
const SYCLUniqueStableNameExpr *S) {
VisitExpr(S);
VisitType(S->getTypeSourceInfo()->getType());
}
void StmtProfiler::VisitPredefinedExpr(const PredefinedExpr *S) {
VisitExpr(S);
ID.AddInteger(S->getIdentKind());

View File

@ -1018,6 +1018,11 @@ void TextNodeDumper::VisitObjCIvarRefExpr(const ObjCIvarRefExpr *Node) {
OS << " isFreeIvar";
}
void TextNodeDumper::VisitSYCLUniqueStableNameExpr(
const SYCLUniqueStableNameExpr *Node) {
dumpType(Node->getTypeSourceInfo()->getType());
}
void TextNodeDumper::VisitPredefinedExpr(const PredefinedExpr *Node) {
OS << " " << PredefinedExpr::getIdentKindName(Node->getIdentKind());
}

View File

@ -107,8 +107,9 @@ namespace {
KEYCXX20 = 0x200000,
KEYOPENCLCXX = 0x400000,
KEYMSCOMPAT = 0x800000,
KEYSYCL = 0x1000000,
KEYALLCXX = KEYCXX | KEYCXX11 | KEYCXX20,
KEYALL = (0xffffff & ~KEYNOMS18 &
KEYALL = (0xfffffff & ~KEYNOMS18 &
~KEYNOOPENCL) // KEYNOMS18 and KEYNOOPENCL are used to exclude.
};
@ -155,6 +156,8 @@ static KeywordStatus getKeywordStatus(const LangOptions &LangOpts,
if (LangOpts.CPlusPlus && (Flags & KEYALLCXX)) return KS_Future;
if (LangOpts.CPlusPlus && !LangOpts.CPlusPlus20 && (Flags & CHAR8SUPPORT))
return KS_Future;
if (LangOpts.isSYCL() && (Flags & KEYSYCL))
return KS_Enabled;
return KS_Disabled;
}

View File

@ -191,12 +191,27 @@ CGNVCUDARuntime::addUnderscoredPrefixToName(StringRef FuncName) const {
return ((Twine("__cuda") + Twine(FuncName)).str());
}
static std::unique_ptr<MangleContext> InitDeviceMC(CodeGenModule &CGM) {
// If the host and device have different C++ ABIs, mark it as the device
// mangle context so that the mangling needs to retrieve the additional
// device lambda mangling number instead of the regular host one.
if (CGM.getContext().getAuxTargetInfo() &&
CGM.getContext().getTargetInfo().getCXXABI().isMicrosoft() &&
CGM.getContext().getAuxTargetInfo()->getCXXABI().isItaniumFamily()) {
return std::unique_ptr<MangleContext>(
CGM.getContext().createDeviceMangleContext(
*CGM.getContext().getAuxTargetInfo()));
}
return std::unique_ptr<MangleContext>(CGM.getContext().createMangleContext(
CGM.getContext().getAuxTargetInfo()));
}
CGNVCUDARuntime::CGNVCUDARuntime(CodeGenModule &CGM)
: CGCUDARuntime(CGM), Context(CGM.getLLVMContext()),
TheModule(CGM.getModule()),
RelocatableDeviceCode(CGM.getLangOpts().GPURelocatableDeviceCode),
DeviceMC(CGM.getContext().createMangleContext(
CGM.getContext().getAuxTargetInfo())) {
DeviceMC(InitDeviceMC(CGM)) {
CodeGen::CodeGenTypes &Types = CGM.getTypes();
ASTContext &Ctx = CGM.getContext();
@ -207,14 +222,6 @@ CGNVCUDARuntime::CGNVCUDARuntime(CodeGenModule &CGM)
CharPtrTy = llvm::PointerType::getUnqual(Types.ConvertType(Ctx.CharTy));
VoidPtrTy = cast<llvm::PointerType>(Types.ConvertType(Ctx.VoidPtrTy));
VoidPtrPtrTy = VoidPtrTy->getPointerTo();
if (CGM.getContext().getAuxTargetInfo()) {
// If the host and device have different C++ ABIs, mark it as the device
// mangle context so that the mangling needs to retrieve the additonal
// device lambda mangling number instead of the regular host one.
DeviceMC->setDeviceMangleContext(
CGM.getContext().getTargetInfo().getCXXABI().isMicrosoft() &&
CGM.getContext().getAuxTargetInfo()->getCXXABI().isItaniumFamily());
}
}
llvm::FunctionCallee CGNVCUDARuntime::getSetupArgumentFn() const {

View File

@ -486,6 +486,8 @@ public:
return CGF.EmitPseudoObjectRValue(E).getScalarVal();
}
Value *VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E);
Value *VisitOpaqueValueExpr(OpaqueValueExpr *E) {
if (E->isGLValue())
return EmitLoadOfLValue(CGF.getOrCreateOpaqueLValueMapping(E),
@ -1581,6 +1583,25 @@ Value *ScalarExprEmitter::VisitExpr(Expr *E) {
return llvm::UndefValue::get(CGF.ConvertType(E->getType()));
}
Value *
ScalarExprEmitter::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) {
ASTContext &Context = CGF.getContext();
llvm::Optional<LangAS> GlobalAS =
Context.getTargetInfo().getConstantAddressSpace();
llvm::Constant *GlobalConstStr = Builder.CreateGlobalStringPtr(
E->ComputeName(Context), "__usn_str",
static_cast<unsigned>(GlobalAS.getValueOr(LangAS::Default)));
unsigned ExprAS = Context.getTargetAddressSpace(E->getType());
if (GlobalConstStr->getType()->getPointerAddressSpace() == ExprAS)
return GlobalConstStr;
llvm::Type *EltTy = GlobalConstStr->getType()->getPointerElementType();
llvm::PointerType *NewPtrTy = llvm::PointerType::get(EltTy, ExprAS);
return Builder.CreateAddrSpaceCast(GlobalConstStr, NewPtrTy, "usn_addr_cast");
}
Value *ScalarExprEmitter::VisitShuffleVectorExpr(ShuffleVectorExpr *E) {
// Vector Mask Case
if (E->getNumSubExprs() == 2) {

View File

@ -1469,6 +1469,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
case tok::kw_this:
Res = ParseCXXThis();
break;
case tok::kw___builtin_sycl_unique_stable_name:
Res = ParseSYCLUniqueStableNameExpression();
break;
case tok::annot_typename:
if (isStartOfObjCClassMessageMissingOpenBracket()) {
@ -2324,6 +2327,33 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
return Operand;
}
/// Parse a __builtin_sycl_unique_stable_name expression. Accepts a type-id as
/// a parameter.
ExprResult Parser::ParseSYCLUniqueStableNameExpression() {
assert(Tok.is(tok::kw___builtin_sycl_unique_stable_name) &&
"Not __bulitin_sycl_unique_stable_name");
SourceLocation OpLoc = ConsumeToken();
BalancedDelimiterTracker T(*this, tok::l_paren);
// __builtin_sycl_unique_stable_name expressions are always parenthesized.
if (T.expectAndConsume(diag::err_expected_lparen_after,
"__builtin_sycl_unique_stable_name"))
return ExprError();
TypeResult Ty = ParseTypeName();
if (Ty.isInvalid()) {
T.skipToEnd();
return ExprError();
}
if (T.consumeClose())
return ExprError();
return Actions.ActOnSYCLUniqueStableNameExpr(OpLoc, T.getOpenLocation(),
T.getCloseLocation(), Ty.get());
}
/// Parse a sizeof or alignof expression.
///

View File

@ -1575,6 +1575,8 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
return mergeCanThrow(CT, canThrow(TS->getTryBody()));
}
case Stmt::SYCLUniqueStableNameExprClass:
return CT_Cannot;
case Stmt::NoStmtClass:
llvm_unreachable("Invalid class for statement");
}

View File

@ -3506,6 +3506,28 @@ ExprResult Sema::BuildPredefinedExpr(SourceLocation Loc,
return PredefinedExpr::Create(Context, Loc, ResTy, IK, SL);
}
ExprResult Sema::BuildSYCLUniqueStableNameExpr(SourceLocation OpLoc,
SourceLocation LParen,
SourceLocation RParen,
TypeSourceInfo *TSI) {
return SYCLUniqueStableNameExpr::Create(Context, OpLoc, LParen, RParen, TSI);
}
ExprResult Sema::ActOnSYCLUniqueStableNameExpr(SourceLocation OpLoc,
SourceLocation LParen,
SourceLocation RParen,
ParsedType ParsedTy) {
TypeSourceInfo *TSI = nullptr;
QualType Ty = GetTypeFromParser(ParsedTy, &TSI);
if (Ty.isNull())
return ExprError();
if (!TSI)
TSI = Context.getTrivialTypeSourceInfo(Ty, LParen);
return BuildSYCLUniqueStableNameExpr(OpLoc, LParen, RParen, TSI);
}
ExprResult Sema::ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind) {
PredefinedExpr::IdentKind IK;

View File

@ -461,11 +461,15 @@ void Sema::handleLambdaNumbering(
std::tie(MCtx, ManglingContextDecl) =
getCurrentMangleNumberContext(Class->getDeclContext());
bool HasKnownInternalLinkage = false;
if (!MCtx && getLangOpts().CUDA) {
if (!MCtx && (getLangOpts().CUDA || getLangOpts().SYCLIsDevice ||
getLangOpts().SYCLIsHost)) {
// Force lambda numbering in CUDA/HIP as we need to name lambdas following
// ODR. Both device- and host-compilation need to have a consistent naming
// on kernel functions. As lambdas are potential part of these `__global__`
// function names, they needs numbering following ODR.
// Also force for SYCL, since we need this for the
// __builtin_sycl_unique_stable_name implementation, which depends on lambda
// mangling.
MCtx = getMangleNumberingContext(Class, ManglingContextDecl);
assert(MCtx && "Retrieving mangle numbering context failed!");
HasKnownInternalLinkage = true;

View File

@ -8,6 +8,7 @@
// This implements Semantic Analysis for SYCL constructs.
//===----------------------------------------------------------------------===//
#include "clang/AST/Mangle.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaDiagnostic.h"
@ -47,3 +48,35 @@ bool Sema::checkSYCLDeviceFunction(SourceLocation Loc, FunctionDecl *Callee) {
return DiagKind != SemaDiagnosticBuilder::K_Immediate &&
DiagKind != SemaDiagnosticBuilder::K_ImmediateWithCallStack;
}
// The SYCL kernel's 'object type' used for diagnostics and naming/mangling is
// the first parameter to a sycl_kernel labeled function template. In SYCL1.2.1,
// this was passed by value, and in SYCL2020, it is passed by reference.
static QualType GetSYCLKernelObjectType(const FunctionDecl *KernelCaller) {
assert(KernelCaller->getNumParams() > 0 && "Insufficient kernel parameters");
QualType KernelParamTy = KernelCaller->getParamDecl(0)->getType();
// SYCL 2020 kernels are passed by reference.
if (KernelParamTy->isReferenceType())
return KernelParamTy->getPointeeType();
// SYCL 1.2.1
return KernelParamTy;
}
void Sema::AddSYCLKernelLambda(const FunctionDecl *FD) {
auto MangleCallback = [](ASTContext &Ctx,
const NamedDecl *ND) -> llvm::Optional<unsigned> {
if (const auto *RD = dyn_cast<CXXRecordDecl>(ND))
Ctx.AddSYCLKernelNamingDecl(RD);
// We always want to go into the lambda mangling (skipping the unnamed
// struct version), so make sure we return a value here.
return 1;
};
QualType Ty = GetSYCLKernelObjectType(FD);
std::unique_ptr<MangleContext> Ctx{ItaniumMangleContext::create(
Context, Context.getDiagnostics(), MangleCallback)};
llvm::raw_null_ostream Out;
Ctx->mangleTypeName(Ty, Out);
}

View File

@ -548,6 +548,35 @@ static void instantiateDependentAMDGPUWavesPerEUAttr(
S.addAMDGPUWavesPerEUAttr(New, Attr, MinExpr, MaxExpr);
}
// This doesn't take any template parameters, but we have a custom action that
// needs to happen when the kernel itself is instantiated. We need to run the
// ItaniumMangler to mark the names required to name this kernel.
static void instantiateDependentSYCLKernelAttr(
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
const SYCLKernelAttr &Attr, Decl *New) {
// Functions cannot be partially specialized, so if we are being instantiated,
// we are obviously a complete specialization. Since this attribute is only
// valid on function template declarations, we know that this is a full
// instantiation of a kernel.
S.AddSYCLKernelLambda(cast<FunctionDecl>(New));
// Evaluate whether this would change any of the already evaluated
// __builtin_sycl_unique_stable_name values.
for (auto &Itr : S.Context.SYCLUniqueStableNameEvaluatedValues) {
const std::string &CurName = Itr.first->ComputeName(S.Context);
if (Itr.second != CurName) {
S.Diag(New->getLocation(),
diag::err_kernel_invalidates_sycl_unique_stable_name);
S.Diag(Itr.first->getLocation(),
diag::note_sycl_unique_stable_name_evaluated_here);
// Update this so future diagnostics work correctly.
Itr.second = CurName;
}
}
New->addAttr(Attr.clone(S.getASTContext()));
}
/// Determine whether the attribute A might be relevent to the declaration D.
/// If not, we can skip instantiating it. The attribute may or may not have
/// been instantiated yet.
@ -723,6 +752,11 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
continue;
}
if (auto *A = dyn_cast<SYCLKernelAttr>(TmplAttr)) {
instantiateDependentSYCLKernelAttr(*this, TemplateArgs, *A, New);
continue;
}
assert(!TmplAttr->isPackExpansion());
if (TmplAttr->isLateParsed() && LateAttrs) {
// Late parsed attributes must be instantiated and attached after the

View File

@ -2399,6 +2399,19 @@ public:
return SEHFinallyStmt::Create(getSema().getASTContext(), Loc, Block);
}
ExprResult RebuildSYCLUniqueStableNameExpr(SourceLocation OpLoc,
SourceLocation LParen,
SourceLocation RParen, Expr *E) {
return getSema().BuildSYCLUniqueStableNameExpr(OpLoc, LParen, RParen, E);
}
ExprResult RebuildSYCLUniqueStableNameExpr(SourceLocation OpLoc,
SourceLocation LParen,
SourceLocation RParen,
TypeSourceInfo *TSI) {
return getSema().BuildSYCLUniqueStableNameExpr(OpLoc, LParen, RParen, TSI);
}
/// Build a new predefined expression.
///
/// By default, performs semantic analysis to build the new expression.
@ -10172,6 +10185,24 @@ TreeTransform<Derived>::TransformConstantExpr(ConstantExpr *E) {
return TransformExpr(E->getSubExpr());
}
template <typename Derived>
ExprResult TreeTransform<Derived>::TransformSYCLUniqueStableNameExpr(
SYCLUniqueStableNameExpr *E) {
if (!E->isTypeDependent())
return E;
TypeSourceInfo *NewT = getDerived().TransformType(E->getTypeSourceInfo());
if (!NewT)
return ExprError();
if (!getDerived().AlwaysRebuild() && E->getTypeSourceInfo() == NewT)
return E;
return getDerived().RebuildSYCLUniqueStableNameExpr(
E->getLocation(), E->getLParenLocation(), E->getRParenLocation(), NewT);
}
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformPredefinedExpr(PredefinedExpr *E) {

View File

@ -581,6 +581,16 @@ void ASTStmtReader::VisitConstantExpr(ConstantExpr *E) {
E->setSubExpr(Record.readSubExpr());
}
void ASTStmtReader::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) {
VisitExpr(E);
E->setLocation(readSourceLocation());
E->setLParenLocation(readSourceLocation());
E->setRParenLocation(readSourceLocation());
E->setTypeSourceInfo(Record.readTypeSourceInfo());
}
void ASTStmtReader::VisitPredefinedExpr(PredefinedExpr *E) {
VisitExpr(E);
bool HasFunctionName = Record.readInt();
@ -2802,6 +2812,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
/*StorageKind=*/Record[ASTStmtReader::NumExprFields]));
break;
case EXPR_SYCL_UNIQUE_STABLE_NAME:
S = SYCLUniqueStableNameExpr::CreateEmpty(Context);
break;
case EXPR_PREDEFINED:
S = PredefinedExpr::CreateEmpty(
Context,

View File

@ -580,6 +580,17 @@ void ASTStmtWriter::VisitConstantExpr(ConstantExpr *E) {
Code = serialization::EXPR_CONSTANT;
}
void ASTStmtWriter::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) {
VisitExpr(E);
Record.AddSourceLocation(E->getLocation());
Record.AddSourceLocation(E->getLParenLocation());
Record.AddSourceLocation(E->getRParenLocation());
Record.AddTypeSourceInfo(E->getTypeSourceInfo());
Code = serialization::EXPR_SYCL_UNIQUE_STABLE_NAME;
}
void ASTStmtWriter::VisitPredefinedExpr(PredefinedExpr *E) {
VisitExpr(E);

View File

@ -1419,6 +1419,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPArraySectionExprClass:
case Stmt::OMPArrayShapingExprClass:
case Stmt::OMPIteratorExprClass:
case Stmt::SYCLUniqueStableNameExprClass:
case Stmt::TypeTraitExprClass: {
Bldr.takeNodes(Pred);
ExplodedNodeSet preVisit;

View File

@ -0,0 +1,28 @@
// RUN: %clang_cc1 -ast-print -fsycl-is-device %s -o - -triple spir64-sycldevice | FileCheck %s
template <typename T>
void WrappedInTemplate(T t) {
(void)__builtin_sycl_unique_stable_name(T);
(void)__builtin_sycl_unique_stable_name(typename T::type);
(void)__builtin_sycl_unique_stable_name(decltype(t.foo()));
}
struct Type {
using type = int;
double foo();
};
void use() {
WrappedInTemplate(Type{});
}
// CHECK: template <typename T> void WrappedInTemplate(T t)
// CHECK: __builtin_sycl_unique_stable_name(T);
// CHECK: __builtin_sycl_unique_stable_name(typename T::type);
// CHECK: __builtin_sycl_unique_stable_name(decltype(t.foo()));
// CHECK: template<> void WrappedInTemplate<Type>(Type t)
// CHECK: __builtin_sycl_unique_stable_name(Type);
// CHECK: __builtin_sycl_unique_stable_name(typename Type::type);
// CHECK: __builtin_sycl_unique_stable_name(decltype(t.foo()));

View File

@ -0,0 +1,159 @@
// RUN: %clang_cc1 -triple spir64-unknown-unknown-sycldevice -fsycl-is-device -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s
// CHECK: @[[LAMBDA_KERNEL3:[^\w]+]] = private unnamed_addr constant [[LAMBDA_K3_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ4mainEUlPZ4mainEUlvE10000_E10000_\00"
// CHECK: @[[INT1:[^\w]+]] = private unnamed_addr constant [[INT_SIZE:\[[0-9]+ x i8\]]] c"_ZTSi\00"
// CHECK: @[[STRING:[^\w]+]] = private unnamed_addr constant [[STRING_SIZE:\[[0-9]+ x i8\]]] c"_ZTSAppL_ZZ4mainE1jE_i\00",
// CHECK: @[[INT2:[^\w]+]] = private unnamed_addr constant [[INT_SIZE]] c"_ZTSi\00"
// CHECK: @[[LAMBDA_X:[^\w]+]] = private unnamed_addr constant [[LAMBDA_X_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE_\00"
// CHECK: @[[MACRO_X:[^\w]+]] = private unnamed_addr constant [[MACRO_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE0_\00"
// CHECK: @[[MACRO_Y:[^\w]+]] = private unnamed_addr constant [[MACRO_SIZE]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE1_\00"
// CHECK: @{{.*}} = private unnamed_addr constant [36 x i8] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE2_\00", align 1
// CHECK: @{{.*}} = private unnamed_addr constant [36 x i8] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE3_\00", align 1
// CHECK: @[[MACRO_MACRO_X:[^\w]+]] = private unnamed_addr constant [[MACRO_MACRO_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE4_\00"
// CHECK: @[[MACRO_MACRO_Y:[^\w]+]] = private unnamed_addr constant [[MACRO_MACRO_SIZE]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE5_\00"
// CHECK: @[[INT3:[^\w]+]] = private unnamed_addr constant [[INT_SIZE]] c"_ZTSi\00"
// CHECK: @[[LAMBDA:[^\w]+]] = private unnamed_addr constant [[LAMBDA_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE_\00"
// CHECK: @[[LAMBDA_IN_DEP_INT:[^\w]+]] = private unnamed_addr constant [[DEP_INT_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ28lambda_in_dependent_functionIiEvvEUlvE_\00",
// CHECK: @[[LAMBDA_IN_DEP_X:[^\w]+]] = private unnamed_addr constant [[DEP_LAMBDA_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ28lambda_in_dependent_functionIZZ4mainENKUlvE10001_clEvEUlvE_EvvEUlvE_\00",
// CHECK: @[[LAMBDA_NO_DEP:[^\w]+]] = private unnamed_addr constant [[NO_DEP_LAMBDA_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ13lambda_no_depIidEvT_T0_EUlidE_\00",
// CHECK: @[[LAMBDA_TWO_DEP:[^\w]+]] = private unnamed_addr constant [[DEP_LAMBDA1_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ14lambda_two_depIZZ4mainENKUlvE10001_clEvEUliE_ZZ4mainENKS0_clEvEUldE_EvvEUlvE_\00",
// CHECK: @[[LAMBDA_TWO_DEP2:[^\w]+]] = private unnamed_addr constant [[DEP_LAMBDA2_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ14lambda_two_depIZZ4mainENKUlvE10001_clEvEUldE_ZZ4mainENKS0_clEvEUliE_EvvEUlvE_\00",
extern "C" void puts(const char *) {}
template <typename T>
void template_param() {
puts(__builtin_sycl_unique_stable_name(T));
}
template <typename T>
void lambda_in_dependent_function() {
auto y = [] {};
puts(__builtin_sycl_unique_stable_name(decltype(y)));
}
template <typename Tw, typename Tz>
void lambda_two_dep() {
auto z = [] {};
puts(__builtin_sycl_unique_stable_name(decltype(z)));
}
template <typename Tw, typename Tz>
void lambda_no_dep(Tw a, Tz b) {
auto p = [](Tw a, Tz b) { return ((Tz)a + b); };
puts(__builtin_sycl_unique_stable_name(decltype(p)));
}
#define DEF_IN_MACRO() \
auto MACRO_X = []() {}; \
auto MACRO_Y = []() {}; \
puts(__builtin_sycl_unique_stable_name(decltype(MACRO_X))); \
puts(__builtin_sycl_unique_stable_name(decltype(MACRO_Y)));
#define MACRO_CALLS_MACRO() \
{ DEF_IN_MACRO(); } \
{ DEF_IN_MACRO(); }
template <typename Ty>
auto func() -> decltype(__builtin_sycl_unique_stable_name(decltype(Ty::str)));
struct Derp {
static constexpr const char str[] = "derp derp derp";
};
template <typename KernelName, typename KernelType>
[[clang::sycl_kernel]] void kernel_single_task(KernelType kernelFunc) {
kernelFunc();
}
int main() {
kernel_single_task<class kernel2>(func<Derp>);
// CHECK: call spir_func void @_Z18kernel_single_taskIZ4mainE7kernel2PFPKcvEEvT0_(i8 addrspace(4)* ()* @_Z4funcI4DerpEDTu33__builtin_sycl_unique_stable_nameDtsrT_3strEEEv)
auto l1 = []() { return 1; };
auto l2 = [](decltype(l1) *l = nullptr) { return 2; };
kernel_single_task<class kernel3>(l2);
puts(__builtin_sycl_unique_stable_name(decltype(l2)));
// CHECK: call spir_func void @_Z18kernel_single_taskIZ4mainE7kernel3Z4mainEUlPZ4mainEUlvE_E_EvT0_
// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[LAMBDA_K3_SIZE]], [[LAMBDA_K3_SIZE]]* @[[LAMBDA_KERNEL3]], i32 0, i32 0) to i8 addrspace(4)*))
constexpr const char str[] = "lalala";
static_assert(__builtin_strcmp(__builtin_sycl_unique_stable_name(decltype(str)), "_ZTSA7_Kc\0") == 0, "unexpected mangling");
int i = 0;
puts(__builtin_sycl_unique_stable_name(decltype(i++)));
// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[INT_SIZE]], [[INT_SIZE]]* @[[INT1]], i32 0, i32 0) to i8 addrspace(4)*))
// FIXME: Ensure that j is incremented because VLAs are terrible.
int j = 55;
puts(__builtin_sycl_unique_stable_name(int[++j]));
// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[STRING_SIZE]], [[STRING_SIZE]]* @[[STRING]], i32 0, i32 0) to i8 addrspace(4)*))
// CHECK: define internal spir_func void @_Z18kernel_single_taskIZ4mainE7kernel2PFPKcvEEvT0_
// CHECK: declare spir_func i8 addrspace(4)* @_Z4funcI4DerpEDTu33__builtin_sycl_unique_stable_nameDtsrT_3strEEEv
// CHECK: define internal spir_func void @_Z18kernel_single_taskIZ4mainE7kernel3Z4mainEUlPZ4mainEUlvE_E_EvT0_
// CHECK: define internal spir_func void @_Z18kernel_single_taskIZ4mainE6kernelZ4mainEUlvE0_EvT0_
kernel_single_task<class kernel>(
[]() {
puts(__builtin_sycl_unique_stable_name(int));
// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[INT_SIZE]], [[INT_SIZE]]* @[[INT2]], i32 0, i32 0) to i8 addrspace(4)*))
auto x = []() {};
puts(__builtin_sycl_unique_stable_name(decltype(x)));
// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[LAMBDA_X_SIZE]], [[LAMBDA_X_SIZE]]* @[[LAMBDA_X]], i32 0, i32 0) to i8 addrspace(4)*))
DEF_IN_MACRO();
// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[MACRO_SIZE]], [[MACRO_SIZE]]* @[[MACRO_X]], i32 0, i32 0) to i8 addrspace(4)*))
// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[MACRO_SIZE]], [[MACRO_SIZE]]* @[[MACRO_Y]], i32 0, i32 0) to i8 addrspace(4)*))
MACRO_CALLS_MACRO();
// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[MACRO_MACRO_SIZE]], [[MACRO_MACRO_SIZE]]* @[[MACRO_MACRO_X]], i32 0, i32 0) to i8 addrspace(4)*))
// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[MACRO_MACRO_SIZE]], [[MACRO_MACRO_SIZE]]* @[[MACRO_MACRO_Y]], i32 0, i32 0) to i8 addrspace(4)*))
template_param<int>();
// CHECK: call spir_func void @_Z14template_paramIiEvv
template_param<decltype(x)>();
// CHECK: call spir_func void @_Z14template_paramIZZ4mainENKUlvE0_clEvEUlvE_Evv
lambda_in_dependent_function<int>();
// CHECK: call spir_func void @_Z28lambda_in_dependent_functionIiEvv
lambda_in_dependent_function<decltype(x)>();
// CHECK: call spir_func void @_Z28lambda_in_dependent_functionIZZ4mainENKUlvE0_clEvEUlvE_Evv
lambda_no_dep<int, double>(3, 5.5);
// CHECK: call spir_func void @_Z13lambda_no_depIidEvT_T0_(i32 3, double 5.500000e+00)
int a = 5;
double b = 10.7;
auto y = [](int a) { return a; };
auto z = [](double b) { return b; };
lambda_two_dep<decltype(y), decltype(z)>();
// CHECK: call spir_func void @_Z14lambda_two_depIZZ4mainENKUlvE0_clEvEUliE_ZZ4mainENKS0_clEvEUldE_Evv
lambda_two_dep<decltype(z), decltype(y)>();
// CHECK: call spir_func void @_Z14lambda_two_depIZZ4mainENKUlvE0_clEvEUldE_ZZ4mainENKS0_clEvEUliE_Evv
});
}
// CHECK: define linkonce_odr spir_func void @_Z14template_paramIiEvv
// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[INT_SIZE]], [[INT_SIZE]]* @[[INT3]], i32 0, i32 0) to i8 addrspace(4)*))
// CHECK: define internal spir_func void @_Z14template_paramIZZ4mainENKUlvE0_clEvEUlvE_Evv
// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[LAMBDA_SIZE]], [[LAMBDA_SIZE]]* @[[LAMBDA]], i32 0, i32 0) to i8 addrspace(4)*))
// CHECK: define linkonce_odr spir_func void @_Z28lambda_in_dependent_functionIiEvv
// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[DEP_INT_SIZE]], [[DEP_INT_SIZE]]* @[[LAMBDA_IN_DEP_INT]], i32 0, i32 0) to i8 addrspace(4)*))
// CHECK: define internal spir_func void @_Z28lambda_in_dependent_functionIZZ4mainENKUlvE0_clEvEUlvE_Evv
// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[DEP_LAMBDA_SIZE]], [[DEP_LAMBDA_SIZE]]* @[[LAMBDA_IN_DEP_X]], i32 0, i32 0) to i8 addrspace(4)*))
// CHECK: define linkonce_odr spir_func void @_Z13lambda_no_depIidEvT_T0_(i32 %a, double %b)
// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[NO_DEP_LAMBDA_SIZE]], [[NO_DEP_LAMBDA_SIZE]]* @[[LAMBDA_NO_DEP]], i32 0, i32 0) to i8 addrspace(4)*))
// CHECK: define internal spir_func void @_Z14lambda_two_depIZZ4mainENKUlvE0_clEvEUliE_ZZ4mainENKS0_clEvEUldE_Evv
// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[DEP_LAMBDA1_SIZE]], [[DEP_LAMBDA1_SIZE]]* @[[LAMBDA_TWO_DEP]], i32 0, i32 0) to i8 addrspace(4)*))
// CHECK: define internal spir_func void @_Z14lambda_two_depIZZ4mainENKUlvE0_clEvEUldE_ZZ4mainENKS0_clEvEUliE_Evv
// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[DEP_LAMBDA2_SIZE]], [[DEP_LAMBDA2_SIZE]]* @[[LAMBDA_TWO_DEP2]], i32 0, i32 0) to i8 addrspace(4)*))

View File

@ -0,0 +1,43 @@
// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify -Wno-unused %s
namespace NS {
using good = double;
}
void f(int var) {
// expected-error@+1{{expected '(' after '__builtin_sycl_unique_stable_name'}}
__builtin_sycl_unique_stable_name int; // Correct usage is __builtin_sycl_unique_stable_name(int);
// expected-error@+1{{expected '(' after '__builtin_sycl_unique_stable_name'}}
__builtin_sycl_unique_stable_name{int}; // Correct usage is __builtin_sycl_unique_stable_name(int);
// expected-error@+2{{expected ')'}}
// expected-note@+1{{to match this '('}}
__builtin_sycl_unique_stable_name(int; // Missing paren before semicolon
// expected-error@+2{{expected ')'}}
// expected-note@+1{{to match this '('}}
__builtin_sycl_unique_stable_name(int, float); // Missing paren before comma
// expected-error@+1{{unknown type name 'var'}}
__builtin_sycl_unique_stable_name(var);
__builtin_sycl_unique_stable_name(NS::good);
// expected-error@+1{{expected a type}}
__builtin_sycl_unique_stable_name(for (int i = 0; i < 10; ++i) {})
__builtin_sycl_unique_stable_name({
(for (int i = 0; i < 10; ++i){})})
}
template <typename T>
void f2() {
__builtin_sycl_unique_stable_name(typename T::good_type);
}
struct S {
class good_type {};
};
void use() {
f2<S>();
}

View File

@ -0,0 +1,9 @@
// RUN: %clang_cc1 -fsyntax-only -verify=notsycl -Wno-unused %s
// RUN: %clang_cc1 -fsyntax-only -fsycl-is-host -verify=sycl -Wno-unused %s
// RUN: %clang_cc1 -fsyntax-only -fsycl-is-device -verify=sycl -Wno-unused %s
// sycl-no-diagnostics
void foo() {
// notsycl-error@+1{{expected '(' for function-style cast or type construction}}
__builtin_sycl_unique_stable_name(int);
}

View File

@ -0,0 +1,215 @@
// RUN: %clang_cc1 %s -std=c++17 -triple x86_64-pc-windows-msvc -fsycl-is-device -verify -fsyntax-only -Wno-unused
// RUN: %clang_cc1 %s -std=c++17 -triple x86_64-linux-gnu -fsycl-is-device -verify -fsyntax-only -Wno-unused
template <typename KernelName, typename KernelType>
[[clang::sycl_kernel]] void kernel_single_task(KernelType kernelFunc) { // #kernelSingleTask
kernelFunc();
}
// kernel1 - expect error
// The current function is named with a lambda (i.e., takes a lambda as a
// template parameter. Call the builtin on the current function then it is
// passed to a kernel. Test that passing the given function to the unique
// stable name builtin and then to the kernel throws an error because the
// latter causes its name mangling to change.
template <typename Func>
void kernel1func(const Func &F1) {
constexpr const char *F1_output = __builtin_sycl_unique_stable_name(Func); // #USN_F1
// expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_sycl_unique_stable_name'}}
// expected-note@#kernel1func_call{{in instantiation of function template specialization}}
// expected-note@#USN_F1{{'__builtin_sycl_unique_stable_name' evaluated here}}
// expected-note@+1{{in instantiation of function template specialization}}
kernel_single_task<class kernel1>(F1); // #kernel1_call
}
void callkernel1() {
kernel1func([]() {}); // #kernel1func_call
}
// kernel2 - expect error
// The current function is named with a lambda (i.e., takes a lambda as a
// template parameter). Call the builtin on the given function,
// then an empty lambda is passed to kernel.
// Test that passing the given function to the unique stable name builtin and
// then passing a different lambda to the kernel still throws an error because
// the calling context is part of naming the kernel. Even though the given
// function (F2) is not passed to the kernel, its mangling changes due to
// kernel call with the unrelated lambda.
template <typename Func>
void kernel2func(const Func &F2) {
constexpr const char *F2_output = __builtin_sycl_unique_stable_name(Func); // #USN_F2
// expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_sycl_unique_stable_name'}}
// expected-note@#kernel2func_call{{in instantiation of function template specialization}}
// expected-note@#USN_F2{{'__builtin_sycl_unique_stable_name' evaluated here}}
// expected-note@+1{{in instantiation of function template specialization}}
kernel_single_task<class kernel2>([]() {});
}
void callkernel2() {
kernel2func([]() {}); // #kernel2func_call
}
template <template <typename> typename Outer, typename Inner>
struct S {
void operator()() const;
};
template <typename Ty>
struct Tangerine {};
template <typename Func>
void kernel3_4func(const Func &F) {
// Test that passing the same lambda to two kernels does not cause an error
// because the kernel uses do not interfere with each other or invalidate
// the stable name in any way.
kernel_single_task<class kernel3>(F);
kernel_single_task<class kernel4>(F);
// Using the same functor twice should be fine
}
// kernel3 and kernel4 - expect no errors
void callkernel3_4() {
kernel3_4func([]() {});
}
template <typename T>
static constexpr const char *output1 = __builtin_sycl_unique_stable_name(T);
#define MACRO() \
auto l14 = []() { return 1; }; \
constexpr const char *l14_output = \
__builtin_sycl_unique_stable_name(decltype(l14));
int main() {
// kernel5 - expect no error
// Test that passing the lambda to the unique stable name builtin and then
// using the lambda in a way that does not contribute to the kernel name
// does not cause an error because the stable name is not invalidated in
// this situation.
auto l5 = []() {};
constexpr const char *l5_output =
__builtin_sycl_unique_stable_name(decltype(l5));
kernel_single_task<class kernel5>(
[=]() { l5(); }); // Used in the kernel, but not the kernel name itself
// kernel6 - expect error
// Test that passing the lambda to the unique stable name builtin and then
// using the same lambda in the naming of a kernel causes a diagnostic on the
// kernel use due to the change in results to the stable name.
auto l6 = []() { return 1; };
constexpr const char *l6_output =
__builtin_sycl_unique_stable_name(decltype(l6)); // #USN_l6
// expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_sycl_unique_stable_name'}}
// expected-note@#USN_l6{{'__builtin_sycl_unique_stable_name' evaluated here}}
// expected-note@+1{{in instantiation of function template specialization}}
kernel_single_task<class kernel6>(l6); // Used in the kernel name after builtin
// kernel7 - expect error
// Same as kernel11 (below) except make the lambda part of naming the kernel.
// Test that passing a lambda to the unique stable name builtin and then
// passing a second lambda to the kernel throws an error because the first
// lambda is included in the signature of the second lambda, hence it changes
// the mangling of the kernel.
auto l7 = []() { return 1; };
auto l8 = [](decltype(l7) *derp = nullptr) { return 2; };
constexpr const char *l7_output =
__builtin_sycl_unique_stable_name(decltype(l7)); // #USN_l7
// expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_sycl_unique_stable_name'}}
// expected-note@#USN_l7{{'__builtin_sycl_unique_stable_name' evaluated here}}
// expected-note@+1{{in instantiation of function template specialization}}
kernel_single_task<class kernel7>(l8);
// kernel8 and kernel9 - expect error
// Tests that passing a lambda to the unique stable name builtin and passing it
// to a kernel called with an if constexpr branch causes a diagnostic on the
// kernel9 use due to the change in the results to the stable name. This happens
// even though the use of kernel9 happens in the false branch of a constexpr if
// because both the true and the false branches cause the instantiation of
// kernel_single_task.
auto l9 = []() { return 1; };
auto l10 = []() { return 2; };
constexpr const char *l10_output =
__builtin_sycl_unique_stable_name(decltype(l10)); // #USN_l10
if constexpr (1) {
kernel_single_task<class kernel8>(l9);
} else {
// expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_sycl_unique_stable_name'}}
// expected-note@#USN_l10{{'__builtin_sycl_unique_stable_name' evaluated here}}
// expected-note@+1{{in instantiation of function template specialization}}
kernel_single_task<class kernel9>(l10);
}
// kernel11 - expect no error
// Test that passing a lambda to the unique stable name builtin and then
// passing a second lambda capturing the first one to the kernel does not
// throw an error because the first lambda is not involved in naming the
// kernel i.e., the mangling does not change.
auto l11 = []() { return 1; };
auto l12 = [l11]() { return 2; };
constexpr const char *l11_output =
__builtin_sycl_unique_stable_name(decltype(l11));
kernel_single_task<class kernel11>(l12);
// kernel12 - expect an error
// Test that passing a lambda to the unique stable name builtin and then
// passing it to the kernel as a template template parameter causes a
// diagnostic on the kernel use due to template template parameter being
// involved in the mangling of the kernel name.
auto l13 = []() { return 1; };
constexpr const char *l13_output =
__builtin_sycl_unique_stable_name(decltype(l13)); // #USN_l13
// expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_sycl_unique_stable_name'}}
// expected-note@#USN_l13{{'__builtin_sycl_unique_stable_name' evaluated here}}
// expected-note@+1{{in instantiation of function template specialization}}
kernel_single_task<class kernel12>(S<Tangerine, decltype(l13)>{});
// kernel13 - expect an error
// Test that passing a lambda to the unique stable name builtin within a macro
// and then calling the macro within the kernel causes an error on the kernel
// and diagnoses in all the expected places despite the use of a macro.
// expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_sycl_unique_stable_name'}}
// expected-note@#USN_MACRO{{'__builtin_sycl_unique_stable_name' evaluated here}}
// expected-note@+1{{in instantiation of function template specialization}}
kernel_single_task<class kernel13>(
[]() {
MACRO(); // #USN_MACRO
});
}
namespace NS {}
void f() {
// expected-error@+1{{unknown type name 'bad_var'}}
__builtin_sycl_unique_stable_name(bad_var);
// expected-error@+1{{use of undeclared identifier 'bad'}}
__builtin_sycl_unique_stable_name(bad::type);
// expected-error@+1{{no type named 'still_bad' in namespace 'NS'}}
__builtin_sycl_unique_stable_name(NS::still_bad);
// FIXME: warning about side-effects in an unevaluated context expected, but
// none currently emitted.
int i = 0;
__builtin_sycl_unique_stable_name(decltype(i++));
// Tests that use within a VLA does not diagnose as a side-effecting use in
// an unevaluated context because the use within a VLA extent forces
// evaluation.
int j = 55;
__builtin_sycl_unique_stable_name(int[++j]); // no warning expected
}
template <typename T>
void f2() {
// expected-error@+1{{no type named 'bad_val' in 'St'}}
__builtin_sycl_unique_stable_name(typename T::bad_val);
// expected-error@+1{{no type named 'bad_type' in 'St'}}
__builtin_sycl_unique_stable_name(typename T::bad_type);
}
struct St {};
void use() {
// expected-note@+1{{in instantiation of}}
f2<St>();
}

View File

@ -336,6 +336,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
case Stmt::ObjCBoxedExprClass:
case Stmt::ObjCSubscriptRefExprClass:
case Stmt::RecoveryExprClass:
case Stmt::SYCLUniqueStableNameExprClass:
K = CXCursor_UnexposedExpr;
break;