Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.. title:: clang-tidy - portability-template-virtual-member-function

portability-template-virtual-member-function
============================================

Finds cases when an uninstantiated virtual member function in a template class causes
cross-compiler incompatibility.

Upon instantiating a template class, non-virtual member functions don't have to be
instantiated unless they are used. Virtual member function instantiation on the other hand
is unspecified and depends on the implementation of the compiler.

In the following snippets the virtual member function is not instantiated by GCC and Clang,
but it is instantiated by MSVC, so while the snippet is accepted by the former compilers,
it is rejected by the latter.

.. code:: c++

template<typename T>
struct CrossPlatformError {
virtual ~CrossPlatformError() = default;

static void used() {}

virtual void unused() {
T MSVCError = this;
};
};

int main() {
CrossPlatformError<int>::used();
return 0;
}

Cross-platform projects that need to support MSVC on Windows might see compiler errors
because certain virtual member functions are instantiated, which are not instantiated
by other compilers on other platforms. This check highlights such virtual member functions.
Original file line number Diff line number Diff line change
Expand Up @@ -546,3 +546,17 @@ void testAlsoNonMoveable() {
}

} // namespace issue_62550

namespace GH111450 {
struct Status;

struct Error {
Error(const Status& S);
};

struct Result {
Error E;
Result(Status&& S) : E(std::move(S)) {}
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: passing result of std::move() as a const reference argument; no move will actually happen [performance-move-const-arg]
};
} // namespace GH111450
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// RUN: %check_clang_tidy %s portability-template-virtual-member-function %t
namespace UninstantiatedVirtualMember {
template<typename T>
struct CrossPlatformError {
virtual ~CrossPlatformError() = default;

static void used() {}

// CHECK-MESSAGES: [[#@LINE+1]]:18: warning: unspecified virtual member function instantiation
virtual void unused() {
T MSVCError = this;
};
};

int main() {
// CHECK-MESSAGES: [[#@LINE+1]]:5: note: template instantiated here
CrossPlatformError<int>::used();
return 0;
}
} // namespace UninstantiatedVirtualMember

namespace UninstantiatedVirtualMembers {
template<typename T>
struct CrossPlatformError {
virtual ~CrossPlatformError() = default;

static void used() {}

// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
// CHECK-MESSAGES: [[#@LINE+13]]:5: note: template instantiated here
virtual void unused() {
T MSVCError = this;
};

// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
// CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here
virtual void unused2() {
T MSVCError = this;
};
};

int main() {
CrossPlatformError<int>::used();
return 0;
}
} // namespace UninstantiatedVirtualMembers

namespace UninstantiatedVirtualDestructor {
template<typename T>
struct CrossPlatformError {
// CHECK-MESSAGES: [[#@LINE+2]]:13: warning: unspecified virtual member function instantiation
// CHECK-MESSAGES: [[#@LINE+9]]:5: note: template instantiated here
virtual ~CrossPlatformError() {
T MSVCError = this;
};

static void used() {}
};

int main() {
CrossPlatformError<int>::used();
return 0;
}
} // namespace UninstantiatedVirtualDestructor

namespace MultipleImplicitInstantiations {
template<typename T>
struct CrossPlatformError {
virtual ~CrossPlatformError() = default;

static void used() {}

// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
// CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here
virtual void unused() {
T MSVCError = this;
};
};

int main() {
CrossPlatformError<int>::used();
CrossPlatformError<float>::used();
CrossPlatformError<long>::used();
return 0;
}
} // namespace MultipleImplicitInstantiations

namespace SomeImplicitInstantiationError {
template <typename T> struct CrossPlatformError {
virtual ~CrossPlatformError() = default;

static void used() {}

// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
// CHECK-MESSAGES: [[#@LINE+5]]:5: note: template instantiated here
virtual void unused(){};
};

int main() {
CrossPlatformError<int>::used();
CrossPlatformError<float> NoError;
return 0;
}
} // namespace SomeImplicitInstantiationError

namespace InstantiatedVirtualMemberFunctions {
template<typename T>
struct NoError {
virtual ~NoError() {};
virtual void unused() {};
virtual void unused2() {};
virtual void unused3() {};
};

int main() {
NoError<int> Ne;
return 0;
}
} // namespace InstantiatedVirtualMemberFunctions

namespace UninstantiatedNonVirtualMemberFunctions {
template<typename T>
struct NoError {
static void used() {};
void unused() {};
void unused2() {};
void unused3() {};
};

int main() {
NoError<int>::used();
return 0;
}
} // namespace UninstantiatedNonVirtualMemberFunctions

namespace PartialSpecializationError {
template<typename T, typename U>
struct CrossPlatformError {};

template<typename U>
struct CrossPlatformError<int, U>{
virtual ~CrossPlatformError() = default;

static void used() {}

// CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
// CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here
virtual void unused() {
U MSVCError = this;
};
};

int main() {
CrossPlatformError<int, float>::used();
return 0;
}
} // namespace PartialSpecializationError

