210 changes: 163 additions & 47 deletions clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
#include "Matchers.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include <cassert>

namespace clang::tidy::utils::decl_ref_expr {

Expand All @@ -34,69 +36,183 @@ void extractNodesByIdTo(ArrayRef<BoundNodes> Matches, StringRef ID,
Nodes.insert(Match.getNodeAs<Node>(ID));
}

// A matcher that matches DeclRefExprs that are used in ways such that the
// underlying declaration is not modified.
// If the declaration is of pointer type, `Indirections` specifies the level
// of indirection of the object whose mutations we are tracking.
//
// For example, given:
// ```
// int i;
// int* p;
// p = &i; // (A)
// *p = 3; // (B)
// ```
//
// `declRefExpr(to(varDecl(hasName("p"))), doesNotMutateObject(0))` matches
// (B), but `declRefExpr(to(varDecl(hasName("p"))), doesNotMutateObject(1))`
// matches (A).
//
AST_MATCHER_P(DeclRefExpr, doesNotMutateObject, int, Indirections) {
// We walk up the parents of the DeclRefExpr recursively until we end up on a
// parent that cannot modify the underlying object. There are a few kinds of
// expressions:
// - Those that cannot be used to mutate the underlying object. We can stop
// recursion there.
// - Those that can be used to mutate the underlying object in analyzable
// ways (such as taking the address or accessing a subobject). We have to
// examine the parents.
// - Those that we don't know how to analyze. In that case we stop there and
// we assume that they can mutate the underlying expression.

struct StackEntry {
StackEntry(const Expr *E, int Indirections)
: E(E), Indirections(Indirections) {}
// The expression to analyze.
const Expr *E;
// The number of pointer indirections of the object being tracked (how
// many times an address was taken).
int Indirections;
};

llvm::SmallVector<StackEntry, 4> Stack;
Stack.emplace_back(&Node, Indirections);
ASTContext &Ctx = Finder->getASTContext();

while (!Stack.empty()) {
const StackEntry Entry = Stack.back();
Stack.pop_back();

// If the expression type is const-qualified at the appropriate indirection
// level then we can not mutate the object.
QualType Ty = Entry.E->getType().getCanonicalType();
for (int I = 0; I < Entry.Indirections; ++I) {
assert(Ty->isPointerType());
Ty = Ty->getPointeeType().getCanonicalType();
}
if (Ty.isConstQualified())
continue;

// Otherwise we have to look at the parents to see how the expression is
// used.
const DynTypedNodeList Parents = Ctx.getParents(*Entry.E);
// Note: most nodes have a single parents, but there exist nodes that have
// several parents, such as `InitListExpr` that have semantic and syntactic
// forms.
for (const auto &Parent : Parents) {
if (Parent.get<CompoundStmt>()) {
// Unused block-scope statement.
continue;
}
const Expr *const P = Parent.get<Expr>();
if (P == nullptr) {
// `Parent` is not an expr (e.g. a `VarDecl`).
// The case of binding to a `const&` or `const*` variable is handled by
// the fact that there is going to be a `NoOp` cast to const below the
// `VarDecl`, so we're not even going to get there.
// The case of copying into a value-typed variable is handled by the
// rvalue cast.
// This triggers only when binding to a mutable reference/ptr variable.
// FIXME: When we take a mutable reference we could keep checking the
// new variable for const usage only.
return false;
}
// Cosmetic nodes.
if (isa<ParenExpr>(P) || isa<MaterializeTemporaryExpr>(P)) {
Stack.emplace_back(P, Entry.Indirections);
continue;
}
if (const auto *const Cast = dyn_cast<CastExpr>(P)) {
switch (Cast->getCastKind()) {
// NoOp casts are used to add `const`. We'll check whether adding that
// const prevents modification when we process the cast.
case CK_NoOp:
// These do nothing w.r.t. to mutability.
case CK_BaseToDerived:
case CK_DerivedToBase:
case CK_UncheckedDerivedToBase:
case CK_Dynamic:
case CK_BaseToDerivedMemberPointer:
case CK_DerivedToBaseMemberPointer:
Stack.emplace_back(Cast, Entry.Indirections);
continue;
case CK_ToVoid:
case CK_PointerToBoolean:
// These do not mutate the underlying variable.
continue;
case CK_LValueToRValue: {
// An rvalue is immutable.
if (Entry.Indirections == 0)
continue;
Stack.emplace_back(Cast, Entry.Indirections);
continue;
}
default:
// Bail out on casts that we cannot analyze.
return false;
}
}
if (const auto *const Member = dyn_cast<MemberExpr>(P)) {
if (const auto *const Method =
dyn_cast<CXXMethodDecl>(Member->getMemberDecl())) {
if (!Method->isConst()) {
// The method can mutate our variable.
return false;
}
continue;
}
Stack.emplace_back(Member, 0);
continue;
}
if (const auto *const Op = dyn_cast<UnaryOperator>(P)) {
switch (Op->getOpcode()) {
case UO_AddrOf:
Stack.emplace_back(Op, Entry.Indirections + 1);
continue;
case UO_Deref:
assert(Entry.Indirections > 0);
Stack.emplace_back(Op, Entry.Indirections - 1);
continue;
default:
// Bail out on unary operators that we cannot analyze.
return false;
}
}

// Assume any other expression can modify the underlying variable.
return false;
}
}

// No parent can modify the variable.
return true;
}

} // namespace

// Finds all DeclRefExprs where a const method is called on VarDecl or VarDecl
// is the a const reference or value argument to a CallExpr or CXXConstructExpr.
SmallPtrSet<const DeclRefExpr *, 16>
constReferenceDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt,
ASTContext &Context) {
auto DeclRefToVar =
declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef");
auto MemberExprOfVar = memberExpr(hasObjectExpression(DeclRefToVar));
auto DeclRefToVarOrMemberExprOfVar =
stmt(anyOf(DeclRefToVar, MemberExprOfVar));
auto ConstMethodCallee = callee(cxxMethodDecl(isConst()));
// Match method call expressions where the variable is referenced as the this
// implicit object argument and operator call expression for member operators
// where the variable is the 0-th argument.
auto Matches = match(
findAll(expr(anyOf(
cxxMemberCallExpr(ConstMethodCallee,
on(DeclRefToVarOrMemberExprOfVar)),
cxxOperatorCallExpr(ConstMethodCallee,
hasArgument(0, DeclRefToVarOrMemberExprOfVar))))),
Stmt, Context);
ASTContext &Context, int Indirections) {
auto Matches = match(findAll(declRefExpr(to(varDecl(equalsNode(&VarDecl))),
doesNotMutateObject(Indirections))
.bind("declRef")),
Stmt, Context);
SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
extractNodesByIdTo(Matches, "declRef", DeclRefs);
auto ConstReferenceOrValue =
qualType(anyOf(matchers::isReferenceToConst(),
unless(anyOf(referenceType(), pointerType(),
substTemplateTypeParmType()))));
auto ConstReferenceOrValueOrReplaced = qualType(anyOf(
ConstReferenceOrValue,
substTemplateTypeParmType(hasReplacementType(ConstReferenceOrValue))));
auto UsedAsConstRefOrValueArg = forEachArgumentWithParam(
DeclRefToVarOrMemberExprOfVar,
parmVarDecl(hasType(ConstReferenceOrValueOrReplaced)));
Matches = match(findAll(invocation(UsedAsConstRefOrValueArg)), Stmt, Context);
extractNodesByIdTo(Matches, "declRef", DeclRefs);
// References and pointers to const assignments.
Matches = match(
findAll(declStmt(has(varDecl(
hasType(qualType(matchers::isReferenceToConst())),
hasInitializer(ignoringImpCasts(DeclRefToVarOrMemberExprOfVar)))))),
Stmt, Context);
extractNodesByIdTo(Matches, "declRef", DeclRefs);
Matches = match(findAll(declStmt(has(varDecl(
hasType(qualType(matchers::isPointerToConst())),
hasInitializer(ignoringImpCasts(unaryOperator(
hasOperatorName("&"),
hasUnaryOperand(DeclRefToVarOrMemberExprOfVar)))))))),
Stmt, Context);
extractNodesByIdTo(Matches, "declRef", DeclRefs);

return DeclRefs;
}

bool isOnlyUsedAsConst(const VarDecl &Var, const Stmt &Stmt,
ASTContext &Context) {
ASTContext &Context, int Indirections) {
// Collect all DeclRefExprs to the loop variable and all CallExprs and
// CXXConstructExprs where the loop variable is used as argument to a const
// reference parameter.
// If the difference is empty it is safe for the loop variable to be a const
// reference.
auto AllDeclRefs = allDeclRefExprs(Var, Stmt, Context);
auto ConstReferenceDeclRefs = constReferenceDeclRefExprs(Var, Stmt, Context);
auto ConstReferenceDeclRefs =
constReferenceDeclRefExprs(Var, Stmt, Context, Indirections);
return isSetDifferenceEmpty(AllDeclRefs, ConstReferenceDeclRefs);
}

Expand Down
33 changes: 23 additions & 10 deletions clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,6 @@

namespace clang::tidy::utils::decl_ref_expr {

/// Returns true if all ``DeclRefExpr`` to the variable within ``Stmt``
/// do not modify it.
///
/// Returns ``true`` if only const methods or operators are called on the
/// variable or the variable is a const reference or value argument to a
/// ``callExpr()``.
bool isOnlyUsedAsConst(const VarDecl &Var, const Stmt &Stmt,
ASTContext &Context);

/// Returns set of all ``DeclRefExprs`` to ``VarDecl`` within ``Stmt``.
llvm::SmallPtrSet<const DeclRefExpr *, 16>
allDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context);
Expand All @@ -34,9 +25,31 @@ allDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl, ASTContext &Context);

/// Returns set of all ``DeclRefExprs`` to ``VarDecl`` within ``Stmt`` where
/// ``VarDecl`` is guaranteed to be accessed in a const fashion.
///
/// If ``VarDecl`` is of pointer type, ``Indirections`` specifies the level
/// of indirection of the object whose mutations we are tracking.
///
/// For example, given:
/// ```
/// int i;
/// int* p;
/// p = &i; // (A)
/// *p = 3; // (B)
/// ```
///
/// - `constReferenceDeclRefExprs(P, Stmt, Context, 0)` returns the reference
// to `p` in (B): the pointee is modified, but the pointer is not;
/// - `constReferenceDeclRefExprs(P, Stmt, Context, 1)` returns the reference
// to `p` in (A): the pointee is modified, but the pointer is not;
llvm::SmallPtrSet<const DeclRefExpr *, 16>
constReferenceDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt,
ASTContext &Context);
ASTContext &Context, int Indirections);

/// Returns true if all ``DeclRefExpr`` to the variable within ``Stmt``
/// do not modify it.
/// See `constReferenceDeclRefExprs` for the meaning of ``Indirections``.
bool isOnlyUsedAsConst(const VarDecl &Var, const Stmt &Stmt,
ASTContext &Context, int Indirections);

