Skip to content

Commit

Permalink
Reimplement __builtin_unique_stable_name-
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Erich Keane committed May 27, 2021
1 parent bd97889 commit 8347ee4
Show file tree
Hide file tree
Showing 44 changed files with 1,111 additions and 34 deletions.
34 changes: 34 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------------------------------
Expand Down
23 changes: 23 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -2360,6 +2360,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// 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;

Expand Down Expand Up @@ -3163,10 +3169,27 @@ OPT_LIST(V)

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.
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/AST/ComputeDependence.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class MaterializeTemporaryExpr;
class CXXFoldExpr;
class TypeTraitExpr;
class ConceptSpecializationExpr;
class SYCLUniqueStableNameExpr;
class PredefinedExpr;
class CallExpr;
class OffsetOfExpr;
Expand Down Expand Up @@ -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);
Expand Down
58 changes: 58 additions & 0 deletions clang/include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -2039,6 +2039,64 @@ class PredefinedExpr final
}
};

// 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 {
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/JSONNodeDumper.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ class JSONNodeDumper
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);
Expand Down
11 changes: 8 additions & 3 deletions clang/include/clang/AST/Mangle.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,6 @@ class MangleContext {
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;
}
Expand Down Expand Up @@ -173,6 +170,8 @@ class MangleContext {

class ItaniumMangleContext : public MangleContext {
public:
using DiscriminatorOverrideTy =
llvm::Optional<unsigned> (*)(ASTContext &, const NamedDecl *);
explicit ItaniumMangleContext(ASTContext &C, DiagnosticsEngine &D)
: MangleContext(C, D, MK_Itanium) {}

Expand All @@ -195,12 +194,18 @@ class ItaniumMangleContext : public MangleContext {

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 {
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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, {})
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/TextNodeDumper.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ class TextNodeDumper
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);
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -6394,6 +6394,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">,
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,8 @@ class LangOptions : public LangOptionsBase {
bool hasWasmExceptions() const {
return getExceptionHandling() == ExceptionHandlingKind::Wasm;
}

bool isSYCL() const { return SYCLIsDevice || SYCLIsHost; }
};

/// Floating point control options
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/StmtNodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down
11 changes: 6 additions & 5 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -695,11 +695,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)
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1800,6 +1800,7 @@ class Parser : public CodeCompletionHandler {
ExprResult ParsePostfixExpressionSuffix(ExprResult LHS);
ExprResult ParseUnaryExprOrTypeTraitExpression();
ExprResult ParseBuiltinPrimaryExpression();
ExprResult ParseSYCLUniqueStableNameExpression();

ExprResult ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
bool &isCastExpr,
Expand Down
13 changes: 13 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,10 @@ class Sema final {
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 {
Expand Down Expand Up @@ -5420,6 +5424,15 @@ class Sema final {
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);
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1965,6 +1965,9 @@ enum StmtCode {

// FixedPointLiteral
EXPR_FIXEDPOINT_LITERAL,

// SYCLUniqueStableNameExpr
EXPR_SYCL_UNIQUE_STABLE_NAME,
};

/// The kinds of designators that can occur in a
Expand Down
Loading

0 comments on commit 8347ee4

Please sign in to comment.