namespace PartialSpecializationNoInstantiation {
template<typename T, typename U>
struct NoInstantiation {};

template<typename U>
struct NoInstantiation<int, U>{
virtual ~NoInstantiation() = default;

static void used() {}

virtual void unused() {
U MSVCError = this;
};
};
} // namespace PartialSpecializationNoInstantiation
2 changes: 2 additions & 0 deletions clang/cmake/caches/Fuchsia-stage2.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ foreach(target armv6m-none-eabi;armv7m-none-eabi;armv8m.main-none-eabi)
set(RUNTIMES_${target}_LIBCXX_CXX_ABI none CACHE STRING "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_STATIC ON CACHE BOOL "")
set(RUNTIMES_${target}_LIBCXX_SHARED_OUTPUT_NAME "c++-shared" CACHE STRING "")
set(RUNTIMES_${target}_LIBCXX_LIBC "llvm-libc" CACHE STRING "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_RANDOM_DEVICE OFF CACHE BOOL "")
Expand Down Expand Up @@ -396,6 +397,7 @@ foreach(target riscv32-unknown-elf)
set(RUNTIMES_${target}_LIBCXX_CXX_ABI none CACHE STRING "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_STATIC ON CACHE BOOL "")
set(RUNTIMES_${target}_LIBCXX_SHARED_OUTPUT_NAME "c++-shared" CACHE STRING "")
set(RUNTIMES_${target}_LIBCXX_LIBC "llvm-libc" CACHE STRING "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_RANDOM_DEVICE OFF CACHE BOOL "")
Expand Down
23 changes: 18 additions & 5 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,16 @@ C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
- Removed the restriction to literal types in constexpr functions in C++23 mode.

- Extend lifetime of temporaries in mem-default-init for P2718R0. Clang now fully
supports `P2718R0 Lifetime extension in range-based for loops <https://wg21.link/P2718R0>`_.

C++20 Feature Support
^^^^^^^^^^^^^^^^^^^^^

C++17 Feature Support
^^^^^^^^^^^^^^^^^^^^^
- The implementation of the relaxed template template argument matching rules is
more complete and reliable, and should provide more accurate diagnostics.

Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -200,7 +207,8 @@ Resolutions to C++ Defect Reports
(`CWG2351: void{} <https://cplusplus.github.io/CWG/issues/2351.html>`_).

- Clang now has improved resolution to CWG2398, allowing class templates to have
default arguments deduced when partial ordering.
default arguments deduced when partial ordering, and better backwards compatibility
in overload resolution.

- Clang now allows comparing unequal object pointers that have been cast to ``void *``
in constant expressions. These comparisons always worked in non-constant expressions.
Expand Down Expand Up @@ -331,6 +339,10 @@ Improvements to Clang's diagnostics

- Clang now diagnoses when the result of a [[nodiscard]] function is discarded after being cast in C. Fixes #GH104391.

- Clang now properly explains the reason a template template argument failed to
match a template template parameter, in terms of the C++17 relaxed matching rules
instead of the old ones.

- Don't emit duplicated dangling diagnostics. (#GH93386).

- Improved diagnostic when trying to befriend a concept. (#GH45182).
Expand Down Expand Up @@ -440,6 +452,8 @@ Bug Fixes to C++ Support
- Correctly check constraints of explicit instantiations of member functions. (#GH46029)
- When performing partial ordering of function templates, clang now checks that
the deduction was consistent. Fixes (#GH18291).
- Fixes to several issues in partial ordering of template template parameters, which
were documented in the test suite.
- Fixed an assertion failure about a constraint of a friend function template references to a value with greater
template depth than the friend function template. (#GH98258)
- Clang now rebuilds the template parameters of out-of-line declarations and specializations in the context
Expand Down Expand Up @@ -468,7 +482,6 @@ Bug Fixes to C++ Support
- Fixed an assertion failure in debug mode, and potential crashes in release mode, when
diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter.
- Fixed an assertion failure by adjusting integral to boolean vector conversions (#GH108326)
- Clang is now better at keeping track of friend function template instance contexts. (#GH55509)
- Fixed an issue deducing non-type template arguments of reference type. (#GH73460)
- Fixed an issue in constraint evaluation, where type constraints on the lambda expression
containing outer unexpanded parameters were not correctly expanded. (#GH101754)
Expand All @@ -478,9 +491,9 @@ Bug Fixes to C++ Support
in certain friend declarations. (#GH93099)
- Clang now instantiates the correct lambda call operator when a lambda's class type is
merged across modules. (#GH110401)
- Clang now uses the correct set of template argument lists when comparing the constraints of
out-of-line definitions and member templates explicitly specialized for a given implicit instantiation of
a class template. (#GH102320)
- Fix a crash when parsing a pseudo destructor involving an invalid type. (#GH111460)
- Fixed an assertion failure when invoking recovery call expressions with explicit attributes
and undeclared templates. (#GH107047, #GH49093)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
17 changes: 17 additions & 0 deletions clang/include/clang/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,23 @@ class InheritableParamAttr : public InheritableAttr {
}
};

class InheritableParamOrStmtAttr : public InheritableParamAttr {
protected:
InheritableParamOrStmtAttr(ASTContext &Context,
const AttributeCommonInfo &CommonInfo,
attr::Kind AK, bool IsLateParsed,
bool InheritEvenIfAlreadyPresent)
: InheritableParamAttr(Context, CommonInfo, AK, IsLateParsed,
InheritEvenIfAlreadyPresent) {}

public:
// Implement isa/cast/dyncast/etc.
static bool classof(const Attr *A) {
return A->getKind() >= attr::FirstInheritableParamOrStmtAttr &&
A->getKind() <= attr::LastInheritableParamOrStmtAttr;
}
};

class HLSLAnnotationAttr : public InheritableAttr {
protected:
HLSLAnnotationAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo,
Expand Down
7 changes: 0 additions & 7 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2299,13 +2299,6 @@ class FunctionDecl : public DeclaratorDecl,
FunctionDeclBits.IsLateTemplateParsed = ILT;
}

bool isInstantiatedFromMemberTemplate() const {
return FunctionDeclBits.IsInstantiatedFromMemberTemplate;
}
void setInstantiatedFromMemberTemplate(bool Val = true) {
FunctionDeclBits.IsInstantiatedFromMemberTemplate = Val;
}

/// Whether this function is "trivial" in some specialized C++ senses.
/// Can only be true for default constructors, copy constructors,
/// copy assignment operators, and destructors. Not meaningful until
Expand Down
13 changes: 7 additions & 6 deletions clang/include/clang/AST/DeclBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,9 @@ class alignas(8) Decl {
/// Whether this declaration comes from a named module.
bool isInNamedModule() const;

/// Whether this declaration comes from a header unit.
bool isFromHeaderUnit() const;

/// Return true if this declaration has an attribute which acts as
/// definition of the entity, such as 'alias' or 'ifunc'.
bool hasDefiningAttr() const;
Expand Down Expand Up @@ -1763,8 +1766,6 @@ class DeclContext {
uint64_t HasImplicitReturnZero : 1;
LLVM_PREFERRED_TYPE(bool)
uint64_t IsLateTemplateParsed : 1;
LLVM_PREFERRED_TYPE(bool)
uint64_t IsInstantiatedFromMemberTemplate : 1;

/// Kind of contexpr specifier as defined by ConstexprSpecKind.
LLVM_PREFERRED_TYPE(ConstexprSpecKind)
Expand Down Expand Up @@ -1815,7 +1816,7 @@ class DeclContext {
};

/// Number of inherited and non-inherited bits in FunctionDeclBitfields.
enum { NumFunctionDeclBits = NumDeclContextBits + 32 };
enum { NumFunctionDeclBits = NumDeclContextBits + 31 };

/// Stores the bits used by CXXConstructorDecl. If modified
/// NumCXXConstructorDeclBits and the accessor
Expand All @@ -1826,12 +1827,12 @@ class DeclContext {
LLVM_PREFERRED_TYPE(FunctionDeclBitfields)
uint64_t : NumFunctionDeclBits;

/// 19 bits to fit in the remaining available space.
/// 20 bits to fit in the remaining available space.
/// Note that this makes CXXConstructorDeclBitfields take
/// exactly 64 bits and thus the width of NumCtorInitializers
/// will need to be shrunk if some bit is added to NumDeclContextBitfields,
/// NumFunctionDeclBitfields or CXXConstructorDeclBitfields.
uint64_t NumCtorInitializers : 16;
uint64_t NumCtorInitializers : 17;
LLVM_PREFERRED_TYPE(bool)
uint64_t IsInheritingConstructor : 1;

Expand All @@ -1845,7 +1846,7 @@ class DeclContext {
};

/// Number of inherited and non-inherited bits in CXXConstructorDeclBitfields.
enum { NumCXXConstructorDeclBits = NumFunctionDeclBits + 19 };
enum { NumCXXConstructorDeclBits = NumFunctionDeclBits + 20 };

/// Stores the bits used by ObjCMethodDecl.
/// If modified NumObjCMethodDeclBits and the accessor
Expand Down
75 changes: 39 additions & 36 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -781,11 +781,15 @@ class RedeclarableTemplateDecl : public TemplateDecl,
EntryType *Entry, void *InsertPos);

struct CommonBase {
CommonBase() {}
CommonBase() : InstantiatedFromMember(nullptr, false) {}

/// The template from which this was most
/// directly instantiated (or null).
RedeclarableTemplateDecl *InstantiatedFromMember = nullptr;
///
/// The boolean value indicates whether this template
/// was explicitly specialized.
llvm::PointerIntPair<RedeclarableTemplateDecl*, 1, bool>
InstantiatedFromMember;

/// If non-null, points to an array of specializations (including
/// partial specializations) known only by their external declaration IDs.
Expand All @@ -805,19 +809,14 @@ class RedeclarableTemplateDecl : public TemplateDecl,
};

/// Pointer to the common data shared by all declarations of this
/// template, and a flag indicating if the template is a member
/// specialization.
mutable llvm::PointerIntPair<CommonBase *, 1, bool> Common;

CommonBase *getCommonPtrInternal() const { return Common.getPointer(); }
/// template.
mutable CommonBase *Common = nullptr;

/// Retrieves the "common" pointer shared by all (re-)declarations of
/// the same template. Calling this routine may implicitly allocate memory
/// for the common pointer.
CommonBase *getCommonPtr() const;

void setCommonPtr(CommonBase *C) const { Common.setPointer(C); }

virtual CommonBase *newCommon(ASTContext &C) const = 0;

// Construct a template decl with name, parameters, and templated element.
Expand Down Expand Up @@ -858,12 +857,15 @@ class RedeclarableTemplateDecl : public TemplateDecl,
/// template<> template<typename T>
/// struct X<int>::Inner { /* ... */ };
/// \endcode
bool isMemberSpecialization() const { return Common.getInt(); }
bool isMemberSpecialization() const {
return getCommonPtr()->InstantiatedFromMember.getInt();
}

/// Note that this member template is a specialization.
void setMemberSpecialization() {
assert(!isMemberSpecialization() && "already a member specialization");
Common.setInt(true);
assert(getCommonPtr()->InstantiatedFromMember.getPointer() &&
"Only member templates can be member template specializations");
getCommonPtr()->InstantiatedFromMember.setInt(true);
}

/// Retrieve the member template from which this template was
Expand Down Expand Up @@ -903,12 +905,12 @@ class RedeclarableTemplateDecl : public TemplateDecl,
/// void X<T>::f(T, U);
/// \endcode
RedeclarableTemplateDecl *getInstantiatedFromMemberTemplate() const {
return getCommonPtr()->InstantiatedFromMember;
return getCommonPtr()->InstantiatedFromMember.getPointer();
}

void setInstantiatedFromMemberTemplate(RedeclarableTemplateDecl *TD) {
assert(!getCommonPtr()->InstantiatedFromMember);
getCommonPtr()->InstantiatedFromMember = TD;
assert(!getCommonPtr()->InstantiatedFromMember.getPointer());
getCommonPtr()->InstantiatedFromMember.setPointer(TD);
}

/// Retrieve the "injected" template arguments that correspond to the
Expand Down Expand Up @@ -1008,15 +1010,6 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl {
return getTemplatedDecl()->isThisDeclarationADefinition();
}

bool isCompatibleWithDefinition() const {
return getTemplatedDecl()->isInstantiatedFromMemberTemplate() ||
isThisDeclarationADefinition();
}
void setInstantiatedFromMemberTemplate(FunctionTemplateDecl *D) {
getTemplatedDecl()->setInstantiatedFromMemberTemplate();
RedeclarableTemplateDecl::setInstantiatedFromMemberTemplate(D);
}

/// Return the specialization with the provided arguments if it exists,
/// otherwise return the insertion point.
FunctionDecl *findSpecialization(ArrayRef<TemplateArgument> Args,
Expand Down Expand Up @@ -1996,8 +1989,6 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl,
/// template arguments have been deduced.
void setInstantiationOf(ClassTemplatePartialSpecializationDecl *PartialSpec,
const TemplateArgumentList *TemplateArgs) {
assert(!isa<ClassTemplatePartialSpecializationDecl>(this) &&
"A partial specialization cannot be instantiated from a template");
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization*>() &&
"Already set to a class template partial specialization!");
auto *PS = new (getASTContext()) SpecializedPartialSpecialization();
Expand All @@ -2009,8 +2000,6 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl,
/// Note that this class template specialization is an instantiation
/// of the given class template.
void setInstantiationOf(ClassTemplateDecl *TemplDecl) {
assert(!isa<ClassTemplatePartialSpecializationDecl>(this) &&
"A partial specialization cannot be instantiated from a template");
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization*>() &&
"Previously set to a class template partial specialization!");
SpecializedTemplate = TemplDecl;
Expand Down Expand Up @@ -2198,11 +2187,18 @@ class ClassTemplatePartialSpecializationDecl
/// struct X<int>::Inner<T*> { /* ... */ };
/// \endcode
bool isMemberSpecialization() const {
return InstantiatedFromMember.getInt();
const auto *First =
cast<ClassTemplatePartialSpecializationDecl>(getFirstDecl());
return First->InstantiatedFromMember.getInt();
}

/// Note that this member template is a specialization.
void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); }
void setMemberSpecialization() {
auto *First = cast<ClassTemplatePartialSpecializationDecl>(getFirstDecl());
assert(First->InstantiatedFromMember.getPointer() &&
"Only member templates can be member template specializations");
return First->InstantiatedFromMember.setInt(true);
}

/// Retrieves the injected specialization type for this partial
/// specialization. This is not the same as the type-decl-type for
Expand Down Expand Up @@ -2272,6 +2268,10 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
return static_cast<Common *>(RedeclarableTemplateDecl::getCommonPtr());
}

void setCommonPtr(Common *C) {
RedeclarableTemplateDecl::Common = C;
}

public:

friend class ASTDeclReader;
Expand Down Expand Up @@ -2754,8 +2754,6 @@ class VarTemplateSpecializationDecl : public VarDecl,
/// template arguments have been deduced.
void setInstantiationOf(VarTemplatePartialSpecializationDecl *PartialSpec,
const TemplateArgumentList *TemplateArgs) {
assert(!isa<VarTemplatePartialSpecializationDecl>(this) &&
"A partial specialization cannot be instantiated from a template");
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization *>() &&
"Already set to a variable template partial specialization!");
auto *PS = new (getASTContext()) SpecializedPartialSpecialization();
Expand All @@ -2767,8 +2765,6 @@ class VarTemplateSpecializationDecl : public VarDecl,
/// Note that this variable template specialization is an instantiation
/// of the given variable template.
void setInstantiationOf(VarTemplateDecl *TemplDecl) {
assert(!isa<VarTemplatePartialSpecializationDecl>(this) &&
"A partial specialization cannot be instantiated from a template");
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization *>() &&
"Previously set to a variable template partial specialization!");
SpecializedTemplate = TemplDecl;
Expand Down Expand Up @@ -2953,11 +2949,18 @@ class VarTemplatePartialSpecializationDecl
/// U* X<int>::Inner<T*> = (T*)(0) + 1;
/// \endcode
bool isMemberSpecialization() const {
return InstantiatedFromMember.getInt();
const auto *First =
cast<VarTemplatePartialSpecializationDecl>(getFirstDecl());
return First->InstantiatedFromMember.getInt();
}

/// Note that this member template is a specialization.
void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); }
void setMemberSpecialization() {
auto *First = cast<VarTemplatePartialSpecializationDecl>(getFirstDecl());
assert(First->InstantiatedFromMember.getPointer() &&
"Only member templates can be member template specializations");
return First->InstantiatedFromMember.setInt(true);
}

SourceRange getSourceRange() const override LLVM_READONLY;

Expand Down
99 changes: 99 additions & 0 deletions clang/include/clang/AST/OpenMPClause.h
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,105 @@ class OMPSizesClause final
}
};

/// This class represents the 'permutation' clause in the
/// '#pragma omp interchange' directive.
///
/// \code{.c}
/// #pragma omp interchange permutation(2,1)
/// for (int i = 0; i < 64; ++i)
/// for (int j = 0; j < 64; ++j)
/// \endcode
class OMPPermutationClause final
: public OMPClause,
private llvm::TrailingObjects<OMPSizesClause, Expr *> {
friend class OMPClauseReader;
friend class llvm::TrailingObjects<OMPSizesClause, Expr *>;

/// Location of '('.
SourceLocation LParenLoc;

/// Number of arguments in the clause, and hence also the number of loops to
/// be permuted.
unsigned NumLoops;

/// Sets the permutation index expressions.
void setArgRefs(ArrayRef<Expr *> VL) {
assert(VL.size() == NumLoops && "Expecting one expression per loop");
llvm::copy(VL, static_cast<OMPPermutationClause *>(this)
->template getTrailingObjects<Expr *>());
}

/// Build an empty clause.
explicit OMPPermutationClause(int NumLoops)
: OMPClause(llvm::omp::OMPC_permutation, SourceLocation(),
SourceLocation()),
NumLoops(NumLoops) {}

public:
/// Build a 'permutation' clause AST node.
///
/// \param C Context of the AST.
/// \param StartLoc Location of the 'permutation' identifier.
/// \param LParenLoc Location of '('.
/// \param EndLoc Location of ')'.
/// \param Args Content of the clause.
static OMPPermutationClause *
Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc,
SourceLocation EndLoc, ArrayRef<Expr *> Args);

/// Build an empty 'permutation' AST node for deserialization.
///
/// \param C Context of the AST.
/// \param NumLoops Number of arguments in the clause.
static OMPPermutationClause *CreateEmpty(const ASTContext &C,
unsigned NumLoops);

/// Sets the location of '('.
void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; }

/// Returns the location of '('.
SourceLocation getLParenLoc() const { return LParenLoc; }

/// Returns the number of list items.
unsigned getNumLoops() const { return NumLoops; }

/// Returns the permutation index expressions.
///@{
MutableArrayRef<Expr *> getArgsRefs() {
return MutableArrayRef<Expr *>(static_cast<OMPPermutationClause *>(this)
->template getTrailingObjects<Expr *>(),
NumLoops);
}
ArrayRef<Expr *> getArgsRefs() const {
return ArrayRef<Expr *>(static_cast<const OMPPermutationClause *>(this)
->template getTrailingObjects<Expr *>(),
NumLoops);
}
///@}

child_range children() {
MutableArrayRef<Expr *> Args = getArgsRefs();
return child_range(reinterpret_cast<Stmt **>(Args.begin()),
reinterpret_cast<Stmt **>(Args.end()));
}
const_child_range children() const {
ArrayRef<Expr *> Args = getArgsRefs();
return const_child_range(reinterpret_cast<Stmt *const *>(Args.begin()),
reinterpret_cast<Stmt *const *>(Args.end()));
}

child_range used_children() {
return child_range(child_iterator(), child_iterator());
}
const_child_range used_children() const {
return const_child_range(const_child_iterator(), const_child_iterator());
}

static bool classof(const OMPClause *T) {
return T->getClauseKind() == llvm::omp::OMPC_permutation;
}
};

/// Representation of the 'full' clause of the '#pragma omp unroll' directive.
///
/// \code
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -3348,6 +3348,14 @@ bool RecursiveASTVisitor<Derived>::VisitOMPSizesClause(OMPSizesClause *C) {
return true;
}

template <typename Derived>
bool RecursiveASTVisitor<Derived>::VisitOMPPermutationClause(
OMPPermutationClause *C) {
for (Expr *E : C->getArgsRefs())
TRY_TO(TraverseStmt(E));
return true;
}

template <typename Derived>
bool RecursiveASTVisitor<Derived>::VisitOMPFullClause(OMPFullClause *C) {
return true;
Expand Down
7 changes: 6 additions & 1 deletion clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,11 @@ class TargetSpecificAttr<TargetSpec target> {
/// redeclarations, even when it's written on a parameter.
class InheritableParamAttr : InheritableAttr;

/// A attribute that is either a declaration attribute or a statement attribute,
/// and if used as a declaration attribute, is inherited by later
/// redeclarations, even when it's written on a parameter.
class InheritableParamOrStmtAttr : InheritableParamAttr;

/// An attribute which changes the ABI rules for a specific parameter.
class ParameterABIAttr : InheritableParamAttr {
let Subjects = SubjectList<[ParmVar]>;
Expand Down Expand Up @@ -928,7 +933,7 @@ def AnalyzerNoReturn : InheritableAttr {
let Documentation = [Undocumented];
}

def Annotate : InheritableParamAttr {
def Annotate : InheritableParamOrStmtAttr {
let Spellings = [Clang<"annotate">];
let Args = [StringArgument<"Annotation">, VariadicExprArgument<"Args">];
// Ensure that the annotate attribute can be used with
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -4745,6 +4745,12 @@ def HLSLCross: LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}

def HLSLDegrees : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_elementwise_degrees"];
let Attributes = [NoThrow, Const];
let Prototype = "void(...)";
}

def HLSLDotProduct : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_dot"];
let Attributes = [NoThrow, Const];
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticDriverKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ def warn_drv_unsupported_option_for_processor : Warning<
def warn_drv_unsupported_openmp_library : Warning<
"the library '%0=%1' is not supported, OpenMP will not be enabled">,
InGroup<OptionIgnored>;
def warn_openmp_experimental : Warning<
"OpenMP support in flang is still experimental">,
InGroup<ExperimentalOption>;

def err_drv_invalid_thread_model_for_target : Error<
"invalid thread model '%0' in '%1' for this target">;
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -1583,3 +1583,7 @@ def ExtractAPIMisuse : DiagGroup<"extractapi-misuse">;
// Warnings about using the non-standard extension having an explicit specialization
// with a storage class specifier.
def ExplicitSpecializationStorageClass : DiagGroup<"explicit-specialization-storage-class">;

// A warning for options that enable a feature that is not yet complete
def ExperimentalOption : DiagGroup<"experimental-option">;

11 changes: 11 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -5262,6 +5262,13 @@ def note_template_arg_refers_here_func : Note<
def err_template_arg_template_params_mismatch : Error<
"template template argument has different template parameters than its "
"corresponding template template parameter">;
def note_template_arg_template_params_mismatch : Note<
"template template argument has different template parameters than its "
"corresponding template template parameter">;
def err_non_deduced_mismatch : Error<
"could not match %diff{$ against $|types}0,1">;
def err_inconsistent_deduction : Error<
"conflicting deduction %diff{$ against $|types}0,1 for parameter">;
def err_template_arg_not_integral_or_enumeral : Error<
"non-type template argument of type %0 must have an integral or enumeration"
" type">;
Expand Down Expand Up @@ -11702,6 +11709,10 @@ def err_omp_dispatch_statement_call
" to a target function or an assignment to one">;
def err_omp_unroll_full_variable_trip_count : Error<
"loop to be fully unrolled must have a constant trip count">;
def err_omp_interchange_permutation_value_range : Error<
"permutation index must be at least 1 and at most %0">;
def err_omp_interchange_permutation_value_repeated : Error<
"index %0 must appear exactly once in the permutation clause">;
def note_omp_directive_here : Note<"'%0' directive found here">;
def err_omp_instantiation_not_supported
: Error<"instantiation of '%0' not supported yet">;
Expand Down
60 changes: 60 additions & 0 deletions clang/include/clang/CIR/CIRGenerator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//===- CIRGenerator.h - CIR Generation from Clang AST ---------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file declares a simple interface to perform CIR generation from Clang
// AST
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_CIR_CIRGENERATOR_H
#define LLVM_CLANG_CIR_CIRGENERATOR_H

#include "clang/AST/ASTConsumer.h"
#include "clang/Basic/CodeGenOptions.h"

#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/Support/VirtualFileSystem.h"

#include <memory>

namespace clang {
class DeclGroupRef;
class DiagnosticsEngine;
} // namespace clang

namespace mlir {
class MLIRContext;
} // namespace mlir
namespace cir {
class CIRGenModule;

class CIRGenerator : public clang::ASTConsumer {
virtual void anchor();
clang::DiagnosticsEngine &diags;
clang::ASTContext *astCtx;
// Only used for debug info.
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs;

const clang::CodeGenOptions &codeGenOpts;

protected:
std::unique_ptr<mlir::MLIRContext> mlirCtx;
std::unique_ptr<CIRGenModule> cgm;

public:
CIRGenerator(clang::DiagnosticsEngine &diags,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
const clang::CodeGenOptions &cgo);
~CIRGenerator() override;
void Initialize(clang::ASTContext &astCtx) override;
bool HandleTopLevelDecl(clang::DeclGroupRef group) override;
};

} // namespace cir

#endif // LLVM_CLANG_CIR_CIRGENERATOR_H
60 changes: 60 additions & 0 deletions clang/include/clang/CIR/FrontendAction/CIRGenAction.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//===---- CIRGenAction.h - CIR Code Generation Frontend Action -*- 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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_CIR_CIRGENACTION_H
#define LLVM_CLANG_CIR_CIRGENACTION_H

#include "clang/Frontend/FrontendAction.h"

#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/OwningOpRef.h"

namespace mlir {
class MLIRContext;
class ModuleOp;
} // namespace mlir

namespace cir {
class CIRGenConsumer;

class CIRGenAction : public clang::ASTFrontendAction {
public:
enum class OutputType {
EmitCIR,
};

private:
friend class CIRGenConsumer;

mlir::OwningOpRef<mlir::ModuleOp> MLIRMod;

mlir::MLIRContext *MLIRCtx;

protected:
CIRGenAction(OutputType Action, mlir::MLIRContext *MLIRCtx = nullptr);

std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance &CI,
llvm::StringRef InFile) override;

public:
~CIRGenAction() override;

OutputType Action;
};

class EmitCIRAction : public CIRGenAction {
virtual void anchor();

public:
EmitCIRAction(mlir::MLIRContext *MLIRCtx = nullptr);
};

} // namespace cir

#endif
6 changes: 3 additions & 3 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -2996,7 +2996,7 @@ defm clangir : BoolFOption<"clangir",
PosFlag<SetTrue, [], [ClangOption, CC1Option], "Use the ClangIR pipeline to compile">,
NegFlag<SetFalse, [], [ClangOption, CC1Option], "Use the AST -> LLVM pipeline to compile">,
BothFlags<[], [ClangOption, CC1Option], "">>;
def emit_cir : Flag<["-"], "emit-cir">, Visibility<[CC1Option]>,
def emit_cir : Flag<["-"], "emit-cir">, Visibility<[ClangOption, CC1Option]>,
Group<Action_Group>, HelpText<"Build ASTs and then lower to ClangIR">;
/// ClangIR-specific options - END

Expand Down Expand Up @@ -6077,7 +6077,7 @@ def _sysroot_EQ : Joined<["--"], "sysroot=">, Visibility<[ClangOption, FlangOpti
def _sysroot : Separate<["--"], "sysroot">, Alias<_sysroot_EQ>;

//===----------------------------------------------------------------------===//
// pie/pic options (clang + flang-new)
// pie/pic options (clang + flang)
//===----------------------------------------------------------------------===//
let Visibility = [ClangOption, FlangOption] in {

Expand All @@ -6093,7 +6093,7 @@ def fno_pie : Flag<["-"], "fno-pie">, Group<f_Group>;
} // let Vis = [Default, FlangOption]

//===----------------------------------------------------------------------===//
// Target Options (clang + flang-new)
// Target Options (clang + flang)
//===----------------------------------------------------------------------===//
let Flags = [TargetSpecific] in {
let Visibility = [ClangOption, FlangOption] in {
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -3595,6 +3595,9 @@ class Parser : public CodeCompletionHandler {
/// Parses the 'sizes' clause of a '#pragma omp tile' directive.
OMPClause *ParseOpenMPSizesClause();

/// Parses the 'permutation' clause of a '#pragma omp interchange' directive.
OMPClause *ParseOpenMPPermutationClause();

/// Parses clause without any additional arguments.
///
/// \param Kind Kind of current clause.
Expand Down
10 changes: 8 additions & 2 deletions clang/include/clang/Sema/Overload.h
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,11 @@ class Sema;

bool TookAddressOfOverload : 1;

/// Have we matched any packs on the parameter side, versus any non-packs on
/// the argument side, in a context where the opposite matching is also
/// allowed?
bool HasMatchedPackOnParmToNonPackOnArg : 1;

/// True if the candidate was found using ADL.
CallExpr::ADLCallKind IsADLCandidate : 1;

Expand Down Expand Up @@ -999,8 +1004,9 @@ class Sema;
friend class OverloadCandidateSet;
OverloadCandidate()
: IsSurrogate(false), IgnoreObjectArgument(false),
TookAddressOfOverload(false), IsADLCandidate(CallExpr::NotADL),
RewriteKind(CRK_None) {}
TookAddressOfOverload(false),
HasMatchedPackOnParmToNonPackOnArg(false),
IsADLCandidate(CallExpr::NotADL), RewriteKind(CRK_None) {}
};

/// OverloadCandidateSet - A set of overload candidates, used in C++
Expand Down
88 changes: 58 additions & 30 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -4453,9 +4453,10 @@ class Sema final : public SemaBase {
SourceLocation *ArgLocation = nullptr);

/// Determine if type T is a valid subject for a nonnull and similar
/// attributes. By default, we look through references (the behavior used by
/// nonnull), but if the second parameter is true, then we treat a reference
/// type as valid.
/// attributes. Dependent types are considered valid so they can be checked
/// during instantiation time. By default, we look through references (the
/// behavior used by nonnull), but if the second parameter is true, then we
/// treat a reference type as valid.
bool isValidPointerAttrType(QualType T, bool RefOkay = false);

/// AddAssumeAlignedAttr - Adds an assume_aligned attribute to a particular
Expand Down Expand Up @@ -4527,9 +4528,10 @@ class Sema final : public SemaBase {
/// declaration.
void AddAlignValueAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E);

/// AddAnnotationAttr - Adds an annotation Annot with Args arguments to D.
void AddAnnotationAttr(Decl *D, const AttributeCommonInfo &CI,
StringRef Annot, MutableArrayRef<Expr *> Args);
/// CreateAnnotationAttr - Creates an annotation Annot with Args arguments.
Attr *CreateAnnotationAttr(const AttributeCommonInfo &CI, StringRef Annot,
MutableArrayRef<Expr *> Args);
Attr *CreateAnnotationAttr(const ParsedAttr &AL);

bool checkMSInheritanceAttrOnDefinition(CXXRecordDecl *RD, SourceRange Range,
bool BestCase,
Expand Down Expand Up @@ -10132,7 +10134,8 @@ class Sema final : public SemaBase {
ADLCallKind IsADLCandidate = ADLCallKind::NotADL,
ConversionSequenceList EarlyConversions = std::nullopt,
OverloadCandidateParamOrder PO = {},
bool AggregateCandidateDeduction = false);
bool AggregateCandidateDeduction = false,
bool HasMatchedPackOnParmToNonPackOnArg = false);

/// Add all of the function declarations in the given function set to
/// the overload candidate set.
Expand Down Expand Up @@ -10167,7 +10170,8 @@ class Sema final : public SemaBase {
bool SuppressUserConversions = false,
bool PartialOverloading = false,
ConversionSequenceList EarlyConversions = std::nullopt,
OverloadCandidateParamOrder PO = {});
OverloadCandidateParamOrder PO = {},
bool HasMatchedPackOnParmToNonPackOnArg = false);

/// Add a C++ member function template as a candidate to the candidate
/// set, using template argument deduction to produce an appropriate member
Expand Down Expand Up @@ -10213,7 +10217,8 @@ class Sema final : public SemaBase {
CXXConversionDecl *Conversion, DeclAccessPair FoundDecl,
CXXRecordDecl *ActingContext, Expr *From, QualType ToType,
OverloadCandidateSet &CandidateSet, bool AllowObjCConversionOnExplicit,
bool AllowExplicit, bool AllowResultConversion = true);
bool AllowExplicit, bool AllowResultConversion = true,
bool HasMatchedPackOnParmToNonPackOnArg = false);

/// Adds a conversion function template specialization
/// candidate to the overload set, using template argument deduction
Expand Down Expand Up @@ -11325,9 +11330,9 @@ class Sema final : public SemaBase {
CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc,
const ParsedAttributesView &Attr, TemplateParameterList *TemplateParams,
AccessSpecifier AS, SourceLocation ModulePrivateLoc,
SourceLocation FriendLoc,
ArrayRef<TemplateParameterList *> OuterTemplateParamLists,
bool IsMemberSpecialization, SkipBodyInfo *SkipBody = nullptr);
SourceLocation FriendLoc, unsigned NumOuterTemplateParamLists,
TemplateParameterList **OuterTemplateParamLists,
SkipBodyInfo *SkipBody = nullptr);

/// Translates template arguments as provided by the parser
/// into template arguments used by semantic analysis.
Expand Down Expand Up @@ -11366,8 +11371,7 @@ class Sema final : public SemaBase {
DeclResult ActOnVarTemplateSpecialization(
Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous,
SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams,
StorageClass SC, bool IsPartialSpecialization,
bool IsMemberSpecialization);
StorageClass SC, bool IsPartialSpecialization);

/// Get the specialization of the given variable template corresponding to
/// the specified argument list, or a null-but-valid result if the arguments
Expand Down Expand Up @@ -11637,7 +11641,8 @@ class Sema final : public SemaBase {
SourceLocation RAngleLoc, unsigned ArgumentPackIndex,
SmallVectorImpl<TemplateArgument> &SugaredConverted,
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
CheckTemplateArgumentKind CTAK);
CheckTemplateArgumentKind CTAK, bool PartialOrdering,
bool *MatchedPackOnParmToNonPackOnArg);

/// Check that the given template arguments can be provided to
/// the given template, converting the arguments along the way.
Expand Down Expand Up @@ -11684,7 +11689,8 @@ class Sema final : public SemaBase {
SmallVectorImpl<TemplateArgument> &SugaredConverted,
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
bool UpdateArgsWithConversions = true,
bool *ConstraintsNotSatisfied = nullptr, bool PartialOrderingTTP = false);
bool *ConstraintsNotSatisfied = nullptr, bool PartialOrderingTTP = false,
bool *MatchedPackOnParmToNonPackOnArg = nullptr);

bool CheckTemplateTypeArgument(
TemplateTypeParmDecl *Param, TemplateArgumentLoc &Arg,
Expand Down Expand Up @@ -11718,7 +11724,9 @@ class Sema final : public SemaBase {
/// It returns true if an error occurred, and false otherwise.
bool CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
TemplateParameterList *Params,
TemplateArgumentLoc &Arg, bool IsDeduced);
TemplateArgumentLoc &Arg,
bool PartialOrdering,
bool *MatchedPackOnParmToNonPackOnArg);

void NoteTemplateLocation(const NamedDecl &Decl,
std::optional<SourceRange> ParamRange = {});
Expand Down Expand Up @@ -12229,8 +12237,8 @@ class Sema final : public SemaBase {
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
unsigned NumExplicitlySpecified, FunctionDecl *&Specialization,
sema::TemplateDeductionInfo &Info,
SmallVectorImpl<OriginalCallArg> const *OriginalCallArgs = nullptr,
bool PartialOverloading = false,
SmallVectorImpl<OriginalCallArg> const *OriginalCallArgs,
bool PartialOverloading, bool PartialOrdering,
llvm::function_ref<bool()> CheckNonDependent = [] { return false; });

/// Perform template argument deduction from a function call
Expand Down Expand Up @@ -12264,7 +12272,8 @@ class Sema final : public SemaBase {
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
FunctionDecl *&Specialization, sema::TemplateDeductionInfo &Info,
bool PartialOverloading, bool AggregateDeductionCandidate,
QualType ObjectType, Expr::Classification ObjectClassification,
bool PartialOrdering, QualType ObjectType,
Expr::Classification ObjectClassification,
llvm::function_ref<bool(ArrayRef<QualType>)> CheckNonDependent);

/// Deduce template arguments when taking the address of a function
Expand Down Expand Up @@ -12417,8 +12426,9 @@ class Sema final : public SemaBase {
sema::TemplateDeductionInfo &Info);

bool isTemplateTemplateParameterAtLeastAsSpecializedAs(
TemplateParameterList *PParam, TemplateDecl *AArg,
const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced);
TemplateParameterList *PParam, TemplateDecl *PArg, TemplateDecl *AArg,
const DefaultArguments &DefaultArgs, SourceLocation ArgLoc,
bool PartialOrdering, bool *MatchedPackOnParmToNonPackOnArg);

/// Mark which template parameters are used in a given expression.
///
Expand Down Expand Up @@ -12727,6 +12737,9 @@ class Sema final : public SemaBase {

/// We are instantiating a type alias template declaration.
TypeAliasTemplateInstantiation,

/// We are performing partial ordering for template template parameters.
PartialOrderingTTP,
} Kind;

/// Was the enclosing context a non-instantiation SFINAE context?
Expand Down Expand Up @@ -12948,6 +12961,12 @@ class Sema final : public SemaBase {
TemplateDecl *Entity, BuildingDeductionGuidesTag,
SourceRange InstantiationRange = SourceRange());

struct PartialOrderingTTP {};
/// \brief Note that we are partial ordering template template parameters.
InstantiatingTemplate(Sema &SemaRef, SourceLocation ArgLoc,
PartialOrderingTTP, TemplateDecl *PArg,
SourceRange InstantiationRange = SourceRange());

/// Note that we have finished instantiating this template.
void Clear();

Expand Down Expand Up @@ -13008,20 +13027,28 @@ class Sema final : public SemaBase {
/// dealing with a specialization. This is only relevant for function
/// template specializations.
///
/// \param Pattern If non-NULL, indicates the pattern from which we will be
/// instantiating the definition of the given declaration, \p ND. This is
/// used to determine the proper set of template instantiation arguments for
/// friend function template specializations.
///
/// \param ForConstraintInstantiation when collecting arguments,
/// ForConstraintInstantiation indicates we should continue looking when
/// encountering a lambda generic call operator, and continue looking for
/// arguments on an enclosing class template.
///
/// \param SkipForSpecialization when specified, any template specializations
/// in a traversal would be ignored.
/// \param ForDefaultArgumentSubstitution indicates we should continue looking
/// when encountering a specialized member function template, rather than
/// returning immediately.
MultiLevelTemplateArgumentList getTemplateInstantiationArgs(
const NamedDecl *D, const DeclContext *DC = nullptr, bool Final = false,
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
bool RelativeToPrimary = false, bool ForConstraintInstantiation = false);

void getTemplateInstantiationArgs(
MultiLevelTemplateArgumentList &Result, const NamedDecl *D,
const DeclContext *DC = nullptr, bool Final = false,
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
bool RelativeToPrimary = false, bool ForConstraintInstantiation = false);
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr,
bool ForConstraintInstantiation = false,
bool SkipForSpecialization = false,
bool ForDefaultArgumentSubstitution = false);

/// RAII object to handle the state changes required to synthesize
/// a function body.
Expand Down Expand Up @@ -13400,7 +13427,8 @@ class Sema final : public SemaBase {
bool InstantiateClassTemplateSpecialization(
SourceLocation PointOfInstantiation,
ClassTemplateSpecializationDecl *ClassTemplateSpec,
TemplateSpecializationKind TSK, bool Complain = true);
TemplateSpecializationKind TSK, bool Complain = true,
bool PrimaryHasMatchedPackOnParmToNonPackOnArg = false);

/// Instantiates the definitions of all of the member
/// of the given class, which is an instantiation of a class template
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Sema/SemaOpenMP.h
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,11 @@ class SemaOpenMP : public SemaBase {
SourceLocation StartLoc,
SourceLocation LParenLoc,
SourceLocation EndLoc);
/// Called on well-form 'permutation' clause after parsing its arguments.
OMPClause *ActOnOpenMPPermutationClause(ArrayRef<Expr *> PermExprs,
SourceLocation StartLoc,
SourceLocation LParenLoc,
SourceLocation EndLoc);
/// Called on well-form 'full' clauses.
OMPClause *ActOnOpenMPFullClause(SourceLocation StartLoc,
SourceLocation EndLoc);
Expand Down
13 changes: 13 additions & 0 deletions clang/include/clang/Sema/TemplateDeduction.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ class TemplateDeductionInfo {
/// Have we suppressed an error during deduction?
bool HasSFINAEDiagnostic = false;

/// Have we matched any packs on the parameter side, versus any non-packs on
/// the argument side, in a context where the opposite matching is also
/// allowed?
bool MatchedPackOnParmToNonPackOnArg = false;

/// The template parameter depth for which we're performing deduction.
unsigned DeducedDepth;

Expand Down Expand Up @@ -87,6 +92,14 @@ class TemplateDeductionInfo {
return DeducedDepth;
}

bool hasMatchedPackOnParmToNonPackOnArg() const {
return MatchedPackOnParmToNonPackOnArg;
}

void setMatchedPackOnParmToNonPackOnArg() {
MatchedPackOnParmToNonPackOnArg = true;
}

/// Get the number of explicitly-specified arguments.
unsigned getNumExplicitArgs() const {
return ExplicitArgs;
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Serialization/ASTReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -2527,7 +2527,7 @@ class BitsUnpacker {

inline bool shouldSkipCheckingODR(const Decl *D) {
return D->getASTContext().getLangOpts().SkipODRCheckInGMF &&
D->isFromGlobalModule();
(D->isFromGlobalModule() || D->isFromHeaderUnit());
}

} // namespace clang
Expand Down
32 changes: 21 additions & 11 deletions clang/lib/AST/ByteCode/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2728,7 +2728,7 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(

const Expr *Inner = E->getSubExpr()->skipRValueSubobjectAdjustments();
if (std::optional<unsigned> LocalIndex =
allocateLocal(Inner, E->getExtendingDecl())) {
allocateLocal(E, Inner->getType(), E->getExtendingDecl())) {
InitLinkScope<Emitter> ILS(this, InitLink::Temp(*LocalIndex));
if (!this->emitGetPtrLocal(*LocalIndex, E))
return false;
Expand Down Expand Up @@ -2869,6 +2869,11 @@ bool Compiler<Emitter>::VisitPredefinedExpr(const PredefinedExpr *E) {
if (DiscardResult)
return true;

if (!Initializing) {
unsigned StringIndex = P.createGlobalString(E->getFunctionName(), E);
return this->emitGetPtrGlobal(StringIndex, E);
}

return this->delegate(E->getFunctionName());
}

Expand Down Expand Up @@ -3406,7 +3411,7 @@ bool Compiler<Emitter>::VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
if (!this->visit(Arg))
return false;

return this->emitFree(E->isArrayForm(), E);
return this->emitFree(E->isArrayForm(), E->isGlobalDelete(), E);
}

template <class Emitter>
Expand Down Expand Up @@ -4029,15 +4034,15 @@ unsigned Compiler<Emitter>::allocateLocalPrimitive(DeclTy &&Src, PrimType Ty,

template <class Emitter>
std::optional<unsigned>
Compiler<Emitter>::allocateLocal(DeclTy &&Src, const ValueDecl *ExtendingDecl) {
Compiler<Emitter>::allocateLocal(DeclTy &&Src, QualType Ty,
const ValueDecl *ExtendingDecl) {
// Make sure we don't accidentally register the same decl twice.
if ([[maybe_unused]] const auto *VD =
dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>())) {
assert(!P.getGlobal(VD));
assert(!Locals.contains(VD));
}

QualType Ty;
const ValueDecl *Key = nullptr;
const Expr *Init = nullptr;
bool IsTemporary = false;
Expand All @@ -4050,7 +4055,8 @@ Compiler<Emitter>::allocateLocal(DeclTy &&Src, const ValueDecl *ExtendingDecl) {
}
if (auto *E = Src.dyn_cast<const Expr *>()) {
IsTemporary = true;
Ty = E->getType();
if (Ty.isNull())
Ty = E->getType();
}

Descriptor *D = P.createDescriptor(
Expand Down Expand Up @@ -6005,6 +6011,9 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {

return this->emitGetPtrParam(It->second.Offset, E);
}

if (D->getType()->isReferenceType())
return false; // FIXME: Do we need to emit InvalidDeclRef?
}

// In case we need to re-visit a declaration.
Expand Down Expand Up @@ -6041,9 +6050,7 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
const auto typeShouldBeVisited = [&](QualType T) -> bool {
if (T.isConstant(Ctx.getASTContext()))
return true;
if (const auto *RT = T->getAs<ReferenceType>())
return RT->getPointeeType().isConstQualified();
return false;
return T->isReferenceType();
};

// DecompositionDecls are just proxies for us.
Expand All @@ -6059,9 +6066,12 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
// other words, we're evaluating the initializer, just to know if we can
// evaluate the initializer.
if (VD->isLocalVarDecl() && typeShouldBeVisited(VD->getType()) &&
VD->getInit() && !VD->getInit()->isValueDependent() &&
VD->evaluateValue())
return revisit(VD);
VD->getInit() && !VD->getInit()->isValueDependent()) {

if (VD->evaluateValue())
return revisit(VD);
return this->emitInvalidDeclRef(cast<DeclRefExpr>(E), E);
}
}
} else {
if (const auto *VD = dyn_cast<VarDecl>(D);
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/AST/ByteCode/Compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,8 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,

/// Allocates a space storing a local given its type.
std::optional<unsigned>
allocateLocal(DeclTy &&Decl, const ValueDecl *ExtendingDecl = nullptr);
allocateLocal(DeclTy &&Decl, QualType Ty = QualType(),
const ValueDecl *ExtendingDecl = nullptr);
unsigned allocateTemporary(const Expr *E);

private:
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/AST/ByteCode/EvaluationResult.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,9 @@ static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc,
const Descriptor *Desc = BasePtr.getDeclDesc();
if (const auto *CD = dyn_cast_if_present<CXXRecordDecl>(R->getDecl())) {
const auto &BS = *std::next(CD->bases_begin(), I);
S.FFDiag(BS.getBaseTypeLoc(), diag::note_constexpr_uninitialized_base)
<< B.Desc->getType() << BS.getSourceRange();
SourceLocation TypeBeginLoc = BS.getBaseTypeLoc();
S.FFDiag(TypeBeginLoc, diag::note_constexpr_uninitialized_base)
<< B.Desc->getType() << SourceRange(TypeBeginLoc, BS.getEndLoc());
} else {
S.FFDiag(Desc->getLocation(), diag::note_constexpr_uninitialized_base)
<< B.Desc->getType();
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/ByteCode/Function.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ class Function final {
return ParamOffsets[ParamIndex];
}

PrimType getParamType(unsigned ParamIndex) const {
return ParamTypes[ParamIndex];
}

private:
/// Construct a function representing an actual function.
Function(Program &P, FunctionDeclTy Source, unsigned ArgSize,
Expand Down
126 changes: 125 additions & 1 deletion clang/lib/AST/ByteCode/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/StringExtras.h"
#include <limits>
Expand Down Expand Up @@ -963,7 +964,7 @@ static bool runRecordDestructor(InterpState &S, CodePtr OpPC,
return true;
}

bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) {
static bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) {
assert(B);
const Descriptor *Desc = B->getDescriptor();

Expand All @@ -988,6 +989,89 @@ bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) {
return runRecordDestructor(S, OpPC, Pointer(const_cast<Block *>(B)), Desc);
}

bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm,
bool IsGlobalDelete) {
if (!CheckDynamicMemoryAllocation(S, OpPC))
return false;

const Expr *Source = nullptr;
const Block *BlockToDelete = nullptr;
{
// Extra scope for this so the block doesn't have this pointer
// pointing to it when we destroy it.
Pointer Ptr = S.Stk.pop<Pointer>();

// Deleteing nullptr is always fine.
if (Ptr.isZero())
return true;

// Remove base casts.
while (Ptr.isBaseClass())
Ptr = Ptr.getBase();

if (!Ptr.isRoot() || Ptr.isOnePastEnd() || Ptr.isArrayElement()) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_delete_subobject)
<< Ptr.toDiagnosticString(S.getASTContext()) << Ptr.isOnePastEnd();
return false;
}

Source = Ptr.getDeclDesc()->asExpr();
BlockToDelete = Ptr.block();

if (!CheckDeleteSource(S, OpPC, Source, Ptr))
return false;

// For a class type with a virtual destructor, the selected operator delete
// is the one looked up when building the destructor.
QualType AllocType = Ptr.getType();
if (!DeleteIsArrayForm && !IsGlobalDelete) {
auto getVirtualOperatorDelete = [](QualType T) -> const FunctionDecl * {
if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
if (const CXXDestructorDecl *DD = RD->getDestructor())
return DD->isVirtual() ? DD->getOperatorDelete() : nullptr;
return nullptr;
};

AllocType->dump();
if (const FunctionDecl *VirtualDelete =
getVirtualOperatorDelete(AllocType);
VirtualDelete &&
!VirtualDelete->isReplaceableGlobalAllocationFunction()) {
S.FFDiag(S.Current->getSource(OpPC),
diag::note_constexpr_new_non_replaceable)
<< isa<CXXMethodDecl>(VirtualDelete) << VirtualDelete;
return false;
}
}
}
assert(Source);
assert(BlockToDelete);

// Invoke destructors before deallocating the memory.
if (!RunDestructors(S, OpPC, BlockToDelete))
return false;

DynamicAllocator &Allocator = S.getAllocator();
const Descriptor *BlockDesc = BlockToDelete->getDescriptor();
std::optional<DynamicAllocator::Form> AllocForm =
Allocator.getAllocationForm(Source);

if (!Allocator.deallocate(Source, BlockToDelete, S)) {
// Nothing has been deallocated, this must be a double-delete.
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_double_delete);
return false;
}

assert(AllocForm);
DynamicAllocator::Form DeleteForm = DeleteIsArrayForm
? DynamicAllocator::Form::Array
: DynamicAllocator::Form::NonArray;
return CheckNewDeleteForms(S, OpPC, *AllocForm, DeleteForm, BlockDesc,
Source);
}

void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED,
const APSInt &Value) {
llvm::APInt Min;
Expand Down Expand Up @@ -1415,6 +1499,46 @@ bool InvalidShuffleVectorIndex(InterpState &S, CodePtr OpPC, uint32_t Index) {
return false;
}

bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC,
const Pointer &Ptr, unsigned BitWidth) {
if (Ptr.isDummy())
return false;

const SourceInfo &E = S.Current->getSource(OpPC);
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
<< 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);

if (Ptr.isBlockPointer() && !Ptr.isZero()) {
// Only allow based lvalue casts if they are lossless.
if (S.getASTContext().getTargetInfo().getPointerWidth(LangAS::Default) !=
BitWidth)
return Invalid(S, OpPC);
}
return true;
}

bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (!CheckPointerToIntegralCast(S, OpPC, Ptr, BitWidth))
return false;

S.Stk.push<IntegralAP<false>>(
IntegralAP<false>::from(Ptr.getIntegerRepresentation(), BitWidth));
return true;
}

bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (!CheckPointerToIntegralCast(S, OpPC, Ptr, BitWidth))
return false;

S.Stk.push<IntegralAP<true>>(
IntegralAP<true>::from(Ptr.getIntegerRepresentation(), BitWidth));
return true;
}

// https://github.com/llvm/llvm-project/issues/102513
#if defined(_WIN32) && !defined(__clang__) && !defined(NDEBUG)
#pragma optimize("", off)
Expand Down
100 changes: 8 additions & 92 deletions clang/lib/AST/ByteCode/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -2289,53 +2289,22 @@ static inline bool CastFloatingIntegralAPS(InterpState &S, CodePtr OpPC,
return CheckFloatResult(S, OpPC, F, Status, FPO);
}

bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC,
const Pointer &Ptr, unsigned BitWidth);
bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth);
bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth);

template <PrimType Name, class T = typename PrimConv<Name>::T>
bool CastPointerIntegral(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (Ptr.isDummy())
if (!CheckPointerToIntegralCast(S, OpPC, Ptr, T::bitWidth()))
return false;

const SourceInfo &E = S.Current->getSource(OpPC);
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
<< 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);

S.Stk.push<T>(T::from(Ptr.getIntegerRepresentation()));
return true;
}

static inline bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC,
uint32_t BitWidth) {
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (Ptr.isDummy())
return false;

const SourceInfo &E = S.Current->getSource(OpPC);
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
<< 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);

S.Stk.push<IntegralAP<false>>(
IntegralAP<false>::from(Ptr.getIntegerRepresentation(), BitWidth));
return true;
}