/// Returns ``true`` if ``DeclRefExpr`` is the argument of a copy-constructor
/// call expression within ``Decl``.
Expand Down
19 changes: 14 additions & 5 deletions clang-tools-extra/clangd/refactor/Rename.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -811,8 +811,18 @@ renameWithinFile(ParsedAST &AST, const NamedDecl &RenameDecl,
continue;
Locs.push_back(RenameLoc);
}
if (const auto *MD = dyn_cast<ObjCMethodDecl>(&RenameDecl))
return renameObjCMethodWithinFile(AST, MD, NewName, std::move(Locs));
if (const auto *MD = dyn_cast<ObjCMethodDecl>(&RenameDecl)) {
// The custom ObjC selector logic doesn't handle the zero arg selector
// case, as it relies on parsing selectors via the trailing `:`.
// We also choose to use regular rename logic for the single-arg selectors
// as the AST/Index has the right locations in that case.
if (MD->getSelector().getNumArgs() > 1)
return renameObjCMethodWithinFile(AST, MD, NewName, std::move(Locs));

// Eat trailing : for single argument methods since they're actually
// considered a separate token during rename.
NewName.consume_back(":");
}
for (const auto &Loc : Locs) {
if (auto Err = FilteredChanges.add(tooling::Replacement(
SM, CharSourceRange::getTokenRange(Loc), NewName)))
Expand Down Expand Up @@ -930,10 +940,9 @@ renameOutsideFile(const NamedDecl &RenameDecl, llvm::StringRef MainFilePath,
std::optional<Selector> Selector = std::nullopt;
llvm::SmallVector<llvm::StringRef, 8> NewNames;
if (const auto *MD = dyn_cast<ObjCMethodDecl>(&RenameDecl)) {
if (MD->getSelector().getNumArgs() > 1) {
RenameIdentifier = MD->getSelector().getNameForSlot(0).str();
RenameIdentifier = MD->getSelector().getNameForSlot(0).str();
if (MD->getSelector().getNumArgs() > 1)
Selector = MD->getSelector();
}
}
NewName.split(NewNames, ":");

Expand Down
8 changes: 7 additions & 1 deletion clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "ClangdServer.h"
#include "ConfigProvider.h"
#include "Diagnostics.h"
#include "Feature.h"
#include "FeatureModule.h"
#include "LSPBinder.h"
#include "LSPClient.h"
Expand Down Expand Up @@ -198,6 +199,9 @@ TEST_F(LSPTest, RecordsLatencies) {
// clang-tidy's renames are converted to clangd's internal rename functionality,
// see clangd#1589 and clangd#741
TEST_F(LSPTest, ClangTidyRename) {
// This test requires clang-tidy checks to be linked in.
if (!CLANGD_TIDY_CHECKS)
return;
Annotations Header(R"cpp(
void [[foo]]();
)cpp");
Expand All @@ -214,7 +218,9 @@ TEST_F(LSPTest, ClangTidyRename) {
Client.didOpen("foo.hpp", Header.code());
Client.didOpen("foo.cpp", Source.code());

auto RenameDiag = Client.diagnostics("foo.cpp").value().at(0);
auto Diags = Client.diagnostics("foo.cpp");
ASSERT_TRUE(Diags && !Diags->empty());
auto RenameDiag = Diags->front();

auto RenameCommand =
(*Client
Expand Down
138 changes: 138 additions & 0 deletions clang-tools-extra/clangd/unittests/RenameTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1943,6 +1943,144 @@ TEST(CrossFileRenameTests, WithUpToDateIndex) {
}
}

TEST(CrossFileRenameTests, ObjC) {
MockCompilationDatabase CDB;
CDB.ExtraClangFlags = {"-xobjective-c"};
// rename is runnning on all "^" points in FooH.
struct Case {
llvm::StringRef FooH;
llvm::StringRef FooM;
llvm::StringRef NewName;
llvm::StringRef ExpectedFooH;
llvm::StringRef ExpectedFooM;
};
Case Cases[] = {// --- Zero arg selector
{
// Input
R"cpp(
@interface Foo
- (int)performA^ction;
@end
)cpp",
R"cpp(
@implementation Foo
- (int)performAction {
[self performAction];
}
@end
)cpp",
// New name
"performNewAction",
// Expected
R"cpp(
@interface Foo
- (int)performNewAction;
@end
)cpp",
R"cpp(
@implementation Foo
- (int)performNewAction {
[self performNewAction];
}
@end
)cpp",
},
// --- Single arg selector
{
// Input
R"cpp(
@interface Foo
- (int)performA^ction:(int)action;
@end
)cpp",
R"cpp(
@implementation Foo
- (int)performAction:(int)action {
[self performAction:action];
}
@end
)cpp",
// New name
"performNewAction:",
// Expected
R"cpp(
@interface Foo
- (int)performNewAction:(int)action;
@end
)cpp",
R"cpp(
@implementation Foo
- (int)performNewAction:(int)action {
[self performNewAction:action];
}
@end
)cpp",
},
// --- Multi arg selector
{
// Input
R"cpp(
@interface Foo
- (int)performA^ction:(int)action with:(int)value;
@end
)cpp",
R"cpp(
@implementation Foo
- (int)performAction:(int)action with:(int)value {
[self performAction:action with:value];
}
@end
)cpp",
// New name
"performNewAction:by:",
// Expected
R"cpp(
@interface Foo
- (int)performNewAction:(int)action by:(int)value;
@end
)cpp",
R"cpp(
@implementation Foo
- (int)performNewAction:(int)action by:(int)value {
[self performNewAction:action by:value];
}
@end
)cpp",
}};

trace::TestTracer Tracer;
for (const auto &T : Cases) {
SCOPED_TRACE(T.FooH);
Annotations FooH(T.FooH);
Annotations FooM(T.FooM);
std::string FooHPath = testPath("foo.h");
std::string FooMPath = testPath("foo.m");

MockFS FS;
FS.Files[FooHPath] = std::string(FooH.code());
FS.Files[FooMPath] = std::string(FooM.code());

auto ServerOpts = ClangdServer::optsForTest();
ServerOpts.BuildDynamicSymbolIndex = true;
ClangdServer Server(CDB, FS, ServerOpts);

// Add all files to clangd server to make sure the dynamic index has been
// built.
runAddDocument(Server, FooHPath, FooH.code());
runAddDocument(Server, FooMPath, FooM.code());

for (const auto &RenamePos : FooH.points()) {
EXPECT_THAT(Tracer.takeMetric("rename_files"), SizeIs(0));
auto FileEditsList =
llvm::cantFail(runRename(Server, FooHPath, RenamePos, T.NewName, {}));
EXPECT_THAT(Tracer.takeMetric("rename_files"), ElementsAre(2));
EXPECT_THAT(applyEdits(std::move(FileEditsList.GlobalChanges)),
UnorderedElementsAre(Pair(Eq(FooHPath), Eq(T.ExpectedFooH)),
Pair(Eq(FooMPath), Eq(T.ExpectedFooM))));
}
}
}