static inline bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC,
uint32_t BitWidth) {
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (Ptr.isDummy())
return false;

const SourceInfo &E = S.Current->getSource(OpPC);
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
<< 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);

S.Stk.push<IntegralAP<true>>(
IntegralAP<true>::from(Ptr.getIntegerRepresentation(), BitWidth));
return true;
}

template <PrimType Name, class T = typename PrimConv<Name>::T>
static inline bool CastIntegralFixedPoint(InterpState &S, CodePtr OpPC,
uint32_t FPS) {
Expand Down Expand Up @@ -3007,61 +2976,8 @@ inline bool AllocCN(InterpState &S, CodePtr OpPC, const Descriptor *ElementDesc,
return true;
}

bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B);
static inline bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm) {
if (!CheckDynamicMemoryAllocation(S, OpPC))
return false;

const Expr *Source = nullptr;
const Block *BlockToDelete = nullptr;
{
// Extra scope for this so the block doesn't have this pointer
// pointing to it when we destroy it.
const Pointer &Ptr = S.Stk.pop<Pointer>();

// Deleteing nullptr is always fine.
if (Ptr.isZero())
return true;

if (!Ptr.isRoot() || Ptr.isOnePastEnd() || Ptr.isArrayElement()) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_delete_subobject)
<< Ptr.toDiagnosticString(S.getASTContext()) << Ptr.isOnePastEnd();
return false;
}

Source = Ptr.getDeclDesc()->asExpr();
BlockToDelete = Ptr.block();

if (!CheckDeleteSource(S, OpPC, Source, Ptr))
return false;
}
assert(Source);
assert(BlockToDelete);

// Invoke destructors before deallocating the memory.
if (!RunDestructors(S, OpPC, BlockToDelete))
return false;

DynamicAllocator &Allocator = S.getAllocator();
const Descriptor *BlockDesc = BlockToDelete->getDescriptor();
std::optional<DynamicAllocator::Form> AllocForm =
Allocator.getAllocationForm(Source);

if (!Allocator.deallocate(Source, BlockToDelete, S)) {
// Nothing has been deallocated, this must be a double-delete.
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_double_delete);
return false;
}

assert(AllocForm);
DynamicAllocator::Form DeleteForm = DeleteIsArrayForm
? DynamicAllocator::Form::Array
: DynamicAllocator::Form::NonArray;
return CheckNewDeleteForms(S, OpPC, *AllocForm, DeleteForm, BlockDesc,
Source);
}
bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm,
bool IsGlobalDelete);

static inline bool IsConstantContext(InterpState &S, CodePtr OpPC) {
S.Stk.push<Boolean>(Boolean::from(S.inConstantContext()));
Expand Down
55 changes: 55 additions & 0 deletions clang/lib/AST/ByteCode/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ static T getParam(const InterpFrame *Frame, unsigned Index) {
return Frame->getParam<T>(Offset);
}

// static APSInt getAPSIntParam(InterpStack &Stk, size_t Offset = 0) {
static APSInt getAPSIntParam(const InterpFrame *Frame, unsigned Index) {
APSInt R;
unsigned Offset = Frame->getFunction()->getParamOffset(Index);
INT_TYPE_SWITCH(Frame->getFunction()->getParamType(Index),
R = Frame->getParam<T>(Offset).toAPSInt());
return R;
}

PrimType getIntPrimType(const InterpState &S) {
const TargetInfo &TI = S.getASTContext().getTargetInfo();
unsigned IntWidth = TI.getIntWidth();
Expand Down Expand Up @@ -1273,6 +1282,44 @@ static bool interp__builtin_ia32_pext(InterpState &S, CodePtr OpPC,
return true;
}

static bool interp__builtin_ia32_addcarry_subborrow(InterpState &S,
CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
if (Call->getNumArgs() != 4 || !Call->getArg(0)->getType()->isIntegerType() ||
!Call->getArg(1)->getType()->isIntegerType() ||
!Call->getArg(2)->getType()->isIntegerType())
return false;

unsigned BuiltinOp = Func->getBuiltinID();
APSInt CarryIn = getAPSIntParam(Frame, 0);
APSInt LHS = getAPSIntParam(Frame, 1);
APSInt RHS = getAPSIntParam(Frame, 2);

bool IsAdd = BuiltinOp == clang::X86::BI__builtin_ia32_addcarryx_u32 ||
BuiltinOp == clang::X86::BI__builtin_ia32_addcarryx_u64;

unsigned BitWidth = LHS.getBitWidth();
unsigned CarryInBit = CarryIn.ugt(0) ? 1 : 0;
APInt ExResult =
IsAdd ? (LHS.zext(BitWidth + 1) + (RHS.zext(BitWidth + 1) + CarryInBit))
: (LHS.zext(BitWidth + 1) - (RHS.zext(BitWidth + 1) + CarryInBit));

APInt Result = ExResult.extractBits(BitWidth, 0);
APSInt CarryOut =
APSInt(ExResult.extractBits(1, BitWidth), /*IsUnsigned=*/true);

Pointer &CarryOutPtr = S.Stk.peek<Pointer>();
QualType CarryOutType = Call->getArg(3)->getType()->getPointeeType();
PrimType CarryOutT = *S.getContext().classify(CarryOutType);
assignInteger(CarryOutPtr, CarryOutT, APSInt(Result, true));

pushInteger(S, CarryOut, Call->getType());

return true;
}

static bool interp__builtin_os_log_format_buffer_size(InterpState &S,
CodePtr OpPC,
const InterpFrame *Frame,
Expand Down Expand Up @@ -1898,6 +1945,14 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
return false;
break;

case clang::X86::BI__builtin_ia32_addcarryx_u32:
case clang::X86::BI__builtin_ia32_addcarryx_u64:
case clang::X86::BI__builtin_ia32_subborrow_u32:
case clang::X86::BI__builtin_ia32_subborrow_u64:
if (!interp__builtin_ia32_addcarry_subborrow(S, OpPC, Frame, F, Call))
return false;
break;

case Builtin::BI__builtin_os_log_format_buffer_size:
if (!interp__builtin_os_log_format_buffer_size(S, OpPC, Frame, F, Call))
return false;
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/ByteCode/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ def AllocCN : Opcode {
}

def Free : Opcode {
let Args = [ArgBool];
let Args = [ArgBool, ArgBool];
}

def CheckNewTypeMismatch : Opcode {
Expand Down
5 changes: 0 additions & 5 deletions clang/lib/AST/ByteCode/Pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,6 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const {
}
}

// FIXME(perf): We compute the lvalue path above, but we can't supply it
// for dummy pointers (that causes crashes later in CheckConstantExpression).
if (isDummy())
Path.clear();

// We assemble the LValuePath starting from the innermost pointer to the
// outermost one. SO in a.b.c, the first element in Path will refer to
// the field 'c', while later code expects it to refer to 'a'.
Expand Down
15 changes: 9 additions & 6 deletions clang/lib/AST/ByteCode/Program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const void *Program::getNativePointer(unsigned Idx) {
return NativePointers[Idx];
}

unsigned Program::createGlobalString(const StringLiteral *S) {
unsigned Program::createGlobalString(const StringLiteral *S, const Expr *Base) {
const size_t CharWidth = S->getCharByteWidth();
const size_t BitWidth = CharWidth * Ctx.getCharBit();

Expand All @@ -52,12 +52,15 @@ unsigned Program::createGlobalString(const StringLiteral *S) {
llvm_unreachable("unsupported character width");
}

if (!Base)
Base = S;

// Create a descriptor for the string.
Descriptor *Desc =
allocateDescriptor(S, CharType, Descriptor::GlobalMD, S->getLength() + 1,
/*isConst=*/true,
/*isTemporary=*/false,
/*isMutable=*/false);
Descriptor *Desc = allocateDescriptor(Base, CharType, Descriptor::GlobalMD,
S->getLength() + 1,
/*isConst=*/true,
/*isTemporary=*/false,
/*isMutable=*/false);

// Allocate storage for the string.
// The byte length does not include the null terminator.
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/AST/ByteCode/Program.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ class Program final {
const void *getNativePointer(unsigned Idx);

/// Emits a string literal among global data.
unsigned createGlobalString(const StringLiteral *S);
unsigned createGlobalString(const StringLiteral *S,
const Expr *Base = nullptr);

/// Returns a pointer to a global.
Pointer getPtrGlobal(unsigned Idx) const;
Expand Down
50 changes: 19 additions & 31 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2696,27 +2696,21 @@ VarDecl *VarDecl::getTemplateInstantiationPattern() const {
if (isTemplateInstantiation(VDTemplSpec->getTemplateSpecializationKind())) {
auto From = VDTemplSpec->getInstantiatedFrom();
if (auto *VTD = From.dyn_cast<VarTemplateDecl *>()) {
while (true) {
VTD = VTD->getMostRecentDecl();
if (VTD->isMemberSpecialization())
break;
if (auto *NewVTD = VTD->getInstantiatedFromMemberTemplate())
VTD = NewVTD;
else
while (!VTD->isMemberSpecialization()) {
auto *NewVTD = VTD->getInstantiatedFromMemberTemplate();
if (!NewVTD)
break;
VTD = NewVTD;
}
return getDefinitionOrSelf(VTD->getTemplatedDecl());
}
if (auto *VTPSD =
From.dyn_cast<VarTemplatePartialSpecializationDecl *>()) {
while (true) {
VTPSD = VTPSD->getMostRecentDecl();
if (VTPSD->isMemberSpecialization())
break;
if (auto *NewVTPSD = VTPSD->getInstantiatedFromMember())
VTPSD = NewVTPSD;
else
while (!VTPSD->isMemberSpecialization()) {
auto *NewVTPSD = VTPSD->getInstantiatedFromMember();
if (!NewVTPSD)
break;
VTPSD = NewVTPSD;
}
return getDefinitionOrSelf<VarDecl>(VTPSD);
}
Expand All @@ -2725,17 +2719,15 @@ VarDecl *VarDecl::getTemplateInstantiationPattern() const {

// If this is the pattern of a variable template, find where it was
// instantiated from. FIXME: Is this necessary?
if (VarTemplateDecl *VTD = VD->getDescribedVarTemplate()) {
while (true) {
VTD = VTD->getMostRecentDecl();
if (VTD->isMemberSpecialization())
break;
if (auto *NewVTD = VTD->getInstantiatedFromMemberTemplate())
VTD = NewVTD;
else
if (VarTemplateDecl *VarTemplate = VD->getDescribedVarTemplate()) {
while (!VarTemplate->isMemberSpecialization()) {
auto *NewVT = VarTemplate->getInstantiatedFromMemberTemplate();
if (!NewVT)
break;
VarTemplate = NewVT;
}
return getDefinitionOrSelf(VTD->getTemplatedDecl());

return getDefinitionOrSelf(VarTemplate->getTemplatedDecl());
}

if (VD == this)
Expand Down Expand Up @@ -3067,7 +3059,6 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
FunctionDeclBits.IsIneligibleOrNotSelected = false;
FunctionDeclBits.HasImplicitReturnZero = false;
FunctionDeclBits.IsLateTemplateParsed = false;
FunctionDeclBits.IsInstantiatedFromMemberTemplate = false;
FunctionDeclBits.ConstexprKind = static_cast<uint64_t>(ConstexprKind);
FunctionDeclBits.BodyContainsImmediateEscalatingExpression = false;
FunctionDeclBits.InstantiationIsPending = false;
Expand Down Expand Up @@ -4151,14 +4142,11 @@ FunctionDecl::getTemplateInstantiationPattern(bool ForDefinition) const {
if (FunctionTemplateDecl *Primary = getPrimaryTemplate()) {
// If we hit a point where the user provided a specialization of this
// template, we're done looking.
while (true) {
Primary = Primary->getMostRecentDecl();
if (ForDefinition && Primary->isMemberSpecialization())
break;
if (auto *NewPrimary = Primary->getInstantiatedFromMemberTemplate())
Primary = NewPrimary;
else
while (!ForDefinition || !Primary->isMemberSpecialization()) {
auto *NewPrimary = Primary->getInstantiatedFromMemberTemplate();
if (!NewPrimary)
break;
Primary = NewPrimary;
}

return getDefinitionOrSelf(Primary->getTemplatedDecl());
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/DeclBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,10 @@ bool Decl::isInNamedModule() const {
return getOwningModule() && getOwningModule()->isNamedModule();
}

bool Decl::isFromHeaderUnit() const {
return getOwningModule() && getOwningModule()->isHeaderUnit();
}

static Decl::Kind getKind(const Decl *D) { return D->getKind(); }
static Decl::Kind getKind(const DeclContext *DC) { return DC->getDeclKind(); }

Expand Down
20 changes: 6 additions & 14 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2023,27 +2023,19 @@ const CXXRecordDecl *CXXRecordDecl::getTemplateInstantiationPattern() const {
if (auto *TD = dyn_cast<ClassTemplateSpecializationDecl>(this)) {
auto From = TD->getInstantiatedFrom();
if (auto *CTD = From.dyn_cast<ClassTemplateDecl *>()) {
while (true) {
CTD = CTD->getMostRecentDecl();
if (CTD->isMemberSpecialization())
break;
if (auto *NewCTD = CTD->getInstantiatedFromMemberTemplate())
CTD = NewCTD;
else
while (auto *NewCTD = CTD->getInstantiatedFromMemberTemplate()) {
if (NewCTD->isMemberSpecialization())
break;
CTD = NewCTD;
}
return GetDefinitionOrSelf(CTD->getTemplatedDecl());
}
if (auto *CTPSD =
From.dyn_cast<ClassTemplatePartialSpecializationDecl *>()) {
while (true) {
CTPSD = CTPSD->getMostRecentDecl();
if (CTPSD->isMemberSpecialization())
break;
if (auto *NewCTPSD = CTPSD->getInstantiatedFromMemberTemplate())
CTPSD = NewCTPSD;
else
while (auto *NewCTPSD = CTPSD->getInstantiatedFromMember()) {
if (NewCTPSD->isMemberSpecialization())
break;
CTPSD = NewCTPSD;
}
return GetDefinitionOrSelf(CTPSD);
}
Expand Down
30 changes: 16 additions & 14 deletions clang/lib/AST/DeclTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,35 +309,35 @@ bool TemplateDecl::isTypeAlias() const {
void RedeclarableTemplateDecl::anchor() {}

RedeclarableTemplateDecl::CommonBase *RedeclarableTemplateDecl::getCommonPtr() const {
if (CommonBase *C = getCommonPtrInternal())
return C;
if (Common)
return Common;

// Walk the previous-declaration chain until we either find a declaration
// with a common pointer or we run out of previous declarations.
SmallVector<const RedeclarableTemplateDecl *, 2> PrevDecls;
for (const RedeclarableTemplateDecl *Prev = getPreviousDecl(); Prev;
Prev = Prev->getPreviousDecl()) {
if (CommonBase *C = Prev->getCommonPtrInternal()) {
setCommonPtr(C);
if (Prev->Common) {
Common = Prev->Common;
break;
}

PrevDecls.push_back(Prev);
}

// If we never found a common pointer, allocate one now.
if (!getCommonPtrInternal()) {
if (!Common) {
// FIXME: If any of the declarations is from an AST file, we probably
// need an update record to add the common data.

setCommonPtr(newCommon(getASTContext()));
Common = newCommon(getASTContext());
}

// Update any previous declarations we saw with the common pointer.
for (const RedeclarableTemplateDecl *Prev : PrevDecls)
Prev->setCommonPtr(getCommonPtrInternal());
Prev->Common = Common;

return getCommonPtrInternal();
return Common;
}

void RedeclarableTemplateDecl::loadLazySpecializationsImpl() const {
Expand Down Expand Up @@ -463,17 +463,19 @@ void FunctionTemplateDecl::addSpecialization(
}

void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) {
using Base = RedeclarableTemplateDecl;

// If we haven't created a common pointer yet, then it can just be created
// with the usual method.
if (!getCommonPtrInternal())
if (!Base::Common)
return;

Common *ThisCommon = static_cast<Common *>(getCommonPtrInternal());
Common *ThisCommon = static_cast<Common *>(Base::Common);
Common *PrevCommon = nullptr;
SmallVector<FunctionTemplateDecl *, 8> PreviousDecls;
for (; Prev; Prev = Prev->getPreviousDecl()) {
if (CommonBase *C = Prev->getCommonPtrInternal()) {
PrevCommon = static_cast<Common *>(C);
if (Prev->Base::Common) {
PrevCommon = static_cast<Common *>(Prev->Base::Common);
break;
}
PreviousDecls.push_back(Prev);
Expand All @@ -483,15 +485,15 @@ void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) {
// use this common pointer.
if (!PrevCommon) {
for (auto *D : PreviousDecls)
D->setCommonPtr(ThisCommon);
D->Base::Common = ThisCommon;
return;
}

// Ensure we don't leak any important state.
assert(ThisCommon->Specializations.size() == 0 &&
"Can't merge incompatible declarations!");

setCommonPtr(PrevCommon);
Base::Common = PrevCommon;
}

//===----------------------------------------------------------------------===//
Expand Down
27 changes: 27 additions & 0 deletions clang/lib/AST/OpenMPClause.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,25 @@ OMPSizesClause *OMPSizesClause::CreateEmpty(const ASTContext &C,
return new (Mem) OMPSizesClause(NumSizes);
}

OMPPermutationClause *OMPPermutationClause::Create(const ASTContext &C,
SourceLocation StartLoc,
SourceLocation LParenLoc,
SourceLocation EndLoc,
ArrayRef<Expr *> Args) {
OMPPermutationClause *Clause = CreateEmpty(C, Args.size());
Clause->setLocStart(StartLoc);
Clause->setLParenLoc(LParenLoc);
Clause->setLocEnd(EndLoc);
Clause->setArgRefs(Args);
return Clause;
}

OMPPermutationClause *OMPPermutationClause::CreateEmpty(const ASTContext &C,
unsigned NumLoops) {
void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(NumLoops));
return new (Mem) OMPPermutationClause(NumLoops);
}

OMPFullClause *OMPFullClause::Create(const ASTContext &C,
SourceLocation StartLoc,
SourceLocation EndLoc) {
Expand Down Expand Up @@ -1841,6 +1860,14 @@ void OMPClausePrinter::VisitOMPSizesClause(OMPSizesClause *Node) {
OS << ")";
}

void OMPClausePrinter::VisitOMPPermutationClause(OMPPermutationClause *Node) {
OS << "permutation(";
llvm::interleaveComma(Node->getArgsRefs(), OS, [&](const Expr *E) {
E->printPretty(OS, nullptr, Policy, 0);
});
OS << ")";
}

void OMPClausePrinter::VisitOMPFullClause(OMPFullClause *Node) { OS << "full"; }

void OMPClausePrinter::VisitOMPPartialClause(OMPPartialClause *Node) {
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/AST/StmtProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,13 @@ void OMPClauseProfiler::VisitOMPSizesClause(const OMPSizesClause *C) {
Profiler->VisitExpr(E);
}

void OMPClauseProfiler::VisitOMPPermutationClause(
const OMPPermutationClause *C) {
for (Expr *E : C->getArgsRefs())
if (E)
Profiler->VisitExpr(E);
}

void OMPClauseProfiler::VisitOMPFullClause(const OMPFullClause *C) {}

void OMPClauseProfiler::VisitOMPPartialClause(const OMPPartialClause *C) {
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Analysis/ProgramPoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ void ProgramPoint::printJson(llvm::raw_ostream &Out, const char *NL) const {
LHS->printJson(Out, nullptr, PP, AddQuotes);
} else {
Out << "null";
}
}

Out << ", \"rhs\": ";
if (const Stmt *RHS = C->getRHS()) {
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Basic/OpenMPKinds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind, StringRef Str,
case OMPC_safelen:
case OMPC_simdlen:
case OMPC_sizes:
case OMPC_permutation:
case OMPC_allocator:
case OMPC_allocate:
case OMPC_collapse:
Expand Down Expand Up @@ -512,6 +513,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind,
case OMPC_safelen:
case OMPC_simdlen:
case OMPC_sizes:
case OMPC_permutation:
case OMPC_allocator:
case OMPC_allocate:
case OMPC_collapse:
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Basic/Targets/DirectX.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class LLVM_LIBRARY_VISIBILITY DirectXTargetInfo : public TargetInfo {
PlatformName = llvm::Triple::getOSTypeName(Triple.getOS());
resetDataLayout("e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:"
"32-f64:64-n8:16:32:64");
TheCXXABI.set(TargetCXXABI::Microsoft);
TheCXXABI.set(TargetCXXABI::GenericItanium);
}
bool useFP16ConversionIntrinsics() const override { return false; }
void getTargetDefines(const LangOptions &Opts,
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CIR/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ include_directories(${LLVM_MAIN_SRC_DIR}/../mlir/include)
include_directories(${CMAKE_BINARY_DIR}/tools/mlir/include)

add_subdirectory(Dialect)
add_subdirectory(CodeGen)
add_subdirectory(FrontendAction)
32 changes: 32 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//===- CIRGenModule.cpp - Per-Module state for CIR generation -------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This is the internal per-translation-unit state used for CIR translation.
//
//===----------------------------------------------------------------------===//

#include "CIRGenModule.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclBase.h"

#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Location.h"
#include "mlir/IR/MLIRContext.h"

using namespace cir;
CIRGenModule::CIRGenModule(mlir::MLIRContext &context,
clang::ASTContext &astctx,
const clang::CodeGenOptions &cgo,
DiagnosticsEngine &diags)
: astCtx(astctx), langOpts(astctx.getLangOpts()),
theModule{mlir::ModuleOp::create(mlir::UnknownLoc())},
target(astCtx.getTargetInfo()) {}

// Emit code for a single top level declaration.
void CIRGenModule::buildTopLevelDecl(Decl *decl) {}
62 changes: 62 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//===--- CIRGenModule.h - Per-Module state for CIR gen ----------*- 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
//
//===----------------------------------------------------------------------===//
//
// This is the internal per-translation-unit state used for CIR translation.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H
#define LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H

#include "CIRGenTypeCache.h"

#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/MLIRContext.h"

namespace clang {
class ASTContext;
class CodeGenOptions;
class Decl;
class DiagnosticsEngine;
class LangOptions;
class TargetInfo;
} // namespace clang

using namespace clang;
namespace cir {

/// This class organizes the cross-function state that is used while generating
/// CIR code.
class CIRGenModule : public CIRGenTypeCache {
CIRGenModule(CIRGenModule &) = delete;
CIRGenModule &operator=(CIRGenModule &) = delete;

public:
CIRGenModule(mlir::MLIRContext &context, clang::ASTContext &astctx,
const clang::CodeGenOptions &cgo,
clang::DiagnosticsEngine &diags);

~CIRGenModule() = default;

private:
/// Hold Clang AST information.
clang::ASTContext &astCtx;

const clang::LangOptions &langOpts;

/// A "module" matches a c/cpp source file: containing a list of functions.
mlir::ModuleOp theModule;

const clang::TargetInfo &target;

public:
void buildTopLevelDecl(clang::Decl *decl);
};
} // namespace cir

#endif // LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H
27 changes: 27 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenTypeCache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//===--- CIRGenTypeCache.h - Commonly used LLVM types and info -*- 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
//
//===----------------------------------------------------------------------===//
//
// This structure provides a set of common types useful during CIR emission.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_LIB_CIR_CIRGENTYPECACHE_H
#define LLVM_CLANG_LIB_CIR_CIRGENTYPECACHE_H

namespace cir {

/// This structure provides a set of types that are commonly used
/// during IR emission. It's initialized once in CodeGenModule's
/// constructor and then copied around into new CIRGenFunction's.
struct CIRGenTypeCache {
CIRGenTypeCache() = default;
};

} // namespace cir

#endif // LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENTYPECACHE_H
43 changes: 43 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenerator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//===--- CIRGenerator.cpp - Emit CIR from ASTs ----------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This builds an AST and converts it to CIR.
//
//===----------------------------------------------------------------------===//

#include "CIRGenModule.h"

#include "clang/AST/DeclGroup.h"
#include "clang/CIR/CIRGenerator.h"

using namespace cir;
using namespace clang;

void CIRGenerator::anchor() {}

CIRGenerator::CIRGenerator(clang::DiagnosticsEngine &diags,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs,
const CodeGenOptions &cgo)
: diags(diags), fs(std::move(vfs)), codeGenOpts{cgo} {}
CIRGenerator::~CIRGenerator() = default;

void CIRGenerator::Initialize(ASTContext &astCtx) {
using namespace llvm;

this->astCtx = &astCtx;

cgm = std::make_unique<CIRGenModule>(*mlirCtx, astCtx, codeGenOpts, diags);
}

bool CIRGenerator::HandleTopLevelDecl(DeclGroupRef group) {

for (Decl *decl : group)
cgm->buildTopLevelDecl(decl);

return true;
}
23 changes: 23 additions & 0 deletions clang/lib/CIR/CodeGen/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
set(
LLVM_LINK_COMPONENTS
Core
Support
)

get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)

add_clang_library(clangCIR
CIRGenerator.cpp
CIRGenModule.cpp

DEPENDS
MLIRCIR
${dialect_libs}

LINK_LIBS
clangAST
clangBasic
clangLex
${dialect_libs}
MLIRCIR
)
72 changes: 72 additions & 0 deletions clang/lib/CIR/FrontendAction/CIRGenAction.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//===--- CIRGenAction.cpp - LLVM Code generation Frontend Action ---------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#include "clang/CIR/FrontendAction/CIRGenAction.h"
#include "clang/CIR/CIRGenerator.h"
#include "clang/Frontend/CompilerInstance.h"

#include "mlir/IR/MLIRContext.h"
#include "mlir/IR/OwningOpRef.h"

using namespace cir;
using namespace clang;

namespace cir {

class CIRGenConsumer : public clang::ASTConsumer {

virtual void anchor();

std::unique_ptr<raw_pwrite_stream> OutputStream;

IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS;
std::unique_ptr<CIRGenerator> Gen;

public:
CIRGenConsumer(CIRGenAction::OutputType Action,
DiagnosticsEngine &DiagnosticsEngine,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
const HeaderSearchOptions &HeaderSearchOptions,
const CodeGenOptions &CodeGenOptions,
const TargetOptions &TargetOptions,
const LangOptions &LangOptions,
const FrontendOptions &FEOptions,
std::unique_ptr<raw_pwrite_stream> OS)
: OutputStream(std::move(OS)), FS(VFS),
Gen(std::make_unique<CIRGenerator>(DiagnosticsEngine, std::move(VFS),
CodeGenOptions)) {}

bool HandleTopLevelDecl(DeclGroupRef D) override {
Gen->HandleTopLevelDecl(D);
return true;
}
};
} // namespace cir

void CIRGenConsumer::anchor() {}

CIRGenAction::CIRGenAction(OutputType Act, mlir::MLIRContext *MLIRCtx)
: MLIRCtx(MLIRCtx ? MLIRCtx : new mlir::MLIRContext), Action(Act) {}

CIRGenAction::~CIRGenAction() { MLIRMod.release(); }

std::unique_ptr<ASTConsumer>
CIRGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
std::unique_ptr<llvm::raw_pwrite_stream> Out = CI.takeOutputStream();

auto Result = std::make_unique<cir::CIRGenConsumer>(
Action, CI.getDiagnostics(), &CI.getVirtualFileSystem(),
CI.getHeaderSearchOpts(), CI.getCodeGenOpts(), CI.getTargetOpts(),
CI.getLangOpts(), CI.getFrontendOpts(), std::move(Out));

return Result;
}

void EmitCIRAction::anchor() {}
EmitCIRAction::EmitCIRAction(mlir::MLIRContext *MLIRCtx)
: CIRGenAction(OutputType::EmitCIR, MLIRCtx) {}
17 changes: 17 additions & 0 deletions clang/lib/CIR/FrontendAction/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
set(LLVM_LINK_COMPONENTS
Core
Support
)

get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)

add_clang_library(clangCIRFrontendAction
CIRGenAction.cpp

LINK_LIBS
clangAST
clangFrontend
clangCIR
MLIRCIR
MLIRIR
)
9 changes: 5 additions & 4 deletions clang/lib/CodeGen/BackendUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1321,10 +1321,11 @@ static void runThinLTOBackend(
Conf.CGFileType = getCodeGenFileType(Action);
break;
}
if (Error E = thinBackend(
Conf, -1, AddStream, *M, *CombinedIndex, ImportList,
ModuleToDefinedGVSummaries[M->getModuleIdentifier()],
/* ModuleMap */ nullptr, Conf.CodeGenOnly, CGOpts.CmdArgs)) {
if (Error E =
thinBackend(Conf, -1, AddStream, *M, *CombinedIndex, ImportList,
ModuleToDefinedGVSummaries[M->getModuleIdentifier()],
/*ModuleMap=*/nullptr, Conf.CodeGenOnly,
/*IRAddStream=*/nullptr, CGOpts.CmdArgs)) {
handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) {
errs() << "Error running ThinLTO backend: " << EIB.message() << '\n';
});
Expand Down
43 changes: 36 additions & 7 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18755,6 +18755,16 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
CGM.getHLSLRuntime().getNormalizeIntrinsic(), ArrayRef<Value *>{X},
nullptr, "hlsl.normalize");
}
case Builtin::BI__builtin_hlsl_elementwise_degrees: {
Value *X = EmitScalarExpr(E->getArg(0));

assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
"degree operand must have a float representation");

return Builder.CreateIntrinsic(
/*ReturnType=*/X->getType(), CGM.getHLSLRuntime().getDegreesIntrinsic(),
ArrayRef<Value *>{X}, nullptr, "hlsl.degrees");
}
case Builtin::BI__builtin_hlsl_elementwise_frac: {
Value *Op0 = EmitScalarExpr(E->getArg(0));
if (!E->getArg(0)->getType()->hasFloatingRepresentation())
Expand Down Expand Up @@ -18867,27 +18877,46 @@ case Builtin::BI__builtin_hlsl_elementwise_isinf: {
ArrayRef<Value *>{Op0, Op1}, nullptr, "hlsl.step");
}
case Builtin::BI__builtin_hlsl_wave_get_lane_index: {
return EmitRuntimeCall(CGM.CreateRuntimeFunction(
llvm::FunctionType::get(IntTy, {}, false), "__hlsl_wave_get_lane_index",
{}, false, true));
// We don't define a SPIR-V intrinsic, instead it is a SPIR-V built-in
// defined in SPIRVBuiltins.td. So instead we manually get the matching name
// for the DirectX intrinsic and the demangled builtin name
switch (CGM.getTarget().getTriple().getArch()) {
case llvm::Triple::dxil:
return EmitRuntimeCall(Intrinsic::getDeclaration(
&CGM.getModule(), Intrinsic::dx_wave_getlaneindex));
case llvm::Triple::spirv:
return EmitRuntimeCall(CGM.CreateRuntimeFunction(
llvm::FunctionType::get(IntTy, {}, false),
"__hlsl_wave_get_lane_index", {}, false, true));
default:
llvm_unreachable(
"Intrinsic WaveGetLaneIndex not supported by target architecture");
}
}
case Builtin::BI__builtin_hlsl_wave_is_first_lane: {
Intrinsic::ID ID = CGM.getHLSLRuntime().getWaveIsFirstLaneIntrinsic();
return EmitRuntimeCall(Intrinsic::getDeclaration(&CGM.getModule(), ID));
}
case Builtin::BI__builtin_hlsl_elementwise_sign: {
Value *Op0 = EmitScalarExpr(E->getArg(0));
auto *Arg0 = E->getArg(0);
Value *Op0 = EmitScalarExpr(Arg0);
llvm::Type *Xty = Op0->getType();
llvm::Type *retType = llvm::Type::getInt32Ty(this->getLLVMContext());
if (Xty->isVectorTy()) {
auto *XVecTy = E->getArg(0)->getType()->getAs<VectorType>();
auto *XVecTy = Arg0->getType()->getAs<VectorType>();
retType = llvm::VectorType::get(
retType, ElementCount::getFixed(XVecTy->getNumElements()));
}
assert((E->getArg(0)->getType()->hasFloatingRepresentation() ||
E->getArg(0)->getType()->hasSignedIntegerRepresentation()) &&
assert((Arg0->getType()->hasFloatingRepresentation() ||
Arg0->getType()->hasIntegerRepresentation()) &&
"sign operand must have a float or int representation");

if (Arg0->getType()->hasUnsignedIntegerRepresentation()) {
Value *Cmp = Builder.CreateICmpEQ(Op0, ConstantInt::get(Xty, 0));
return Builder.CreateSelect(Cmp, ConstantInt::get(retType, 0),
ConstantInt::get(retType, 1), "hlsl.sign");
}

return Builder.CreateIntrinsic(
retType, CGM.getHLSLRuntime().getSignIntrinsic(),
ArrayRef<Value *>{Op0}, nullptr, "hlsl.sign");
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGHLSLRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class CGHLSLRuntime {
GENERATE_HLSL_INTRINSIC_FUNCTION(All, all)
GENERATE_HLSL_INTRINSIC_FUNCTION(Any, any)
GENERATE_HLSL_INTRINSIC_FUNCTION(Cross, cross)
GENERATE_HLSL_INTRINSIC_FUNCTION(Degrees, degrees)
GENERATE_HLSL_INTRINSIC_FUNCTION(Frac, frac)
GENERATE_HLSL_INTRINSIC_FUNCTION(Length, length)
GENERATE_HLSL_INTRINSIC_FUNCTION(Lerp, lerp)
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/ItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2997,6 +2997,10 @@ void ItaniumCXXABI::registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D,
if (D.isNoDestroy(CGM.getContext()))
return;

// HLSL doesn't support atexit.
if (CGM.getLangOpts().HLSL)
return CGM.AddCXXDtorEntry(dtor, addr);

// OpenMP offloading supports C++ constructors and destructors but we do not
// always have 'atexit' available. Instead lower these to use the LLVM global
// destructors which we can handle directly in the runtime. Note that this is
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2029,7 +2029,7 @@ void Driver::PrintHelp(bool ShowHidden) const {

void Driver::PrintVersion(const Compilation &C, raw_ostream &OS) const {
if (IsFlangMode()) {
OS << getClangToolFullVersion("flang-new") << '\n';
OS << getClangToolFullVersion("flang") << '\n';
} else {
// FIXME: The following handlers should use a callback mechanism, we don't
// know what the client would like to do.
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Driver/ToolChain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,9 @@ static const DriverSuffix *FindDriverSuffix(StringRef ProgName, size_t &Pos) {
{"cl", "--driver-mode=cl"},
{"++", "--driver-mode=g++"},
{"flang", "--driver-mode=flang"},
// For backwards compatibility, we create a symlink for `flang` called
// `flang-new`. This will be removed in the future.
{"flang-new", "--driver-mode=flang"},
{"clang-dxc", "--driver-mode=dxc"},
};

Expand Down
12 changes: 4 additions & 8 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5139,6 +5139,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
}
}

if (Args.hasArg(options::OPT_fclangir))
CmdArgs.push_back("-fclangir");

if (IsOpenMPDevice) {
// We have to pass the triple of the host if compiling for an OpenMP device.
std::string NormalizedTriple =
Expand Down Expand Up @@ -9107,13 +9110,6 @@ void OffloadPackager::ConstructJob(Compilation &C, const JobAction &JA,
llvm::copy_if(Features, std::back_inserter(FeatureArgs),
[](StringRef Arg) { return !Arg.starts_with("-target"); });

if (TC->getTriple().isAMDGPU()) {
for (StringRef Feature : llvm::split(Arch.split(':').second, ':')) {
FeatureArgs.emplace_back(
Args.MakeArgString(Feature.take_back() + Feature.drop_back()));
}
}

// TODO: We need to pass in the full target-id and handle it properly in the
// linker wrapper.
SmallVector<std::string> Parts{
Expand All @@ -9123,7 +9119,7 @@ void OffloadPackager::ConstructJob(Compilation &C, const JobAction &JA,
"kind=" + Kind.str(),
};

if (TC->getDriver().isUsingOffloadLTO() || TC->getTriple().isAMDGPU())
if (TC->getDriver().isUsingOffloadLTO())
for (StringRef Feature : FeatureArgs)
Parts.emplace_back("feature=" + Feature.str());

Expand Down
8 changes: 4 additions & 4 deletions clang/lib/Driver/ToolChains/Cuda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,10 @@ void CudaToolChain::addClangTargetOptions(
if (CudaInstallation.version() >= CudaVersion::CUDA_90)
CC1Args.push_back("-fcuda-allow-variadic-functions");

if (DriverArgs.hasFlag(options::OPT_fcuda_short_ptr,
options::OPT_fno_cuda_short_ptr, false))
CC1Args.append({"-mllvm", "--nvptx-short-ptr"});

if (DriverArgs.hasArg(options::OPT_nogpulib))
return;

Expand All @@ -873,10 +877,6 @@ void CudaToolChain::addClangTargetOptions(

clang::CudaVersion CudaInstallationVersion = CudaInstallation.version();

if (DriverArgs.hasFlag(options::OPT_fcuda_short_ptr,
options::OPT_fno_cuda_short_ptr, false))
CC1Args.append({"-mllvm", "--nvptx-short-ptr"});

if (CudaInstallationVersion >= CudaVersion::UNKNOWN)
CC1Args.push_back(
DriverArgs.MakeArgString(Twine("-target-sdk-version=") +
Expand Down
9 changes: 5 additions & 4 deletions clang/lib/Driver/ToolChains/Flang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,9 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA,

if (Args.hasArg(options::OPT_fopenmp_force_usm))
CmdArgs.push_back("-fopenmp-force-usm");
// TODO: OpenMP support isn't "done" yet, so for now we warn that it
// is experimental.
D.Diag(diag::warn_openmp_experimental);

// FIXME: Clang supports a whole bunch more flags here.
break;
Expand Down Expand Up @@ -881,14 +884,12 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA,

CmdArgs.push_back(Input.getFilename());

// TODO: Replace flang-new with flang once the new driver replaces the
// throwaway driver
const char *Exec = Args.MakeArgString(D.GetProgramPath("flang-new", TC));
const char *Exec = Args.MakeArgString(D.GetProgramPath("flang", TC));
C.addCommand(std::make_unique<Command>(JA, *this,
ResponseFileSupport::AtFileUTF8(),
Exec, CmdArgs, Inputs, Output));
}

Flang::Flang(const ToolChain &TC) : Tool("flang-new", "flang frontend", TC) {}
Flang::Flang(const ToolChain &TC) : Tool("flang", "flang frontend", TC) {}

Flang::~Flang() {}
Loading