TEST(CrossFileRenameTests, CrossFileOnLocalSymbol) {
// cross-file rename should work for function-local symbols, even there is no
// index provided.
Expand Down
10 changes: 10 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ The improvements are...
Improvements to clang-tidy
--------------------------

- Improved :program:`run-clang-tidy.py` script. Added argument `-source-filter`
to filter source files from the compilation database, via a RegEx. In a
similar fashion to what `-header-filter` does for header files.

New checks
^^^^^^^^^^

Expand Down Expand Up @@ -179,6 +183,12 @@ Changes in existing checks
<clang-tidy/checks/modernize/use-override>` check to also remove any trailing
whitespace when deleting the ``virtual`` keyword.

- Improved :doc:`performance-unnecessary-copy-initialization
<clang-tidy/checks/performance/unnecessary-copy-initialization>` check by
detecting more cases of constant access. In particular, pointers can be
analyzed, se the check now handles the common patterns
`const auto e = (*vector_ptr)[i]` and `const auto e = vector_ptr->at(i);`.

- Improved :doc:`readability-implicit-bool-conversion
<clang-tidy/checks/readability/implicit-bool-conversion>` check to provide
valid fix suggestions for ``static_cast`` without a preceding space and
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/include-cleaner/lib/WalkAST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ class ASTWalker : public RecursiveASTVisitor<ASTWalker> {

bool VisitDeclRefExpr(DeclRefExpr *DRE) {
auto *FD = DRE->getFoundDecl();
// Prefer the underlying decl if FoundDecl isn't a shadow decl, e.g:
// - For templates, found-decl is always primary template, but we want the
// specializaiton itself.
if (!llvm::isa<UsingShadowDecl>(FD))
FD = DRE->getDecl();
// For refs to non-meber-like decls, use the found decl.
// For member-like decls, we should have a reference from the qualifier to
// the container decl instead, which is preferred as it'll handle
Expand Down
18 changes: 11 additions & 7 deletions clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ TEST(WalkAST, VarTemplates) {
template<typename T> T* $explicit^Foo<T*> = nullptr;)cpp",
"int *x = ^Foo<int *>;"),
ElementsAre(Decl::VarTemplateSpecialization));
// Implicit specializations through explicit instantiations has source
// locations pointing at the primary template.
EXPECT_THAT(testWalk(R"cpp(
template<typename T> T $explicit^Foo = 0;
template int Foo<int>;)cpp",
Expand All @@ -239,18 +241,19 @@ TEST(WalkAST, FunctionTemplates) {
EXPECT_THAT(testWalk(R"cpp(
template <typename T> void $explicit^foo() {})cpp",
"auto x = []{ ^foo<int>(); };"),
ElementsAre(Decl::FunctionTemplate));
// FIXME: DeclRefExpr points at primary template, not the specialization.
ElementsAre(Decl::Function));
EXPECT_THAT(testWalk(R"cpp(
template<typename T> void $explicit^foo() {}
template<> void foo<int>(){})cpp",
template<typename T> void foo() {}
template<> void $explicit^foo<int>(){})cpp",
"auto x = []{ ^foo<int>(); };"),
ElementsAre(Decl::FunctionTemplate));
ElementsAre(Decl::Function));
// The decl is actually the specialization, but explicit instantations point
// at the primary template.
EXPECT_THAT(testWalk(R"cpp(
template<typename T> void $explicit^foo() {};
template void foo<int>();)cpp",
"auto x = [] { ^foo<int>(); };"),
ElementsAre(Decl::FunctionTemplate));
ElementsAre(Decl::Function));
}
TEST(WalkAST, TemplateSpecializationsFromUsingDecl) {
// Class templates
Expand Down Expand Up @@ -548,7 +551,8 @@ TEST(WalkAST, Concepts) {
testWalk(Concept, "template<typename T> void func() requires ^Foo<T> {}");
testWalk(Concept, "void func(^Foo auto x) {}");
// FIXME: Foo should be explicitly referenced.
testWalk("template<typename T> concept Foo = true;", "void func() { ^Foo auto x = 1; }");
testWalk("template<typename T> concept Foo = true;",
"void func() { ^Foo auto x = 1; }");
}

TEST(WalkAST, FriendDecl) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ struct Container {
ConstIterator<T> end() const;
void nonConstMethod();
bool constMethod() const;

const T& at(int) const;
T& at(int);

};

using ExpensiveToCopyContainerAlias = Container<ExpensiveToCopyType>;
Expand Down Expand Up @@ -225,25 +229,25 @@ void PositiveOperatorCallConstValueParamAlias(const ExpensiveToCopyContainerAlia
VarCopyConstructed.constMethod();
}

void PositiveOperatorCallConstValueParam(const Container<ExpensiveToCopyType>* C) {
void PositiveOperatorCallConstPtrParam(const Container<ExpensiveToCopyType>* C) {
const auto AutoAssigned = (*C)[42];
// TODO-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
// TODO-FIXES: const auto& AutoAssigned = (*C)[42];
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
// CHECK-FIXES: const auto& AutoAssigned = (*C)[42];
AutoAssigned.constMethod();

const auto AutoCopyConstructed((*C)[42]);
// TODO-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
// TODO-FIXES: const auto& AutoCopyConstructed((*C)[42]);
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
// CHECK-FIXES: const auto& AutoCopyConstructed((*C)[42]);
AutoCopyConstructed.constMethod();

const ExpensiveToCopyType VarAssigned = C->operator[](42);
// TODO-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
// TODO-FIXES: const ExpensiveToCopyType& VarAssigned = C->operator[](42);
const ExpensiveToCopyType VarAssigned = C->at(42);
// CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
// CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = C->at(42);
VarAssigned.constMethod();

const ExpensiveToCopyType VarCopyConstructed(C->operator[](42));
// TODO-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
// TODO-FIXES: const ExpensiveToCopyType& VarCopyConstructed(C->operator[](42));
const ExpensiveToCopyType VarCopyConstructed(C->at(42));
// CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
// CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(C->at(42));
VarCopyConstructed.constMethod();
}

Expand Down Expand Up @@ -876,3 +880,4 @@ void negativeNonConstMemberExpr() {
mutate(&Copy.Member);
}
}

274 changes: 168 additions & 106 deletions clang-tools-extra/unittests/clang-tidy/DeclRefExprUtilsTest.cpp

Large diffs are not rendered by default.

143 changes: 142 additions & 1 deletion clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,146 @@ the configuration (without a prefix: ``Auto``).
}


.. _AlignConsecutiveTableGenCondOperatorColons:

**AlignConsecutiveTableGenCondOperatorColons** (``AlignConsecutiveStyle``) :versionbadge:`clang-format 19` :ref:`¶ <AlignConsecutiveTableGenCondOperatorColons>`
Style of aligning consecutive TableGen cond operator colons.
Align the colons of cases inside !cond operators.

.. code-block:: c++

!cond(!eq(size, 1) : 1,
!eq(size, 16): 1,
true : 0)

Nested configuration flags:

Alignment options.

They can also be read as a whole for compatibility. The choices are:
- None
- Consecutive
- AcrossEmptyLines
- AcrossComments
- AcrossEmptyLinesAndComments

For example, to align across empty lines and not across comments, either
of these work.

.. code-block:: c++

AlignConsecutiveMacros: AcrossEmptyLines

AlignConsecutiveMacros:
Enabled: true
AcrossEmptyLines: true
AcrossComments: false

* ``bool Enabled`` Whether aligning is enabled.

.. code-block:: c++

#define SHORT_NAME 42
#define LONGER_NAME 0x007f
#define EVEN_LONGER_NAME (2)
#define foo(x) (x * x)
#define bar(y, z) (y + z)

int a = 1;
int somelongname = 2;
double c = 3;

int aaaa : 1;
int b : 12;
int ccc : 8;

int aaaa = 12;
float b = 23;
std::string ccc;

* ``bool AcrossEmptyLines`` Whether to align across empty lines.

.. code-block:: c++

true:
int a = 1;
int somelongname = 2;
double c = 3;

int d = 3;

false:
int a = 1;
int somelongname = 2;
double c = 3;

int d = 3;

* ``bool AcrossComments`` Whether to align across comments.

.. code-block:: c++

true:
int d = 3;
/* A comment. */
double e = 4;
false:
int d = 3;
/* A comment. */
double e = 4;
* ``bool AlignCompound`` Only for ``AlignConsecutiveAssignments``. Whether compound assignments
like ``+=`` are aligned along with ``=``.

.. code-block:: c++

true:
a &= 2;
bbb = 2;

false:
a &= 2;
bbb = 2;

* ``bool AlignFunctionPointers`` Only for ``AlignConsecutiveDeclarations``. Whether function pointers are
aligned.

.. code-block:: c++

true:
unsigned i;
int &r;
int *p;
int (*f)();
false:
unsigned i;
int &r;
int *p;
int (*f)();
* ``bool PadOperators`` Only for ``AlignConsecutiveAssignments``. Whether short assignment
operators are left-padded to the same length as long ones in order to
put all assignment operators to the right of the left hand side.

.. code-block:: c++

true:
a >>= 2;
bbb = 2;

a = 2;
bbb >>= 2;

false:
a >>= 2;
bbb = 2;

a = 2;
bbb >>= 2;


.. _AlignEscapedNewlines:

**AlignEscapedNewlines** (``EscapedNewlineAlignmentStyle``) :versionbadge:`clang-format 5` :ref:`¶ <AlignEscapedNewlines>`
Expand Down Expand Up @@ -4844,7 +4984,8 @@ the configuration (without a prefix: ``Auto``).
.. _RemoveSemicolon:

**RemoveSemicolon** (``Boolean``) :versionbadge:`clang-format 16` :ref:`¶ <RemoveSemicolon>`
Remove semicolons after the closing brace of a non-empty function.
Remove semicolons after the closing braces of functions and
constructors/destructors.

.. warning::

Expand Down
110 changes: 110 additions & 0 deletions clang/docs/HLSL/ExpectedDifferences.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@

Expected Differences vs DXC and FXC
===================================

.. contents::
:local:

Introduction
============

HLSL currently has two reference compilers, the `DirectX Shader Compiler (DXC)
<https://github.com/microsoft/DirectXShaderCompiler/>`_ and the
`Effect-Compiler (FXC) <https://learn.microsoft.com/en-us/windows/win32/direct3dtools/fxc>`_.
The two reference compilers do not fully agree. Some known disagreements in the
references are tracked on
`DXC's GitHub
<https://github.com/microsoft/DirectXShaderCompiler/issues?q=is%3Aopen+is%3Aissue+label%3Afxc-disagrees>`_,
but many more are known to exist.

HLSL as implemented by Clang will also not fully match either of the reference
implementations, it is instead being written to match the `draft language
specification <https://microsoft.github.io/hlsl-specs/specs/hlsl.pdf>`_.

This document is a non-exhaustive collection the known differences between
Clang's implementation of HLSL and the existing reference compilers.

General Principles
------------------

Most of the intended differences between Clang and the earlier reference
compilers are focused on increased consistency and correctness. Both reference
compilers do not always apply language rules the same in all contexts.

Clang also deviates from the reference compilers by providing different
diagnostics, both in terms of the textual messages and the contexts in which
diagnostics are produced. While striving for a high level of source
compatibility with conforming HLSL code, Clang may produce earlier and more
robust diagnostics for incorrect code or reject code that a reference compiler
incorrectly accepted.

Language Version
================

Clang targets language compatibility for HLSL 2021 as implemented by DXC.
Language features that were removed in earlier versions of HLSL may be added on
a case-by-case basis, but are not planned for the initial implementation.

Overload Resolution
===================

Clang's HLSL implementation adopts C++ overload resolution rules as proposed for
HLSL 202x based on proposal
`0007 <https://github.com/microsoft/hlsl-specs/blob/main/proposals/0007-const-instance-methods.md>`_
and
`0008 <https://github.com/microsoft/hlsl-specs/blob/main/proposals/0008-non-member-operator-overloading.md>`_.

Clang's implementation extends standard overload resolution rules to HLSL
library functionality. This causes subtle changes in overload resolution
behavior between Clang and DXC. Some examples include:

.. code-block:: c++

void halfOrInt16(half H);
void halfOrInt16(uint16_t U);
void halfOrInt16(int16_t I);

void takesDoubles(double, double, double);

cbuffer CB {
uint U;
int I;
float X, Y, Z;
double3 A, B;
}

export void call() {
halfOrInt16(U); // DXC: Fails with call ambiguous between int16_t and uint16_t overloads
// Clang: Resolves to halfOrInt16(uint16_t).
halfOrInt16(I); // All: Resolves to halfOrInt16(int16_t).
half H;
#ifndef IGNORE_ERRORS
// asfloat16 is a builtin with overloads for half, int16_t, and uint16_t.
H = asfloat16(I); // DXC: Fails to resolve overload for int.
// Clang: Resolves to asfloat16(int16_t).
H = asfloat16(U); // DXC: Fails to resolve overload for int.
// Clang: Resolves to asfloat16(uint16_t).
#endif
H = asfloat16(0x01); // DXC: Resolves to asfloat16(half).
// Clang: Resolves to asfloat16(uint16_t).

takesDoubles(X, Y, Z); // Works on all compilers
#ifndef IGNORE_ERRORS
fma(X, Y, Z); // DXC: Fails to resolve no known conversion from float to double.
// Clang: Resolves to fma(double,double,double).
#endif

double D = dot(A, B); // DXC: Resolves to dot(double3, double3), fails DXIL Validation.
// FXC: Expands to compute double dot product with fmul/fadd
// Clang: Resolves to dot(float3, float3), emits conversion warnings.

}

.. note::

In Clang, a conscious decision was made to exclude the ``dot(vector<double,N>, vector<double,N>)``
overload and allow overload resolution to resolve the
``vector<float,N>`` overload. This approach provides ``-Wconversion``
diagnostic notifying the user of the conversion rather than silently altering
precision relative to the other overloads (as FXC does) or generating code
that will fail validation (as DXC does).
1 change: 1 addition & 0 deletions clang/docs/HLSL/HLSLDocs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ HLSL Design and Implementation
.. toctree::
:maxdepth: 1

ExpectedDifferences
HLSLIRReference
ResourceTypes
EntryFunctions
Expand Down
59 changes: 30 additions & 29 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,6 @@ Clang Frontend Potentially Breaking Changes
of ``-Wno-gnu-binary-literal`` will no longer silence this pedantic warning,
which may break existing uses with ``-Werror``.

Target OS macros extension
^^^^^^^^^^^^^^^^^^^^^^^^^^
A new Clang extension (see :ref:`here <target_os_detail>`) is enabled for
Darwin (Apple platform) targets. Clang now defines ``TARGET_OS_*`` macros for
these targets, which could break existing code bases with improper checks for
the ``TARGET_OS_`` macros. For example, existing checks might fail to include
the ``TargetConditionals.h`` header from Apple SDKs and therefore leaving the
macros undefined and guarded code unexercised.

Affected code should be checked to see if it's still intended for the specific
target and fixed accordingly.

The extension can be turned off by the option ``-fno-define-target-os-macros``
as a workaround.

What's New in Clang |release|?
==============================
Some of the major new features and improvements to Clang are listed
Expand All @@ -98,8 +83,6 @@ C++20 Feature Support

- Implemented the `__is_layout_compatible` intrinsic to support
`P0466R5: Layout-compatibility and Pointer-interconvertibility Traits <https://wg21.link/P0466R5>`_.
Note: `CWG2759: [[no_unique_address] and common initial sequence <https://cplusplus.github.io/CWG/issues/2759.html>`_
is not yet implemented.

C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -123,6 +106,10 @@ Resolutions to C++ Defect Reports
of two types.
(`CWG1719: Layout compatibility and cv-qualification revisited <https://cplusplus.github.io/CWG/issues/1719.html>`_).

- ``[[no_unique_address]]`` is now respected when evaluating layout
compatibility of two types.
(`CWG2759: [[no_unique_address] and common initial sequence <https://cplusplus.github.io/CWG/issues/2759.html>`_).

C Language Changes
------------------

Expand Down Expand Up @@ -161,17 +148,6 @@ Non-comprehensive list of changes in this release
New Compiler Flags
------------------

.. _target_os_detail:

Target OS macros extension
^^^^^^^^^^^^^^^^^^^^^^^^^^
A pair of new flags ``-fdefine-target-os-macros`` and
``-fno-define-target-os-macros`` has been added to Clang to enable/disable the
extension to provide built-in definitions of a list of ``TARGET_OS_*`` macros
based on the target triple.

The extension is enabled by default for Darwin (Apple platform) targets.

Deprecated Compiler Flags
-------------------------

Expand Down Expand Up @@ -296,11 +272,17 @@ Bug Fixes to C++ Support
local variable, which is supported as a C11 extension in C++. Previously, it
was only accepted at namespace scope but not at local function scope.
- Clang no longer tries to call consteval constructors at runtime when they appear in a member initializer.
(`#782154 <https://github.com/llvm/llvm-project/issues/82154>`_`)
(`#82154 <https://github.com/llvm/llvm-project/issues/82154>`_`)
- Fix crash when using an immediate-escalated function at global scope.
(`#82258 <https://github.com/llvm/llvm-project/issues/82258>`_)
- Correctly immediate-escalate lambda conversion functions.
(`#82258 <https://github.com/llvm/llvm-project/issues/82258>`_)
- Fixed an issue where template parameters of a nested abbreviated generic lambda within
a requires-clause lie at the same depth as those of the surrounding lambda. This,
in turn, results in the wrong template argument substitution during constraint checking.
(`#78524 <https://github.com/llvm/llvm-project/issues/78524>`_)
- Clang no longer instantiates the exception specification of discarded candidate function
templates when determining the primary template of an explicit specialization.

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -326,6 +308,17 @@ X86 Support
Arm and AArch64 Support
^^^^^^^^^^^^^^^^^^^^^^^

- ARMv7+ targets now default to allowing unaligned access, except Armv6-M, and
Armv8-M without the Main Extension. Baremetal targets should check that the
new default will work with their system configurations, since it requires
that SCTLR.A is 0, SCTLR.U is 1, and that the memory in question is
configured as "normal" memory. This brings Clang in-line with the default
settings for GCC and Arm Compiler. Aside from making Clang align with other
compilers, changing the default brings major performance and code size
improvements for most targets. We have not changed the default behavior for
ARMv6, but may revisit that decision in the future. Users can restore the old
behavior with -m[no-]unaligned-access.

Android Support
^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -408,6 +401,14 @@ Moved checkers
Sanitizers
----------

- ``-fsanitize=signed-integer-overflow`` now instruments signed arithmetic even
when ``-fwrapv`` is enabled. Previously, only division checks were enabled.

Users with ``-fwrapv`` as well as a sanitizer group like
``-fsanitize=undefined`` or ``-fsanitize=integer`` enabled may want to
manually disable potentially noisy signed integer overflow checks with
``-fno-sanitize=signed-integer-overflow``

Python Binding Changes
----------------------

Expand Down
3 changes: 3 additions & 0 deletions clang/docs/StandardCPlusPlusModules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,9 @@ headers to:
...
#endif

If the modules imported by your library provides such headers too, remember to add them to
your ``your_library_imported.h`` too.

Importing modules
~~~~~~~~~~~~~~~~~

Expand Down
9 changes: 5 additions & 4 deletions clang/docs/UndefinedBehaviorSanitizer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,11 @@ Available checks are:
- ``-fsanitize=signed-integer-overflow``: Signed integer overflow, where the
result of a signed integer computation cannot be represented in its type.
This includes all the checks covered by ``-ftrapv``, as well as checks for
signed division overflow (``INT_MIN/-1``), but not checks for
lossy implicit conversions performed before the computation
(see ``-fsanitize=implicit-conversion``). Both of these two issues are
handled by ``-fsanitize=implicit-conversion`` group of checks.
signed division overflow (``INT_MIN/-1``). Note that checks are still
added even when ``-fwrapv`` is enabled. This sanitizer does not check for
lossy implicit conversions performed before the computation (see
``-fsanitize=implicit-conversion``). Both of these two issues are handled
by ``-fsanitize=implicit-conversion`` group of checks.
- ``-fsanitize=unreachable``: If control flow reaches an unreachable
program point.
- ``-fsanitize=unsigned-integer-overflow``: Unsigned integer overflow, where
Expand Down
27 changes: 24 additions & 3 deletions clang/docs/analyzer/checkers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,8 @@ Check for undefined results of binary operators.
core.VLASize (C)
""""""""""""""""
Check for declarations of Variable Length Arrays of undefined or zero size.
Check for declarations of VLA of undefined or zero size.
Check for declarations of Variable Length Arrays (VLA) of undefined, zero or negative
size.
.. code-block:: c
Expand All @@ -229,6 +228,28 @@ Check for declarations of Variable Length Arrays of undefined or zero size.
int vla2[x]; // warn: zero size
}
The checker also gives warning if the `TaintPropagation` checker is switched on
and an unbound, attacker controlled (tainted) value is used to define
the size of the VLA.
.. code-block:: c
void taintedVLA(void) {
int x;
scanf("%d", &x);
int vla[x]; // Declared variable-length array (VLA) has tainted (attacker controlled) size, that can be 0 or negative
}
void taintedVerfieidVLA(void) {
int x;
scanf("%d", &x);
if (x<1)
return;
int vla[x]; // no-warning. The analyzer can prove that x must be positive.
}
.. _core-uninitialized-ArraySubscript:
core.uninitialized.ArraySubscript (C)
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -527,8 +527,10 @@ class SExprBuilder {
BlockInfo *CurrentBlockInfo = nullptr;
};

#ifndef NDEBUG
// Dump an SCFG to llvm::errs().
void printSCFG(CFGWalker &Walker);
#endif // NDEBUG

} // namespace threadSafety
} // namespace clang
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -753,12 +753,9 @@ RecordStorageLocation *getImplicitObjectLocation(const CXXMemberCallExpr &MCE,
RecordStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
const Environment &Env);

/// Returns the fields of a `RecordDecl` that are initialized by an
/// `InitListExpr`, in the order in which they appear in
/// `InitListExpr::inits()`.
/// `Init->getType()` must be a record type.
std::vector<const FieldDecl *>
getFieldsForInitListExpr(const InitListExpr *InitList);
/// Returns the fields of `RD` that are initialized by an `InitListExpr`, in the
/// order in which they appear in `InitListExpr::inits()`.
std::vector<FieldDecl *> getFieldsForInitListExpr(const RecordDecl *RD);

/// Associates a new `RecordValue` with `Loc` and returns the new value.
RecordValue &refreshRecordValue(RecordStorageLocation &Loc, Environment &Env);
Expand Down
12 changes: 6 additions & 6 deletions clang/include/clang/Analysis/FlowSensitive/DataflowValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ class DataflowValues {
//===--------------------------------------------------------------------===//

public:
typedef typename ValueTypes::ValTy ValTy;
typedef typename ValueTypes::AnalysisDataTy AnalysisDataTy;
typedef _AnalysisDirTag AnalysisDirTag;
typedef llvm::DenseMap<ProgramPoint, ValTy> EdgeDataMapTy;
typedef llvm::DenseMap<const CFGBlock*, ValTy> BlockDataMapTy;
typedef llvm::DenseMap<const Stmt*, ValTy> StmtDataMapTy;
using ValTy = typename ValueTypes::ValTy;
using AnalysisDataTy = typename ValueTypes::AnalysisDataTy;
using AnalysisDirTag = _AnalysisDirTag;
using EdgeDataMapTy = llvm::DenseMap<ProgramPoint, ValTy>;
using BlockDataMapTy = llvm::DenseMap<const CFGBlock *, ValTy>;
using StmtDataMapTy = llvm::DenseMap<const Stmt *, ValTy>;

//===--------------------------------------------------------------------===//
// Predicates.
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 @@ -4524,6 +4524,12 @@ def HLSLCreateHandle : LangBuiltin<"HLSL_LANG"> {
let Prototype = "void*(unsigned char)";
}

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

// Builtins for XRay.
def XRayCustomEvent : Builtin {
let Spellings = ["__xray_customevent"];
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticFrontendKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,10 @@ def err_builtin_needs_feature : Error<"%0 needs target feature %1">;
def err_function_needs_feature : Error<
"always_inline function %1 requires target feature '%2', but would "
"be inlined into function %0 that is compiled without support for '%2'">;
def err_function_always_inline_attribute_mismatch : Error<
"always_inline function %1 and its caller %0 have mismatching %2 attributes">;
def err_function_always_inline_new_za : Error<
"always_inline function %0 has new za state">;

def warn_avx_calling_convention
: Warning<"AVX vector %select{return|argument}0 of type %1 without '%2' "
Expand Down
12 changes: 12 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -10364,6 +10364,8 @@ def err_x86_builtin_tile_arg_duplicate : Error<

def err_builtin_target_unsupported : Error<
"builtin is not supported on this target">;
def err_builtin_aix_os_unsupported : Error<
"this builtin is available only on AIX 7.2 and later operating systems">;
def err_builtin_longjmp_unsupported : Error<
"__builtin_longjmp is not supported for the current target">;
def err_builtin_setjmp_unsupported : Error<
Expand Down Expand Up @@ -10758,6 +10760,13 @@ def warn_imp_cast_drops_unaligned : Warning<

} // end of sema category

let CategoryName = "API Notes Issue" in {

def err_incompatible_replacement_type : Error<
"API notes replacement type %0 has a different size from original type %1">;

} // end of API Notes category

let CategoryName = "OpenMP Issue" in {
// OpenMP support.
def err_omp_expected_var_arg : Error<
Expand Down Expand Up @@ -12201,4 +12210,7 @@ def warn_acc_clause_unimplemented
def err_acc_construct_appertainment
: Error<"OpenACC construct '%0' cannot be used here; it can only "
"be used in a statement context">;
def err_acc_branch_in_out_compute_construct
: Error<"invalid %select{branch|return}0 %select{out of|into}1 OpenACC "
"Compute Construct">;
} // end of sema component.
4 changes: 3 additions & 1 deletion clang/include/clang/Basic/DiagnosticSerializationKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ def err_pch_diagopt_mismatch : Error<"%0 is currently enabled, but was not in "
"the PCH file">;
def err_pch_modulecache_mismatch : Error<"PCH was compiled with module cache "
"path '%0', but the path is currently '%1'">;
def err_pch_vfsoverlay_mismatch : Error<"PCH was compiled with different VFS overlay files than are currently in use">;
def warn_pch_vfsoverlay_mismatch : Warning<
"PCH was compiled with different VFS overlay files than are currently in use">,
InGroup<DiagGroup<"pch-vfs-diff">>;
def note_pch_vfsoverlay_files : Note<"%select{PCH|current translation unit}0 has the following VFS overlays:\n%1">;
def note_pch_vfsoverlay_empty : Note<"%select{PCH|current translation unit}0 has no VFS overlays">;

Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/arm_sme.td
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ let TargetGuard = "sme" in {
def SVZERO_MASK_ZA : SInst<"svzero_mask_za", "vi", "", MergeNone, "aarch64_sme_zero",
[IsOverloadNone, IsStreamingCompatible, IsInOutZA],
[ImmCheck<0, ImmCheck0_255>]>;
def SVZERO_ZA : SInst<"svzero_za", "v", "", MergeNone, "aarch64_sme_zero",
def SVZERO_ZA : SInst<"svzero_za", "vv", "", MergeNone, "aarch64_sme_zero",
[IsOverloadNone, IsStreamingCompatible, IsOutZA]>;
}

Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Driver/ToolChain.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ class ToolChain {
RM_Disabled,
};

enum ExceptionsMode {
EM_Enabled,
EM_Disabled,
};

struct BitCodeLibraryInfo {
std::string Path;
bool ShouldInternalize;
Expand All @@ -141,6 +146,8 @@ class ToolChain {

const RTTIMode CachedRTTIMode;

const ExceptionsMode CachedExceptionsMode;

/// The list of toolchain specific path prefixes to search for libraries.
path_list LibraryPaths;

Expand Down Expand Up @@ -318,6 +325,9 @@ class ToolChain {
// Returns the RTTIMode for the toolchain with the current arguments.
RTTIMode getRTTIMode() const { return CachedRTTIMode; }

// Returns the ExceptionsMode for the toolchain with the current arguments.
ExceptionsMode getExceptionsMode() const { return CachedExceptionsMode; }

/// Return any implicit target and/or mode flag for an invocation of
/// the compiler driver as `ProgName`.
///
Expand Down
15 changes: 14 additions & 1 deletion clang/include/clang/Format/Format.h
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,16 @@ struct FormatStyle {
/// \version 17
ShortCaseStatementsAlignmentStyle AlignConsecutiveShortCaseStatements;

/// Style of aligning consecutive TableGen cond operator colons.
/// Align the colons of cases inside !cond operators.
/// \code
/// !cond(!eq(size, 1) : 1,
/// !eq(size, 16): 1,
/// true : 0)
/// \endcode
/// \version 19
AlignConsecutiveStyle AlignConsecutiveTableGenCondOperatorColons;

/// Different styles for aligning escaped newlines.
enum EscapedNewlineAlignmentStyle : int8_t {
/// Don't align escaped newlines.
Expand Down Expand Up @@ -3771,7 +3781,8 @@ struct FormatStyle {
/// \version 17
RemoveParenthesesStyle RemoveParentheses;

/// Remove semicolons after the closing brace of a non-empty function.
/// Remove semicolons after the closing braces of functions and
/// constructors/destructors.
/// \warning
/// Setting this option to ``true`` could lead to incorrect code formatting
/// due to clang-format's lack of complete semantic information. As such,
Expand Down Expand Up @@ -4804,6 +4815,8 @@ struct FormatStyle {
AlignConsecutiveMacros == R.AlignConsecutiveMacros &&
AlignConsecutiveShortCaseStatements ==
R.AlignConsecutiveShortCaseStatements &&
AlignConsecutiveTableGenCondOperatorColons ==
R.AlignConsecutiveTableGenCondOperatorColons &&
AlignEscapedNewlines == R.AlignEscapedNewlines &&
AlignOperands == R.AlignOperands &&
AlignTrailingComments == R.AlignTrailingComments &&
Expand Down
23 changes: 13 additions & 10 deletions clang/include/clang/Frontend/ASTUnit.h
Original file line number Diff line number Diff line change
Expand Up @@ -691,16 +691,19 @@ class ASTUnit {
/// lifetime is expected to extend past that of the returned ASTUnit.
///
/// \returns - The initialized ASTUnit or null if the AST failed to load.
static std::unique_ptr<ASTUnit> LoadFromASTFile(
const std::string &Filename, const PCHContainerReader &PCHContainerRdr,
WhatToLoad ToLoad, IntrusiveRefCntPtr<DiagnosticsEngine> Diags,
const FileSystemOptions &FileSystemOpts,
std::shared_ptr<HeaderSearchOptions> HSOpts, bool OnlyLocalDecls = false,
CaptureDiagsKind CaptureDiagnostics = CaptureDiagsKind::None,
bool AllowASTWithCompilerErrors = false,
bool UserFilesAreVolatile = false,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
llvm::vfs::getRealFileSystem());
static std::unique_ptr<ASTUnit>
LoadFromASTFile(const std::string &Filename,
const PCHContainerReader &PCHContainerRdr, WhatToLoad ToLoad,
IntrusiveRefCntPtr<DiagnosticsEngine> Diags,
const FileSystemOptions &FileSystemOpts,
std::shared_ptr<HeaderSearchOptions> HSOpts,
std::shared_ptr<LangOptions> LangOpts = nullptr,
bool OnlyLocalDecls = false,
CaptureDiagsKind CaptureDiagnostics = CaptureDiagsKind::None,
bool AllowASTWithCompilerErrors = false,
bool UserFilesAreVolatile = false,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
llvm::vfs::getRealFileSystem());

private:
/// Helper function for \c LoadFromCompilerInvocation() and
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Frontend/CompilerInstance.h
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,9 @@ class CompilerInstance : public ModuleLoader {

LangOptions &getLangOpts() { return Invocation->getLangOpts(); }
const LangOptions &getLangOpts() const { return Invocation->getLangOpts(); }
std::shared_ptr<LangOptions> getLangOptsPtr() const {
return Invocation->getLangOptsPtr();
}

PreprocessorOptions &getPreprocessorOpts() {
return Invocation->getPreprocessorOpts();
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Frontend/CompilerInvocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ class CompilerInvocation : public CompilerInvocationBase {
std::shared_ptr<PreprocessorOptions> getPreprocessorOptsPtr() {
return PPOpts;
}
std::shared_ptr<LangOptions> getLangOptsPtr() { return LangOpts; }
/// @}

/// Create a compiler invocation from a list of input options.
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/InstallAPI/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ struct InstallAPIContext {
llvm::Triple TargetTriple{};

/// File Path of output location.
StringRef OutputLoc{};
llvm::StringRef OutputLoc{};

/// What encoding to write output as.
llvm::MachO::FileType FT = llvm::MachO::FileType::TBD_V5;
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/InstallAPI/HeaderFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

namespace clang::installapi {
enum class HeaderType {
/// Unset or unknown type.
Unknown,
/// Represents declarations accessible to all clients.
Public,
/// Represents declarations accessible to a disclosed set of clients.
Expand All @@ -41,6 +43,7 @@ class HeaderFile {
std::optional<clang::Language> Language;

public:
HeaderFile() = delete;
HeaderFile(StringRef FullPath, HeaderType Type,
StringRef IncludeName = StringRef(),
std::optional<clang::Language> Language = std::nullopt)
Expand Down
30 changes: 30 additions & 0 deletions clang/include/clang/Sema/Scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ class Scope {
/// template scope in between), the outer scope does not increase the
/// depth of recursion.
LambdaScope = 0x8000000,
/// This is the scope of an OpenACC Compute Construct, which restricts
/// jumping into/out of it.
OpenACCComputeConstructScope = 0x10000000,
};

private:
Expand Down Expand Up @@ -469,6 +472,14 @@ class Scope {
return false;
}

/// Return true if this scope is a loop.
bool isLoopScope() const {
// 'switch' is the only loop that is not a 'break' scope as well, so we can
// just check BreakScope and not SwitchScope.
return (getFlags() & Scope::BreakScope) &&
!(getFlags() & Scope::SwitchScope);
}

/// Determines whether this scope is the OpenMP directive scope
bool isOpenMPDirectiveScope() const {
return (getFlags() & Scope::OpenMPDirectiveScope);
Expand Down Expand Up @@ -504,6 +515,25 @@ class Scope {
return getFlags() & Scope::OpenMPOrderClauseScope;
}

/// Determine whether this scope is the statement associated with an OpenACC
/// Compute construct directive.
bool isOpenACCComputeConstructScope() const {
return getFlags() & Scope::OpenACCComputeConstructScope;
}

bool isInOpenACCComputeConstructScope() const {
for (const Scope *S = this; S; S = S->getParent()) {
if (S->getFlags() & Scope::OpenACCComputeConstructScope)
return true;
else if (S->getFlags() &
(Scope::FnScope | Scope::ClassScope | Scope::BlockScope |
Scope::TemplateParamScope | Scope::FunctionPrototypeScope |
Scope::AtCatchScope | Scope::ObjCMethodScope))
return false;
}
return false;
}

/// Determine whether this scope is a while/do/for statement, which can have
/// continue statements embedded into it.
bool isContinueScope() const {
Expand Down
11 changes: 10 additions & 1 deletion clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -4883,6 +4883,12 @@ class Sema final {
bool checkCommonAttributeFeatures(const Stmt *S, const ParsedAttr &A,
bool SkipArgCountCheck = false);

/// Map any API notes provided for this declaration to attributes on the
/// declaration.
///
/// Triggered by declaration-attribute processing.
void ProcessAPINotes(Decl *D);

/// 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
Expand Down Expand Up @@ -8540,7 +8546,7 @@ class Sema final {
/// if the arguments are dependent.
ExprResult CheckVarTemplateId(const CXXScopeSpec &SS,
const DeclarationNameInfo &NameInfo,
VarTemplateDecl *Template,
VarTemplateDecl *Template, NamedDecl *FoundD,
SourceLocation TemplateLoc,
const TemplateArgumentListInfo *TemplateArgs);

Expand Down Expand Up @@ -14057,6 +14063,7 @@ class Sema final {
bool CheckPPCBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID,
CallExpr *TheCall);
bool CheckAMDGCNBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
bool CheckHLSLBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
bool CheckRISCVLMUL(CallExpr *TheCall, unsigned ArgNum);
bool CheckRISCVBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID,
CallExpr *TheCall);
Expand Down Expand Up @@ -14122,6 +14129,8 @@ class Sema final {

bool CheckPPCMMAType(QualType Type, SourceLocation TypeLoc);

bool SemaBuiltinVectorMath(CallExpr *TheCall, QualType &Res);
bool SemaBuiltinVectorToScalarMath(CallExpr *TheCall);
bool SemaBuiltinElementwiseMath(CallExpr *TheCall);
bool SemaBuiltinElementwiseTernaryMath(CallExpr *TheCall);
bool PrepareBuiltinElementwiseMathOneArgCall(CallExpr *TheCall);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,16 @@ class SValBuilder {
/// that value is returned. Otherwise, returns NULL.
virtual const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal val) = 0;

/// Tries to get the minimal possible (integer) value of a given SVal. If the
/// constraint manager cannot provide an useful answer, this returns NULL.
/// Tries to get the minimal possible (integer) value of a given SVal. This
/// always returns the value of a ConcreteInt, but may return NULL if the
/// value is symbolic and the constraint manager cannot provide a useful
/// answer.
virtual const llvm::APSInt *getMinValue(ProgramStateRef state, SVal val) = 0;

/// Tries to get the maximal possible (integer) value of a given SVal. If the
/// constraint manager cannot provide an useful answer, this returns NULL.
/// Tries to get the maximal possible (integer) value of a given SVal. This
/// always returns the value of a ConcreteInt, but may return NULL if the
/// value is symbolic and the constraint manager cannot provide a useful
/// answer.
virtual const llvm::APSInt *getMaxValue(ProgramStateRef state, SVal val) = 0;

/// Simplify symbolic expressions within a given SVal. Return an SVal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ enum class ScanningOptimizations {
/// Remove unused -ivfsoverlay arguments.
VFS = 4,

DSS_LAST_BITMASK_ENUM(VFS),
/// Canonicalize -D and -U options.
Macros = 8,

DSS_LAST_BITMASK_ENUM(Macros),
Default = All
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ struct ModuleDeps {
BuildInfo;
};

using PrebuiltModuleVFSMapT = llvm::StringMap<llvm::StringSet<>>;

class ModuleDepCollector;

/// Callback that records textual includes and direct modular includes/imports
Expand Down Expand Up @@ -214,6 +216,7 @@ class ModuleDepCollector final : public DependencyCollector {
CompilerInstance &ScanInstance, DependencyConsumer &C,
DependencyActionController &Controller,
CompilerInvocation OriginalCI,
PrebuiltModuleVFSMapT PrebuiltModuleVFSMap,
ScanningOptimizations OptimizeArgs, bool EagerLoadModules,
bool IsStdModuleP1689Format);

Expand All @@ -233,6 +236,8 @@ class ModuleDepCollector final : public DependencyCollector {
DependencyConsumer &Consumer;
/// Callbacks for computing dependency information.
DependencyActionController &Controller;
/// Mapping from prebuilt AST files to their sorted list of VFS overlay files.
PrebuiltModuleVFSMapT PrebuiltModuleVFSMap;
/// Path to the main source file.
std::string MainFile;
/// Hash identifying the compilation conditions of the current TU.
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ add_clang_library(clangAST
Interp/Record.cpp
Interp/Source.cpp
Interp/State.cpp
Interp/InterpShared.cpp
ItaniumCXXABI.cpp
ItaniumMangle.cpp
JSONNodeDumper.cpp
Expand Down
162 changes: 118 additions & 44 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
#include "Context.h"
#include "Floating.h"
#include "Function.h"
#include "InterpShared.h"
#include "PrimType.h"
#include "Program.h"
#include "clang/AST/Attr.h"

using namespace clang;
using namespace clang::interp;
Expand Down Expand Up @@ -658,19 +660,16 @@ bool ByteCodeExprGen<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
return false;
}

// Both LHS and RHS might _not_ be of complex type, but one of them
// needs to be.
const Expr *LHS = E->getLHS();
const Expr *RHS = E->getRHS();
PrimType LHSElemT = this->classifyComplexElementType(LHS->getType());
PrimType RHSElemT = this->classifyComplexElementType(RHS->getType());

unsigned LHSOffset = this->allocateLocalPrimitive(LHS, PT_Ptr, true, false);
unsigned RHSOffset = this->allocateLocalPrimitive(RHS, PT_Ptr, true, false);
PrimType ResultElemT = this->classifyComplexElementType(E->getType());
unsigned ResultOffset = ~0u;
if (!this->DiscardResult)
if (!DiscardResult)
ResultOffset = this->allocateLocalPrimitive(E, PT_Ptr, true, false);

assert(LHSElemT == RHSElemT);

// Save result pointer in ResultOffset
if (!this->DiscardResult) {
if (!this->emitDupPtr(E))
Expand All @@ -680,16 +679,64 @@ bool ByteCodeExprGen<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
}

// Evaluate LHS and save value to LHSOffset.
if (!this->visit(LHS))
return false;
if (!this->emitSetLocal(PT_Ptr, LHSOffset, E))
return false;
bool LHSIsComplex;
unsigned LHSOffset;
if (LHS->getType()->isAnyComplexType()) {
LHSIsComplex = true;
LHSOffset = this->allocateLocalPrimitive(LHS, PT_Ptr, true, false);
if (!this->visit(LHS))
return false;
if (!this->emitSetLocal(PT_Ptr, LHSOffset, E))
return false;
} else {
LHSIsComplex = false;
PrimType LHST = classifyPrim(LHS->getType());
LHSOffset = this->allocateLocalPrimitive(LHS, LHST, true, false);
if (!this->visit(LHS))
return false;
if (!this->emitSetLocal(LHST, LHSOffset, E))
return false;
}

// Same with RHS.
if (!this->visit(RHS))
return false;
if (!this->emitSetLocal(PT_Ptr, RHSOffset, E))
return false;
bool RHSIsComplex;
unsigned RHSOffset;
if (RHS->getType()->isAnyComplexType()) {
RHSIsComplex = true;
RHSOffset = this->allocateLocalPrimitive(RHS, PT_Ptr, true, false);
if (!this->visit(RHS))
return false;
if (!this->emitSetLocal(PT_Ptr, RHSOffset, E))
return false;
} else {
RHSIsComplex = false;
PrimType RHST = classifyPrim(RHS->getType());
RHSOffset = this->allocateLocalPrimitive(RHS, RHST, true, false);
if (!this->visit(RHS))
return false;
if (!this->emitSetLocal(RHST, RHSOffset, E))
return false;
}

// For both LHS and RHS, either load the value from the complex pointer, or
// directly from the local variable. For index 1 (i.e. the imaginary part),
// just load 0 and do the operation anyway.
auto loadComplexValue = [this](bool IsComplex, unsigned ElemIndex,
unsigned Offset, const Expr *E) -> bool {
if (IsComplex) {
if (!this->emitGetLocal(PT_Ptr, Offset, E))
return false;
if (!this->emitConstUint8(ElemIndex, E))
return false;
if (!this->emitArrayElemPtrPopUint8(E))
return false;
return this->emitLoadPop(classifyComplexElementType(E->getType()), E);
}
if (ElemIndex == 0)
return this->emitGetLocal(classifyPrim(E->getType()), Offset, E);
return this->visitZeroInitializer(classifyPrim(E->getType()), E->getType(),
E);
};

// Now we can get pointers to the LHS and RHS from the offsets above.
BinaryOperatorKind Op = E->getOpcode();
Expand All @@ -700,41 +747,29 @@ bool ByteCodeExprGen<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
return false;
}

if (!this->emitGetLocal(PT_Ptr, LHSOffset, E))
return false;
if (!this->emitConstUint8(ElemIndex, E))
return false;
if (!this->emitArrayElemPtrPopUint8(E))
return false;
if (!this->emitLoadPop(LHSElemT, E))
if (!loadComplexValue(LHSIsComplex, ElemIndex, LHSOffset, LHS))
return false;

if (!this->emitGetLocal(PT_Ptr, RHSOffset, E))
return false;
if (!this->emitConstUint8(ElemIndex, E))
return false;
if (!this->emitArrayElemPtrPopUint8(E))
return false;
if (!this->emitLoadPop(RHSElemT, E))
if (!loadComplexValue(RHSIsComplex, ElemIndex, RHSOffset, RHS))
return false;

// The actual operation.
switch (Op) {
case BO_Add:
if (LHSElemT == PT_Float) {
if (ResultElemT == PT_Float) {
if (!this->emitAddf(getRoundingMode(E), E))
return false;
} else {
if (!this->emitAdd(LHSElemT, E))
if (!this->emitAdd(ResultElemT, E))
return false;
}
break;
case BO_Sub:
if (LHSElemT == PT_Float) {
if (ResultElemT == PT_Float) {
if (!this->emitSubf(getRoundingMode(E), E))
return false;
} else {
if (!this->emitSub(LHSElemT, E))
if (!this->emitSub(ResultElemT, E))
return false;
}
break;
Expand All @@ -745,10 +780,10 @@ bool ByteCodeExprGen<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {

if (!this->DiscardResult) {
// Initialize array element with the value we just computed.
if (!this->emitInitElemPop(LHSElemT, ElemIndex, E))
if (!this->emitInitElemPop(ResultElemT, ElemIndex, E))
return false;
} else {
if (!this->emitPop(LHSElemT, E))
if (!this->emitPop(ResultElemT, E))
return false;
}
}
Expand Down Expand Up @@ -1644,13 +1679,24 @@ bool ByteCodeExprGen<Emitter>::VisitCompoundLiteralExpr(

std::optional<PrimType> T = classify(E->getType());
if (E->isFileScope()) {
// Avoid creating a variable if this is a primitive RValue anyway.
if (T && !E->isLValue())
return this->delegate(Init);

if (std::optional<unsigned> GlobalIndex = P.createGlobal(E)) {
if (classify(E->getType()))
return this->visit(Init);
if (!this->emitGetPtrGlobal(*GlobalIndex, E))
return false;

if (T) {
if (!this->visit(Init))
return false;
return this->emitInitGlobal(*T, *GlobalIndex, E);
}

return this->visitInitializer(Init);
}

return false;
}

// Otherwise, use a local variable.
Expand Down Expand Up @@ -1696,6 +1742,9 @@ bool ByteCodeExprGen<Emitter>::VisitLambdaExpr(const LambdaExpr *E) {
const Expr *Init = *CaptureInitIt;
++CaptureInitIt;

if (!Init)
continue;

if (std::optional<PrimType> T = classify(Init)) {
if (!this->visit(Init))
return false;
Expand Down Expand Up @@ -2510,6 +2559,12 @@ template <class Emitter>
bool ByteCodeExprGen<Emitter>::visitDecl(const VarDecl *VD) {
assert(!VD->isInvalidDecl() && "Trying to constant evaluate an invalid decl");

// Global variable we've already seen but that's uninitialized means
// evaluating the initializer failed. Just return failure.
if (std::optional<unsigned> Index = P.getGlobal(VD);
Index && !P.getPtrGlobal(*Index).isInitialized())
return false;

// Create and initialize the variable.
if (!this->visitVarDecl(VD))
return false;
Expand Down Expand Up @@ -2650,6 +2705,7 @@ bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
QualType ReturnType = E->getCallReturnType(Ctx.getASTContext());
std::optional<PrimType> T = classify(ReturnType);
bool HasRVO = !ReturnType->isVoidType() && !T;
const FunctionDecl *FuncDecl = E->getDirectCallee();

if (HasRVO) {
if (DiscardResult) {
Expand All @@ -2661,23 +2717,29 @@ bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
return false;
}
} else {
assert(Initializing);
// We need the result. Prepare a pointer to return or
// dup the current one.
if (!Initializing) {
if (std::optional<unsigned> LocalIndex = allocateLocal(E)) {
if (!this->emitGetPtrLocal(*LocalIndex, E))
return false;
}
}
if (!this->emitDupPtr(E))
return false;
}
}

auto Args = E->arguments();
auto Args = llvm::ArrayRef(E->getArgs(), E->getNumArgs());
// Calling a static operator will still
// pass the instance, but we don't need it.
// Discard it here.
if (isa<CXXOperatorCallExpr>(E)) {
if (const auto *MD =
dyn_cast_if_present<CXXMethodDecl>(E->getDirectCallee());
if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>(FuncDecl);
MD && MD->isStatic()) {
if (!this->discard(E->getArg(0)))
return false;
Args = drop_begin(Args, 1);
Args = Args.drop_front();
}
}

Expand All @@ -2687,13 +2749,25 @@ bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
return false;
}

llvm::BitVector NonNullArgs = collectNonNullArgs(FuncDecl, Args);
// Put arguments on the stack.
unsigned ArgIndex = 0;
for (const auto *Arg : Args) {
if (!this->visit(Arg))
return false;

// If we know the callee already, check the known parametrs for nullability.
if (FuncDecl && NonNullArgs[ArgIndex]) {
PrimType ArgT = classify(Arg).value_or(PT_Ptr);
if (ArgT == PT_Ptr || ArgT == PT_FnPtr) {
if (!this->emitCheckNonNullArg(ArgT, Arg))
return false;
}
}
++ArgIndex;
}

if (const FunctionDecl *FuncDecl = E->getDirectCallee()) {
if (FuncDecl) {
const Function *Func = getFunction(FuncDecl);
if (!Func)
return false;
Expand Down Expand Up @@ -2742,7 +2816,7 @@ bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
if (!this->visit(E->getCallee()))
return false;

if (!this->emitCallPtr(ArgSize, E))
if (!this->emitCallPtr(ArgSize, E, E))
return false;
}

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ std::optional<PrimType> Context::classify(QualType T) const {
if (T->isReferenceType() || T->isPointerType())
return PT_Ptr;

if (const auto *AT = dyn_cast<AtomicType>(T))
if (const auto *AT = T->getAs<AtomicType>())
return classify(AT->getValueType());

if (const auto *DT = dyn_cast<DecltypeType>(T))
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/Descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ struct Descriptor final {
bool isRecord() const { return !IsArray && ElemRecord; }
/// Checks if this is a dummy descriptor.
bool isDummy() const { return IsDummy; }

void dump() const;
void dump(llvm::raw_ostream &OS) const;
};

/// Bitfield tracking the initialisation status of elements of primitive arrays.
Expand Down
79 changes: 72 additions & 7 deletions clang/lib/AST/Interp/Disasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "Opcode.h"
#include "PrimType.h"
#include "Program.h"
#include "clang/AST/ASTDumperUtils.h"
#include "clang/AST/DeclCXX.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Format.h"
Expand Down Expand Up @@ -55,7 +56,10 @@ inline IntegralAP<true> ReadArg<IntegralAP<true>>(Program &P, CodePtr &OpPC) {
LLVM_DUMP_METHOD void Function::dump() const { dump(llvm::errs()); }

LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const {
OS << getName() << " " << (const void *)this << "\n";
{
ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_GREEN, true});
OS << getName() << " " << (const void *)this << "\n";
}
OS << "frame size: " << getFrameSize() << "\n";
OS << "arg size: " << getArgSize() << "\n";
OS << "rvo: " << hasRVO() << "\n";
Expand Down Expand Up @@ -83,14 +87,75 @@ LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const {
LLVM_DUMP_METHOD void Program::dump() const { dump(llvm::errs()); }

LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const {
OS << ":: Program\n";
OS << "Global Variables: " << Globals.size() << "\n";
OS << "Functions: " << Funcs.size() << "\n";
OS << "\n";
for (auto &Func : Funcs) {
{
ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_RED, true});
OS << "\n:: Program\n";
}

{
ColorScope SC(OS, true, {llvm::raw_ostream::WHITE, true});
OS << "Total memory : " << Allocator.getTotalMemory() << " bytes\n";
OS << "Global Variables: " << Globals.size() << "\n";
}
unsigned GI = 0;
for (const Global *G : Globals) {
const Descriptor *Desc = G->block()->getDescriptor();
OS << GI << ": " << (void *)G->block() << " ";
{
Pointer GP = getPtrGlobal(GI);
ColorScope SC(OS, true,
GP.isInitialized()
? TerminalColor{llvm::raw_ostream::GREEN, false}
: TerminalColor{llvm::raw_ostream::RED, false});
OS << (GP.isInitialized() ? "initialized " : "uninitialized ");
}
Desc->dump(OS);
OS << "\n";
++GI;
}

{
ColorScope SC(OS, true, {llvm::raw_ostream::WHITE, true});
OS << "Functions: " << Funcs.size() << "\n";
}
for (const auto &Func : Funcs) {
Func.second->dump();
}
for (auto &Anon : AnonFuncs) {
for (const auto &Anon : AnonFuncs) {
Anon->dump();
}
}

LLVM_DUMP_METHOD void Descriptor::dump() const {
dump(llvm::errs());
llvm::errs() << '\n';
}

LLVM_DUMP_METHOD void Descriptor::dump(llvm::raw_ostream &OS) const {
// Source
{
ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true});
if (const auto *ND = dyn_cast_if_present<NamedDecl>(asDecl()))
OS << ND->getName();
else if (asExpr())
OS << "expr (TODO)";
}

// Print a few interesting bits about the descriptor.
if (isPrimitiveArray())
OS << " primitive-array";
else if (isCompositeArray())
OS << " composite-array";
else if (isRecord())
OS << " record";
else if (isPrimitive())
OS << " primitive";

if (isZeroSizeArray())
OS << " zero-size-arrary";
else if (isUnknownSizeArray())
OS << " unknown-size-array";

if (isDummy())
OS << " dummy";
}
5 changes: 4 additions & 1 deletion clang/lib/AST/Interp/Function.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
#ifndef LLVM_CLANG_AST_INTERP_FUNCTION_H
#define LLVM_CLANG_AST_INTERP_FUNCTION_H

#include "Source.h"
#include "Descriptor.h"
#include "Source.h"
#include "clang/AST/ASTLambda.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "llvm/Support/raw_ostream.h"

Expand Down Expand Up @@ -108,6 +109,8 @@ class Function final {
/// Checks if the first argument is a RVO pointer.
bool hasRVO() const { return HasRVO; }

bool hasNonNullAttr() const { return getDecl()->hasAttr<NonNullAttr>(); }

/// Range over the scope blocks.
llvm::iterator_range<llvm::SmallVector<Scope, 2>::const_iterator>
scopes() const {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/FunctionPointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class FunctionPointer final {
FunctionPointer(const Function *Func) : Func(Func) { assert(Func); }

const Function *getFunction() const { return Func; }
bool isZero() const { return !Func; }

APValue toAPValue() const {
if (!Func)
Expand Down
33 changes: 31 additions & 2 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@
//===----------------------------------------------------------------------===//

#include "Interp.h"
#include <limits>
#include <vector>
#include "Function.h"
#include "InterpFrame.h"
#include "InterpShared.h"
#include "InterpStack.h"
#include "Opcode.h"
#include "PrimType.h"
Expand All @@ -22,6 +21,10 @@
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "llvm/ADT/APSInt.h"
#include <limits>
#include <vector>

using namespace clang;

using namespace clang;
using namespace clang::interp;
Expand Down Expand Up @@ -462,6 +465,10 @@ bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
if (S.getLangOpts().CPlusPlus11) {
const FunctionDecl *DiagDecl = F->getDecl();

// Invalid decls have been diagnosed before.
if (DiagDecl->isInvalidDecl())
return false;

// If this function is not constexpr because it is an inherited
// non-constexpr constructor, diagnose that directly.
const auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl);
Expand Down Expand Up @@ -618,6 +625,28 @@ bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR) {
return false;
}

bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F,
const CallExpr *CE, unsigned ArgSize) {
auto Args = llvm::ArrayRef(CE->getArgs(), CE->getNumArgs());
auto NonNullArgs = collectNonNullArgs(F->getDecl(), Args);
unsigned Offset = 0;
unsigned Index = 0;
for (const Expr *Arg : Args) {
if (NonNullArgs[Index] && Arg->getType()->isPointerType()) {
const Pointer &ArgPtr = S.Stk.peek<Pointer>(ArgSize - Offset);
if (ArgPtr.isZero()) {
const SourceLocation &Loc = S.Current->getLocation(OpPC);
S.CCEDiag(Loc, diag::note_non_null_attribute_failed);
return false;
}
}

Offset += align(primSize(S.Ctx.classify(Arg).value_or(PT_Ptr)));
++Index;
}
return true;
}

bool Interpret(InterpState &S, APValue &Result) {
// The current stack frame when we started Interpret().
// This is being used by the ops to determine wheter
Expand Down
37 changes: 34 additions & 3 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This);
/// Checks if a method is pure virtual.
bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD);

/// Checks if all the arguments annotated as 'nonnull' are in fact not null.
bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F,
const CallExpr *CE, unsigned ArgSize);

/// Sets the given integral value to the pointer, which is of
/// a std::{weak,partial,strong}_ordering type.
bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC,
Expand Down Expand Up @@ -804,7 +808,8 @@ bool CMP3(InterpState &S, CodePtr OpPC, const ComparisonCategoryInfo *CmpInfo) {
}

assert(CmpInfo);
const auto *CmpValueInfo = CmpInfo->getValueInfo(CmpResult);
const auto *CmpValueInfo =
CmpInfo->getValueInfo(CmpInfo->makeWeakResult(CmpResult));
assert(CmpValueInfo);
assert(CmpValueInfo->hasValidIntValue());
const APSInt &IntValue = CmpValueInfo->getIntValue();
Expand Down Expand Up @@ -1043,7 +1048,7 @@ bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {

/// 1) Converts the value on top of the stack to an APValue
/// 2) Sets that APValue on \Temp
/// 3) Initialized global with index \I with that
/// 3) Initializes global with index \I with that
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitGlobalTemp(InterpState &S, CodePtr OpPC, uint32_t I,
const LifetimeExtendedTemporaryDecl *Temp) {
Expand Down Expand Up @@ -1980,6 +1985,7 @@ inline bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,

return false;
}

inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
uint32_t VarArgSize) {
if (Func->hasThisPointer()) {
Expand Down Expand Up @@ -2083,12 +2089,25 @@ inline bool CallBI(InterpState &S, CodePtr &PC, const Function *Func,
return false;
}

inline bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize) {
inline bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
const CallExpr *CE) {
const FunctionPointer &FuncPtr = S.Stk.pop<FunctionPointer>();

const Function *F = FuncPtr.getFunction();
if (!F) {
const Expr *E = S.Current->getExpr(OpPC);
S.FFDiag(E, diag::note_constexpr_null_callee)
<< const_cast<Expr *>(E) << E->getSourceRange();
return false;
}
assert(F);

// Check argument nullability state.
if (F->hasNonNullAttr()) {
if (!CheckNonNullArgs(S, OpPC, F, CE, ArgSize))
return false;
}

assert(ArgSize >= F->getWrittenArgSize());
uint32_t VarArgSize = ArgSize - F->getWrittenArgSize();

Expand Down Expand Up @@ -2145,6 +2164,18 @@ inline bool OffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E) {
return true;
}

template <PrimType Name, class T = typename PrimConv<Name>::T>
inline bool CheckNonNullArg(InterpState &S, CodePtr OpPC) {
const T &Arg = S.Stk.peek<T>();
if (!Arg.isZero())
return true;

const SourceLocation &Loc = S.Current->getLocation(OpPC);
S.CCEDiag(Loc, diag::note_non_null_attribute_failed);

return false;
}

//===----------------------------------------------------------------------===//
// Read opcode arguments
//===----------------------------------------------------------------------===//
Expand Down
289 changes: 156 additions & 133 deletions clang/lib/AST/Interp/InterpBuiltin.cpp

Large diffs are not rendered by default.

42 changes: 42 additions & 0 deletions clang/lib/AST/Interp/InterpShared.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//===--- InterpShared.cpp ---------------------------------------*- 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
//
//===----------------------------------------------------------------------===//

#include "InterpShared.h"
#include "clang/AST/Attr.h"
#include "llvm/ADT/BitVector.h"

namespace clang {
namespace interp {

llvm::BitVector collectNonNullArgs(const FunctionDecl *F,
const llvm::ArrayRef<const Expr *> &Args) {
llvm::BitVector NonNullArgs;
if (!F)
return NonNullArgs;

assert(F);
NonNullArgs.resize(Args.size());

for (const auto *Attr : F->specific_attrs<NonNullAttr>()) {
if (!Attr->args_size()) {
NonNullArgs.set();
break;
} else
for (auto Idx : Attr->args()) {
unsigned ASTIdx = Idx.getASTIndex();
if (ASTIdx >= Args.size())
continue;
NonNullArgs[ASTIdx] = true;
}
}

return NonNullArgs;
}

} // namespace interp
} // namespace clang
26 changes: 26 additions & 0 deletions clang/lib/AST/Interp/InterpShared.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//===--- InterpShared.h -----------------------------------------*- 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_LIB_AST_INTERP_SHARED_H
#define LLVM_CLANG_LIB_AST_INTERP_SHARED_H

#include "llvm/ADT/BitVector.h"

namespace clang {
class FunctionDecl;
class Expr;

namespace interp {

llvm::BitVector collectNonNullArgs(const FunctionDecl *F,
const llvm::ArrayRef<const Expr *> &Args);

} // namespace interp
} // namespace clang

#endif
7 changes: 6 additions & 1 deletion clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ def CallBI : Opcode {
}

def CallPtr : Opcode {
let Args = [ArgUint32];
let Args = [ArgUint32, ArgCallExpr];
let Types = [];
}

Expand Down Expand Up @@ -706,3 +706,8 @@ def InvalidDeclRef : Opcode {
}

def ArrayDecay : Opcode;

def CheckNonNullArg : Opcode {
let Types = [PtrTypeClass];
let HasGroup = 1;
}
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/Program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ unsigned Program::createGlobalString(const StringLiteral *S) {
return I;
}

Pointer Program::getPtrGlobal(unsigned Idx) {
Pointer Program::getPtrGlobal(unsigned Idx) const {
assert(Idx < Globals.size());
return Pointer(Globals[Idx]->block());
}
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/AST/Interp/Program.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class Program final {
unsigned createGlobalString(const StringLiteral *S);

/// Returns a pointer to a global.
Pointer getPtrGlobal(unsigned Idx);
Pointer getPtrGlobal(unsigned Idx) const;

/// Returns the value of a global.
Block *getGlobal(unsigned Idx) {
Expand Down Expand Up @@ -190,6 +190,7 @@ class Program final {
std::byte *data() { return B.data(); }
/// Return a pointer to the block.
Block *block() { return &B; }
const Block *block() const { return &B; }

private:
/// Required metadata - does not actually track pointers.
Expand Down
31 changes: 29 additions & 2 deletions clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,35 @@ buildStmtToBasicBlockMap(const CFG &Cfg) {

StmtToBlock[Stmt->getStmt()] = Block;
}
if (const Stmt *TerminatorStmt = Block->getTerminatorStmt())
StmtToBlock[TerminatorStmt] = Block;
}
// Some terminator conditions don't appear as a `CFGElement` anywhere else -
// for example, this is true if the terminator condition is a `&&` or `||`
// operator.
// We associate these conditions with the block the terminator appears in,
// but only if the condition has not already appeared as a regular
// `CFGElement`. (The `insert()` below does nothing if the key already exists
// in the map.)
for (const CFGBlock *Block : Cfg) {
if (Block != nullptr)
if (const Stmt *TerminatorCond = Block->getTerminatorCondition())
StmtToBlock.insert({TerminatorCond, Block});
}
// Terminator statements typically don't appear as a `CFGElement` anywhere
// else, so we want to associate them with the block that they terminate.
// However, there are some important special cases:
// - The conditional operator is a type of terminator, but it also appears
// as a regular `CFGElement`, and we want to associate it with the block
// in which it appears as a `CFGElement`.
// - The `&&` and `||` operators are types of terminators, but like the
// conditional operator, they can appear as a regular `CFGElement` or
// as a terminator condition (see above).
// We process terminators last to make sure that we only associate them with
// the block they terminate if they haven't previously occurred as a regular
// `CFGElement` or as a terminator condition.
for (const CFGBlock *Block : Cfg) {
if (Block != nullptr)
if (const Stmt *TerminatorStmt = Block->getTerminatorStmt())
StmtToBlock.insert({TerminatorStmt, Block});
}
return StmtToBlock;
}
Expand Down
18 changes: 4 additions & 14 deletions clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,8 @@ getFieldsGlobalsAndFuncs(const Stmt &S, FieldSet &Fields,
if (const auto *FD = dyn_cast<FieldDecl>(VD))
Fields.insert(FD);
} else if (auto *InitList = dyn_cast<InitListExpr>(&S)) {
if (InitList->getType()->isRecordType())
for (const auto *FD : getFieldsForInitListExpr(InitList))
if (RecordDecl *RD = InitList->getType()->getAsRecordDecl())
for (const auto *FD : getFieldsForInitListExpr(RD))
Fields.insert(FD);
}
}
Expand Down Expand Up @@ -1104,22 +1104,12 @@ RecordStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
return Env.get<RecordStorageLocation>(*Base);
}

std::vector<const FieldDecl *>
getFieldsForInitListExpr(const InitListExpr *InitList) {
const RecordDecl *RD = InitList->getType()->getAsRecordDecl();
assert(RD != nullptr);

std::vector<const FieldDecl *> Fields;

if (InitList->getType()->isUnionType()) {
Fields.push_back(InitList->getInitializedFieldInUnion());
return Fields;
}

std::vector<FieldDecl *> getFieldsForInitListExpr(const RecordDecl *RD) {
// Unnamed bitfields are only used for padding and do not appear in
// `InitListExpr`'s inits. However, those fields do appear in `RecordDecl`'s
// field list, and we thus need to remove them before mapping inits to
// fields to avoid mapping inits to the wrongs fields.
std::vector<FieldDecl *> Fields;
llvm::copy_if(
RD->fields(), std::back_inserter(Fields),
[](const FieldDecl *Field) { return !Field->isUnnamedBitfield(); });
Expand Down
31 changes: 14 additions & 17 deletions clang/lib/Analysis/FlowSensitive/Transfer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -663,10 +663,17 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
void VisitInitListExpr(const InitListExpr *S) {
QualType Type = S->getType();

if (!Type->isRecordType()) {
// Until array initialization is implemented, we don't need to care about
// cases where `getNumInits() > 1`.
if (S->getNumInits() == 1)
if (Type->isUnionType()) {
// FIXME: Initialize unions properly.
if (auto *Val = Env.createValue(Type))
Env.setValue(*S, *Val);
return;
}

if (!Type->isStructureOrClassType()) {
// Until array initialization is implemented, we skip arrays and don't
// need to care about cases where `getNumInits() > 1`.
if (!Type->isArrayType() && S->getNumInits() == 1)
propagateValueOrStorageLocation(*S->getInit(0), *S, Env);
return;
}
Expand All @@ -681,9 +688,10 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs;

// This only contains the direct fields for the given type.
std::vector<const FieldDecl *> FieldsForInit = getFieldsForInitListExpr(S);
std::vector<FieldDecl *> FieldsForInit =
getFieldsForInitListExpr(Type->getAsRecordDecl());

// `S->inits()` contains all the initializer expressions, including the
// `S->inits()` contains all the initializer epressions, including the
// ones for direct base classes.
auto Inits = S->inits();
size_t InitIdx = 0;
Expand Down Expand Up @@ -723,17 +731,6 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
FieldLocs.insert({Field, &Loc});
}

// In the case of a union, we don't in general have initializers for all
// of the fields. Create storage locations for the remaining fields (but
// don't associate them with values).
if (Type->isUnionType()) {
for (const FieldDecl *Field :
Env.getDataflowAnalysisContext().getModeledFields(Type)) {
if (auto [it, inserted] = FieldLocs.insert({Field, nullptr}); inserted)
it->second = &Env.createStorageLocation(Field->getType());
}
}

// Check that we satisfy the invariant that a `RecordStorageLoation`
// contains exactly the set of modeled fields for that type.
// `ModeledFields` includes fields from all the bases, but only the
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Analysis/ThreadSafetyCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -995,7 +995,7 @@ void SExprBuilder::exitCFG(const CFGBlock *Last) {
IncompleteArgs.clear();
}

/*
#ifndef NDEBUG
namespace {

class TILPrinter :
Expand All @@ -1016,4 +1016,4 @@ void printSCFG(CFGWalker &Walker) {

} // namespace threadSafety
} // namespace clang
*/
#endif // NDEBUG
8 changes: 7 additions & 1 deletion clang/lib/Basic/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,13 @@ StringRef AArch64TargetInfo::getFeatureDependencies(StringRef Name) const {
}

bool AArch64TargetInfo::validateCpuSupports(StringRef FeatureStr) const {
return llvm::AArch64::parseArchExtension(FeatureStr).has_value();
// CPU features might be separated by '+', extract them and check
llvm::SmallVector<StringRef, 8> Features;
FeatureStr.split(Features, "+");
for (auto &Feature : Features)
if (!llvm::AArch64::parseArchExtension(Feature.trim()).has_value())
return false;
return true;
}

bool AArch64TargetInfo::hasFeature(StringRef Feature) const {
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Basic/Targets/AArch64.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo {
DiagnosticsEngine &Diags) override;
ParsedTargetAttr parseTargetAttr(StringRef Str) const override;
bool supportsTargetAttributeTune() const override { return true; }

bool supportsCpuSupports() const override { return true; }
bool checkArithmeticFenceSupported() const override { return true; }

bool hasBFloat16Type() const override;
Expand Down
6 changes: 4 additions & 2 deletions clang/lib/Basic/Targets/AMDGPU.h
Original file line number Diff line number Diff line change
Expand Up @@ -414,8 +414,10 @@ class LLVM_LIBRARY_VISIBILITY AMDGPUTargetInfo final : public TargetInfo {
// value ~0.
uint64_t getNullPointerValue(LangAS AS) const override {
// FIXME: Also should handle region.
return (AS == LangAS::opencl_local || AS == LangAS::opencl_private)
? ~0 : 0;
return (AS == LangAS::opencl_local || AS == LangAS::opencl_private ||
AS == LangAS::sycl_local || AS == LangAS::sycl_private)
? ~0
: 0;
}

void setAuxTarget(const TargetInfo *Aux) override;
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/Basic/Targets/PPC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,16 @@ bool PPCTargetInfo::validateCpuSupports(StringRef FeatureStr) const {
}

bool PPCTargetInfo::validateCpuIs(StringRef CPUName) const {
llvm::Triple Triple = getTriple();
if (Triple.isOSAIX()) {
#define PPC_AIX_CPU(NAME, SUPPORT, INDEX, OP, VALUE) .Case(NAME, true)
return llvm::StringSwitch<bool>(CPUName)
#include "llvm/TargetParser/PPCTargetParser.def"
.Default(false);
}

assert(Triple.isOSLinux() &&
"__builtin_cpu_is() is only supported for AIX and Linux.");
#define PPC_LNX_CPU(NAME, NUM) .Case(NAME, true)
return llvm::StringSwitch<bool>(CPUName)
#include "llvm/TargetParser/PPCTargetParser.def"
Expand Down
10 changes: 9 additions & 1 deletion clang/lib/Basic/Targets/PPC.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,16 @@ class LLVM_LIBRARY_VISIBILITY PPCTargetInfo : public TargetInfo {

// We support __builtin_cpu_supports/__builtin_cpu_is on targets that
// have Glibc since it is Glibc that provides the HWCAP[2] in the auxv.
static constexpr int MINIMUM_AIX_OS_MAJOR = 7;
static constexpr int MINIMUM_AIX_OS_MINOR = 2;
bool supportsCpuSupports() const override { return getTriple().isOSGlibc(); }
bool supportsCpuIs() const override { return getTriple().isOSGlibc(); }
bool supportsCpuIs() const override {
llvm::Triple Triple = getTriple();
// AIX 7.2 is the minimum requirement to support __builtin_cpu_is().
return Triple.isOSGlibc() ||
(Triple.isOSAIX() &&
!Triple.isOSVersionLT(MINIMUM_AIX_OS_MAJOR, MINIMUM_AIX_OS_MINOR));
}
bool validateCpuSupports(StringRef Feature) const override;
bool validateCpuIs(StringRef Name) const override;
};
Expand Down
114 changes: 114 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include "llvm/IR/IntrinsicsAMDGPU.h"
#include "llvm/IR/IntrinsicsARM.h"
#include "llvm/IR/IntrinsicsBPF.h"
#include "llvm/IR/IntrinsicsDirectX.h"
#include "llvm/IR/IntrinsicsHexagon.h"
#include "llvm/IR/IntrinsicsNVPTX.h"
#include "llvm/IR/IntrinsicsPowerPC.h"
Expand Down Expand Up @@ -5982,6 +5983,10 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
llvm_unreachable("Bad evaluation kind in EmitBuiltinExpr");
}

// EmitHLSLBuiltinExpr will check getLangOpts().HLSL
if (Value *V = EmitHLSLBuiltinExpr(BuiltinID, E))
return RValue::get(V);

if (getLangOpts().HIPStdPar && getLangOpts().CUDAIsDevice)
return EmitHipStdParUnsupportedBuiltin(this, FD);

Expand Down Expand Up @@ -10638,6 +10643,9 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
BuiltinID <= clang::AArch64::LastSMEBuiltin)
return EmitAArch64SMEBuiltinExpr(BuiltinID, E);

if (BuiltinID == Builtin::BI__builtin_cpu_supports)
return EmitAArch64CpuSupports(E);

unsigned HintID = static_cast<unsigned>(-1);
switch (BuiltinID) {
default: break;
Expand Down Expand Up @@ -14025,6 +14033,19 @@ Value *CodeGenFunction::EmitX86CpuInit() {
return Builder.CreateCall(Func);
}

Value *CodeGenFunction::EmitAArch64CpuSupports(const CallExpr *E) {
const Expr *ArgExpr = E->getArg(0)->IgnoreParenCasts();
StringRef ArgStr = cast<StringLiteral>(ArgExpr)->getString();
llvm::SmallVector<StringRef, 8> Features;
ArgStr.split(Features, "+");
for (auto &Feature : Features) {
Feature = Feature.trim();
if (Feature != "default")
Features.push_back(Feature);
}
return EmitAArch64CpuSupports(Features);
}

llvm::Value *
CodeGenFunction::EmitAArch64CpuSupports(ArrayRef<StringRef> FeaturesStrs) {
uint64_t FeaturesMask = llvm::AArch64::getCpuSupportsMask(FeaturesStrs);
Expand Down Expand Up @@ -16542,12 +16563,59 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID,

Intrinsic::ID ID = Intrinsic::not_intrinsic;

#include "llvm/TargetParser/PPCTargetParser.def"
auto GenAIXPPCBuiltinCpuExpr = [&](unsigned SupportMethod, unsigned FieldIdx,
unsigned CompOp,
unsigned OpValue) -> Value * {
if (SupportMethod == AIX_BUILTIN_PPC_FALSE)
return llvm::ConstantInt::getFalse(ConvertType(E->getType()));

if (SupportMethod == AIX_BUILTIN_PPC_TRUE)
return llvm::ConstantInt::getTrue(ConvertType(E->getType()));

assert(SupportMethod <= USE_SYS_CONF && "Invalid value for SupportMethod.");
assert((CompOp == COMP_EQ) && "Only equal comparisons are supported.");

llvm::Type *STy = llvm::StructType::get(PPC_SYSTEMCONFIG_TYPE);
llvm::Constant *SysConf =
CGM.CreateRuntimeVariable(STy, "_system_configuration");

// Grab the appropriate field from _system_configuration.
llvm::Value *Idxs[] = {ConstantInt::get(Int32Ty, 0),
ConstantInt::get(Int32Ty, FieldIdx)};

llvm::Value *FieldValue = Builder.CreateGEP(STy, SysConf, Idxs);
FieldValue = Builder.CreateAlignedLoad(Int32Ty, FieldValue,
CharUnits::fromQuantity(4));
assert(FieldValue->getType()->isIntegerTy(32) &&
"Only 32-bit integers are supported in GenAIXPPCBuiltinCpuExpr().");
return Builder.CreateICmp(ICmpInst::ICMP_EQ, FieldValue,
ConstantInt::get(Int32Ty, OpValue));
};

switch (BuiltinID) {
default: return nullptr;

case Builtin::BI__builtin_cpu_is: {
const Expr *CPUExpr = E->getArg(0)->IgnoreParenCasts();
StringRef CPUStr = cast<clang::StringLiteral>(CPUExpr)->getString();
llvm::Triple Triple = getTarget().getTriple();

if (Triple.isOSAIX()) {
unsigned IsCpuSupport, FieldIdx, CompareOp, CpuIdValue;
typedef std::tuple<unsigned, unsigned, unsigned, unsigned> CPUType;
std::tie(IsCpuSupport, FieldIdx, CompareOp, CpuIdValue) =
static_cast<CPUType>(StringSwitch<CPUType>(CPUStr)
#define PPC_AIX_CPU(NAME, SUPPORT_MAGIC, INDEX, COMPARE_OP, VALUE) \
.Case(NAME, {SUPPORT_MAGIC, INDEX, COMPARE_OP, VALUE})
#include "llvm/TargetParser/PPCTargetParser.def"
);
return GenAIXPPCBuiltinCpuExpr(IsCpuSupport, FieldIdx, CompareOp,
CpuIdValue);
}

assert(Triple.isOSLinux() &&
"__builtin_cpu_is() is only supported for AIX and Linux.");
unsigned NumCPUID = StringSwitch<unsigned>(CPUStr)
#define PPC_LNX_CPU(Name, NumericID) .Case(Name, NumericID)
#include "llvm/TargetParser/PPCTargetParser.def"
Expand Down Expand Up @@ -17896,6 +17964,52 @@ llvm::Value *CodeGenFunction::EmitScalarOrConstFoldImmArg(unsigned ICEArguments,
return Arg;
}

Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
const CallExpr *E) {
if (!getLangOpts().HLSL)
return nullptr;

switch (BuiltinID) {
case Builtin::BI__builtin_hlsl_dot: {
Value *Op0 = EmitScalarExpr(E->getArg(0));
Value *Op1 = EmitScalarExpr(E->getArg(1));
llvm::Type *T0 = Op0->getType();
llvm::Type *T1 = Op1->getType();
if (!T0->isVectorTy() && !T1->isVectorTy()) {
if (T0->isFloatingPointTy())
return Builder.CreateFMul(Op0, Op1, "dx.dot");

if (T0->isIntegerTy())
return Builder.CreateMul(Op0, Op1, "dx.dot");

// Bools should have been promoted
llvm_unreachable(
"Scalar dot product is only supported on ints and floats.");
}
// A VectorSplat should have happened
assert(T0->isVectorTy() && T1->isVectorTy() &&
"Dot product of vector and scalar is not supported.");

// A vector sext or sitofp should have happened
assert(T0->getScalarType() == T1->getScalarType() &&
"Dot product of vectors need the same element types.");

[[maybe_unused]] auto *VecTy0 =
E->getArg(0)->getType()->getAs<VectorType>();
[[maybe_unused]] auto *VecTy1 =
E->getArg(1)->getType()->getAs<VectorType>();
// A HLSLVectorTruncation should have happend
assert(VecTy0->getNumElements() == VecTy1->getNumElements() &&
"Dot product requires vectors to be of the same size.");

return Builder.CreateIntrinsic(
/*ReturnType*/ T0->getScalarType(), Intrinsic::dx_dot,
ArrayRef<Value *>{Op0, Op1}, nullptr, "dx.dot");
} break;
}
return nullptr;
}

Value *CodeGenFunction::EmitAMDGPUBuiltinExpr(unsigned BuiltinID,
const CallExpr *E) {
llvm::AtomicOrdering AO = llvm::AtomicOrdering::SequentiallyConsistent;
Expand Down
22 changes: 11 additions & 11 deletions clang/lib/CodeGen/CGCUDANV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -760,10 +760,10 @@ llvm::Function *CGNVCUDARuntime::makeModuleCtorFunction() {
// to contain the fat binary but will be populated somewhere else,
// e.g. by lld through link script.
FatBinStr = new llvm::GlobalVariable(
CGM.getModule(), CGM.Int8Ty,
/*isConstant=*/true, llvm::GlobalValue::ExternalLinkage, nullptr,
"__hip_fatbin", nullptr,
llvm::GlobalVariable::NotThreadLocal);
CGM.getModule(), CGM.Int8Ty,
/*isConstant=*/true, llvm::GlobalValue::ExternalLinkage, nullptr,
"__hip_fatbin_" + CGM.getContext().getCUIDHash(), nullptr,
llvm::GlobalVariable::NotThreadLocal);
cast<llvm::GlobalVariable>(FatBinStr)->setSection(FatbinConstantName);
}

Expand Down Expand Up @@ -816,8 +816,8 @@ llvm::Function *CGNVCUDARuntime::makeModuleCtorFunction() {
// thread safety of the loaded program. Therefore we can assume sequential
// execution of constructor functions here.
if (IsHIP) {
auto Linkage = CudaGpuBinary ? llvm::GlobalValue::InternalLinkage :
llvm::GlobalValue::LinkOnceAnyLinkage;
auto Linkage = CudaGpuBinary ? llvm::GlobalValue::InternalLinkage
: llvm::GlobalValue::ExternalLinkage;
llvm::BasicBlock *IfBlock =
llvm::BasicBlock::Create(Context, "if", ModuleCtorFunc);
llvm::BasicBlock *ExitBlock =
Expand All @@ -826,11 +826,11 @@ llvm::Function *CGNVCUDARuntime::makeModuleCtorFunction() {
// of HIP ABI.
GpuBinaryHandle = new llvm::GlobalVariable(
TheModule, PtrTy, /*isConstant=*/false, Linkage,
/*Initializer=*/llvm::ConstantPointerNull::get(PtrTy),
"__hip_gpubin_handle");
if (Linkage == llvm::GlobalValue::LinkOnceAnyLinkage)
GpuBinaryHandle->setComdat(
CGM.getModule().getOrInsertComdat(GpuBinaryHandle->getName()));
/*Initializer=*/
CudaGpuBinary ? llvm::ConstantPointerNull::get(PtrTy) : nullptr,
CudaGpuBinary
? "__hip_gpubin_handle"
: "__hip_gpubin_handle_" + CGM.getContext().getCUIDHash());
GpuBinaryHandle->setAlignment(CGM.getPointerAlign().getAsAlign());
// Prevent the weak symbol in different shared libraries being merged.
if (Linkage != llvm::GlobalValue::InternalLinkage)
Expand Down
Loading