3,161 changes: 3,148 additions & 13 deletions clang/include/clang/Sema/Sema.h

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,26 @@ ANALYZER_OPTION(bool, ShouldCrosscheckWithZ3, "crosscheck-with-z3",
"constraint manager backend.",
false)

ANALYZER_OPTION(
unsigned, Z3CrosscheckEQClassTimeoutThreshold,
"crosscheck-with-z3-eqclass-timeout-threshold",
"Set a timeout for bug report equivalence classes in milliseconds. "
"If we exhaust this threshold, we will drop the bug report eqclass "
"instead of doing more Z3 queries. Set 0 for no timeout.", 700)

ANALYZER_OPTION(
unsigned, Z3CrosscheckTimeoutThreshold,
"crosscheck-with-z3-timeout-threshold",
"Set a timeout for individual Z3 queries in milliseconds. "
"Set 0 for no timeout.", 300)

ANALYZER_OPTION(
unsigned, Z3CrosscheckRLimitThreshold,
"crosscheck-with-z3-rlimit-threshold",
"Set the Z3 resource limit threshold. This sets a deterministic cutoff "
"point for Z3 queries, as longer queries usually consume more resources. "
"Set 0 for unlimited.", 400'000)

ANALYZER_OPTION(bool, ShouldReportIssuesInMainSourceFile,
"report-in-main-source-file",
"Whether or not the diagnostic report should be always "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -597,29 +597,6 @@ class SuppressInlineDefensiveChecksVisitor final : public BugReporterVisitor {
PathSensitiveBugReport &BR) override;
};

/// The bug visitor will walk all the nodes in a path and collect all the
/// constraints. When it reaches the root node, will create a refutation
/// manager and check if the constraints are satisfiable
class FalsePositiveRefutationBRVisitor final : public BugReporterVisitor {
private:
/// Holds the constraints in a given path
ConstraintMap Constraints;

public:
FalsePositiveRefutationBRVisitor();

void Profile(llvm::FoldingSetNodeID &ID) const override;

PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
BugReporterContext &BRC,
PathSensitiveBugReport &BR) override;

void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *EndPathNode,
PathSensitiveBugReport &BR) override;
void addConstraints(const ExplodedNode *N,
bool OverwriteConstraintsOnExistingSyms);
};

/// The visitor detects NoteTags and displays the event notes they contain.
class TagVisitor : public BugReporterVisitor {
public:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//===- Z3CrosscheckVisitor.h - Crosscheck reports with Z3 -------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines the visitor and utilities around it for Z3 report
// refutation.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_Z3CROSSCHECKVISITOR_H
#define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_Z3CROSSCHECKVISITOR_H

#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"

namespace clang::ento {

/// The bug visitor will walk all the nodes in a path and collect all the
/// constraints. When it reaches the root node, will create a refutation
/// manager and check if the constraints are satisfiable.
class Z3CrosscheckVisitor final : public BugReporterVisitor {
public:
struct Z3Result {
std::optional<bool> IsSAT = std::nullopt;
unsigned Z3QueryTimeMilliseconds = 0;
unsigned UsedRLimit = 0;
};
Z3CrosscheckVisitor(Z3CrosscheckVisitor::Z3Result &Result,
const AnalyzerOptions &Opts);

void Profile(llvm::FoldingSetNodeID &ID) const override;

PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
BugReporterContext &BRC,
PathSensitiveBugReport &BR) override;

void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *EndPathNode,
PathSensitiveBugReport &BR) override;

private:
void addConstraints(const ExplodedNode *N,
bool OverwriteConstraintsOnExistingSyms);

/// Holds the constraints in a given path.
ConstraintMap Constraints;
Z3Result &Result;
const AnalyzerOptions &Opts;
};

/// The oracle will decide if a report should be accepted or rejected based on
/// the results of the Z3 solver and the statistics of the queries of a report
/// equivalenece class.
class Z3CrosscheckOracle {
public:
explicit Z3CrosscheckOracle(const AnalyzerOptions &Opts) : Opts(Opts) {}

enum Z3Decision {
AcceptReport, // The report was SAT.
RejectReport, // The report was UNSAT or UNDEF.
RejectEQClass, // The heuristic suggests to skip the current eqclass.
};

/// Updates the internal state with the new Z3Result and makes a decision how
/// to proceed:
/// - Accept the report if the Z3Result was SAT.
/// - Suggest dropping the report equvalence class based on the accumulated
/// statistics.
/// - Otherwise, reject the report if the Z3Result was UNSAT or UNDEF.
///
/// Conditions for dropping the equivalence class:
/// - Accumulative time spent in Z3 checks is more than 700ms in the eqclass.
/// - Hit the 300ms query timeout in the report eqclass.
/// - Hit the 400'000 rlimit in the report eqclass.
///
/// All these thresholds are configurable via the analyzer options.
///
/// Refer to
/// https://discourse.llvm.org/t/analyzer-rfc-taming-z3-query-times/79520 to
/// see why this heuristic was chosen.
Z3Decision interpretQueryResult(const Z3CrosscheckVisitor::Z3Result &Meta);

private:
const AnalyzerOptions &Opts;
unsigned AccumulatedZ3QueryTimeInEqClass = 0; // ms
};

} // namespace clang::ento

#endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_Z3CROSSCHECKVISITOR_H
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ class SMTConstraintManager : public clang::ento::SimpleConstraintManager {
public:
SMTConstraintManager(clang::ento::ExprEngine *EE,
clang::ento::SValBuilder &SB)
: SimpleConstraintManager(EE, SB) {}
: SimpleConstraintManager(EE, SB) {
Solver->setBoolParam("model", true); // Enable model finding
Solver->setUnsignedParam("timeout", 15000 /*milliseconds*/);
}
virtual ~SMTConstraintManager() = default;

//===------------------------------------------------------------------===//
Expand Down
63 changes: 31 additions & 32 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1521,11 +1521,11 @@ void CXXRecordDecl::setCaptures(ASTContext &Context,
auto *ToCapture = (LambdaCapture *)Context.Allocate(sizeof(LambdaCapture) *
Captures.size());
Data.AddCaptureList(Context, ToCapture);
for (unsigned I = 0, N = Captures.size(); I != N; ++I) {
if (Captures[I].isExplicit())
for (const LambdaCapture &C : Captures) {
if (C.isExplicit())
++Data.NumExplicitCaptures;

new (ToCapture) LambdaCapture(Captures[I]);
new (ToCapture) LambdaCapture(C);
ToCapture++;
}

Expand Down Expand Up @@ -2056,40 +2056,39 @@ void CXXRecordDecl::completeDefinition() {
completeDefinition(nullptr);
}

static bool hasPureVirtualFinalOverrider(
const CXXRecordDecl &RD, const CXXFinalOverriderMap *FinalOverriders) {
if (!FinalOverriders) {
CXXFinalOverriderMap MyFinalOverriders;
RD.getFinalOverriders(MyFinalOverriders);
return hasPureVirtualFinalOverrider(RD, &MyFinalOverriders);
}

for (const CXXFinalOverriderMap::value_type &
OverridingMethodsEntry : *FinalOverriders) {
for (const auto &[_, SubobjOverrides] : OverridingMethodsEntry.second) {
assert(SubobjOverrides.size() > 0 &&
"All virtual functions have overriding virtual functions");

if (SubobjOverrides.front().Method->isPureVirtual())
return true;
}
}
return false;
}

void CXXRecordDecl::completeDefinition(CXXFinalOverriderMap *FinalOverriders) {
RecordDecl::completeDefinition();

// If the class may be abstract (but hasn't been marked as such), check for
// any pure final overriders.
if (mayBeAbstract()) {
CXXFinalOverriderMap MyFinalOverriders;
if (!FinalOverriders) {
getFinalOverriders(MyFinalOverriders);
FinalOverriders = &MyFinalOverriders;
}

bool Done = false;
for (CXXFinalOverriderMap::iterator M = FinalOverriders->begin(),
MEnd = FinalOverriders->end();
M != MEnd && !Done; ++M) {
for (OverridingMethods::iterator SO = M->second.begin(),
SOEnd = M->second.end();
SO != SOEnd && !Done; ++SO) {
assert(SO->second.size() > 0 &&
"All virtual functions have overriding virtual functions");

// C++ [class.abstract]p4:
// A class is abstract if it contains or inherits at least one
// pure virtual function for which the final overrider is pure
// virtual.
if (SO->second.front().Method->isPureVirtual()) {
data().Abstract = true;
Done = true;
break;
}
}
}
}
//
// C++ [class.abstract]p4:
// A class is abstract if it contains or inherits at least one
// pure virtual function for which the final overrider is pure
// virtual.
if (mayBeAbstract() && hasPureVirtualFinalOverrider(*this, FinalOverriders))
markAbstract();

// Set access bits correctly on the directly-declared conversions.
for (conversion_iterator I = conversion_begin(), E = conversion_end();
Expand Down
122 changes: 96 additions & 26 deletions clang/lib/AST/Interp/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,19 @@ template <class Emitter> class SwitchScope final : public LabelScope<Emitter> {
CaseMap OldCaseLabels;
};

template <class Emitter> class StmtExprScope final {
public:
StmtExprScope(Compiler<Emitter> *Ctx) : Ctx(Ctx), OldFlag(Ctx->InStmtExpr) {
Ctx->InStmtExpr = true;
}

~StmtExprScope() { Ctx->InStmtExpr = OldFlag; }

private:
Compiler<Emitter> *Ctx;
bool OldFlag;
};

} // namespace interp
} // namespace clang

Expand Down Expand Up @@ -1272,7 +1285,13 @@ bool Compiler<Emitter>::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
template <class Emitter>
bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
const Expr *ArrayFiller, const Expr *E) {
if (E->getType()->isVoidType())

QualType QT = E->getType();

if (const auto *AT = QT->getAs<AtomicType>())
QT = AT->getValueType();

if (QT->isVoidType())
return this->emitInvalid(E);

// Handle discarding first.
Expand All @@ -1285,17 +1304,16 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
}

// Primitive values.
if (std::optional<PrimType> T = classify(E->getType())) {
if (std::optional<PrimType> T = classify(QT)) {
assert(!DiscardResult);
if (Inits.size() == 0)
return this->visitZeroInitializer(*T, E->getType(), E);
return this->visitZeroInitializer(*T, QT, E);
assert(Inits.size() == 1);
return this->delegate(Inits[0]);
}

QualType T = E->getType();
if (T->isRecordType()) {
const Record *R = getRecord(E->getType());
if (QT->isRecordType()) {
const Record *R = getRecord(QT);

if (Inits.size() == 1 && E->getType() == Inits[0]->getType())
return this->delegate(Inits[0]);
Expand Down Expand Up @@ -1392,8 +1410,8 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
return this->emitFinishInit(E);
}

if (T->isArrayType()) {
if (Inits.size() == 1 && E->getType() == Inits[0]->getType())
if (QT->isArrayType()) {
if (Inits.size() == 1 && QT == Inits[0]->getType())
return this->delegate(Inits[0]);

unsigned ElementIndex = 0;
Expand Down Expand Up @@ -1425,7 +1443,7 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
// FIXME: This should go away.
if (ArrayFiller) {
const ConstantArrayType *CAT =
Ctx.getASTContext().getAsConstantArrayType(E->getType());
Ctx.getASTContext().getAsConstantArrayType(QT);
uint64_t NumElems = CAT->getZExtSize();

for (; ElementIndex != NumElems; ++ElementIndex) {
Expand All @@ -1437,7 +1455,7 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
return this->emitFinishInit(E);
}

if (const auto *ComplexTy = E->getType()->getAs<ComplexType>()) {
if (const auto *ComplexTy = QT->getAs<ComplexType>()) {
unsigned NumInits = Inits.size();

if (NumInits == 1)
Expand Down Expand Up @@ -1467,7 +1485,7 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
return true;
}

if (const auto *VecT = E->getType()->getAs<VectorType>()) {
if (const auto *VecT = QT->getAs<VectorType>()) {
unsigned NumVecElements = VecT->getNumElements();
assert(NumVecElements >= Inits.size());

Expand Down Expand Up @@ -3025,6 +3043,33 @@ bool Compiler<Emitter>::VisitCXXStdInitializerListExpr(
return this->emitInitFieldPtr(R->getField(1u)->Offset, E);
}

template <class Emitter>
bool Compiler<Emitter>::VisitStmtExpr(const StmtExpr *E) {
BlockScope<Emitter> BS(this);
StmtExprScope<Emitter> SS(this);

const CompoundStmt *CS = E->getSubStmt();
const Stmt *Result = CS->getStmtExprResult();
for (const Stmt *S : CS->body()) {
if (S != Result) {
if (!this->visitStmt(S))
return false;
continue;
}

assert(S == Result);
// This better produces a value (i.e. is an expression).
if (const Expr *ResultExpr = dyn_cast<Expr>(S)) {
if (DiscardResult)
return this->discard(ResultExpr);
return this->delegate(ResultExpr);
}
return false;
}

return true;
}

template <class Emitter> bool Compiler<Emitter>::discard(const Expr *E) {
OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/true,
/*NewInitializing=*/false);
Expand Down Expand Up @@ -3434,7 +3479,7 @@ bool Compiler<Emitter>::visitDecl(const VarDecl *VD, bool ConstantContext) {
}

// Create and initialize the variable.
if (!this->visitVarDecl(VD))
if (!this->visitVarDecl(VD, /*Toplevel=*/true))
return false;

// Get a pointer to the variable
Expand Down Expand Up @@ -3481,7 +3526,7 @@ bool Compiler<Emitter>::visitDecl(const VarDecl *VD, bool ConstantContext) {
}

template <class Emitter>
VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD) {
VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD, bool Toplevel) {
// We don't know what to do with these, so just return false.
if (VD->getType().isNull())
return false;
Expand All @@ -3494,37 +3539,45 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD) {
const Expr *Init = VD->getInit();
std::optional<PrimType> VarT = classify(VD->getType());

auto checkDecl = [&]() -> bool {
bool NeedsOp = !Toplevel && VD->isLocalVarDecl() && VD->isStaticLocal();
return !NeedsOp || this->emitCheckDecl(VD, VD);
};

if (Context::shouldBeGloballyIndexed(VD)) {
auto initGlobal = [&](unsigned GlobalIndex) -> bool {
assert(Init);
DeclScope<Emitter> LocalScope(this, VD);

if (VarT) {
if (!this->visit(Init))
return false;
return this->emitInitGlobal(*VarT, GlobalIndex, VD);
return checkDecl() && false;

return checkDecl() && this->emitInitGlobal(*VarT, GlobalIndex, VD);
}
return this->visitGlobalInitializer(Init, GlobalIndex);

return checkDecl() && this->visitGlobalInitializer(Init, GlobalIndex);
};

// We've already seen and initialized this global.
if (std::optional<unsigned> GlobalIndex = P.getGlobal(VD)) {
if (P.getPtrGlobal(*GlobalIndex).isInitialized())
return true;
return checkDecl();

// The previous attempt at initialization might've been unsuccessful,
// so let's try this one.
return Init && initGlobal(*GlobalIndex);
return Init && checkDecl() && initGlobal(*GlobalIndex);
}

std::optional<unsigned> GlobalIndex = P.createGlobal(VD, Init);

if (!GlobalIndex)
return false;

return !Init || initGlobal(*GlobalIndex);
return !Init || (checkDecl() && initGlobal(*GlobalIndex));
} else {
VariableScope<Emitter> LocalScope(this, VD);

if (VarT) {
unsigned Offset = this->allocateLocalPrimitive(
VD, *VarT, VD->getType().isConstQualified());
Expand Down Expand Up @@ -3985,7 +4038,7 @@ template <class Emitter> bool Compiler<Emitter>::visitLoopBody(const Stmt *S) {
return true;

if (const auto *CS = dyn_cast<CompoundStmt>(S)) {
for (auto *InnerStmt : CS->body())
for (const auto *InnerStmt : CS->body())
if (!visitStmt(InnerStmt))
return false;
return true;
Expand All @@ -3997,15 +4050,15 @@ template <class Emitter> bool Compiler<Emitter>::visitLoopBody(const Stmt *S) {
template <class Emitter>
bool Compiler<Emitter>::visitCompoundStmt(const CompoundStmt *S) {
BlockScope<Emitter> Scope(this);
for (auto *InnerStmt : S->body())
for (const auto *InnerStmt : S->body())
if (!visitStmt(InnerStmt))
return false;
return true;
}

template <class Emitter>
bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS) {
for (auto *D : DS->decls()) {
for (const auto *D : DS->decls()) {
if (isa<StaticAssertDecl, TagDecl, TypedefNameDecl, UsingEnumDecl,
FunctionDecl>(D))
continue;
Expand All @@ -4022,6 +4075,9 @@ bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS) {

template <class Emitter>
bool Compiler<Emitter>::visitReturnStmt(const ReturnStmt *RS) {
if (this->InStmtExpr)
return this->emitUnsupported(RS);

if (const Expr *RE = RS->getRetValue()) {
ExprScope<Emitter> RetScope(this);
if (ReturnType) {
Expand Down Expand Up @@ -4105,6 +4161,7 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt *S) {
LabelTy EndLabel = this->getLabel(); // Label after the loop.
LoopScope<Emitter> LS(this, EndLabel, CondLabel);

this->fallthrough(CondLabel);
this->emitLabel(CondLabel);

if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt())
Expand Down Expand Up @@ -4140,19 +4197,22 @@ template <class Emitter> bool Compiler<Emitter>::visitDoStmt(const DoStmt *S) {
LoopScope<Emitter> LS(this, EndLabel, CondLabel);
LocalScope<Emitter> Scope(this);

this->fallthrough(StartLabel);
this->emitLabel(StartLabel);
{
DestructorScope<Emitter> DS(Scope);

if (!this->visitLoopBody(Body))
return false;
this->fallthrough(CondLabel);
this->emitLabel(CondLabel);
if (!this->visitBool(Cond))
return false;
}
if (!this->jumpTrue(StartLabel))
return false;

this->fallthrough(EndLabel);
this->emitLabel(EndLabel);
return true;
}
Expand All @@ -4173,6 +4233,7 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {

if (Init && !this->visitStmt(Init))
return false;
this->fallthrough(CondLabel);
this->emitLabel(CondLabel);

if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt())
Expand All @@ -4191,13 +4252,15 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {

if (Body && !this->visitLoopBody(Body))
return false;
this->fallthrough(IncLabel);
this->emitLabel(IncLabel);
if (Inc && !this->discard(Inc))
return false;
}

if (!this->jump(CondLabel))
return false;
this->fallthrough(EndLabel);
this->emitLabel(EndLabel);
return true;
}
Expand Down Expand Up @@ -4229,6 +4292,7 @@ bool Compiler<Emitter>::visitCXXForRangeStmt(const CXXForRangeStmt *S) {
return false;

// Now the condition as well as the loop variable assignment.
this->fallthrough(CondLabel);
this->emitLabel(CondLabel);
if (!this->visitBool(Cond))
return false;
Expand All @@ -4245,13 +4309,16 @@ bool Compiler<Emitter>::visitCXXForRangeStmt(const CXXForRangeStmt *S) {

if (!this->visitLoopBody(Body))
return false;
this->fallthrough(IncLabel);
this->emitLabel(IncLabel);
if (!this->discard(Inc))
return false;
}

if (!this->jump(CondLabel))
return false;

this->fallthrough(EndLabel);
this->emitLabel(EndLabel);
return true;
}
Expand Down Expand Up @@ -4906,8 +4973,11 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
return this->emitGetLocal(PT_Ptr, Offset, E);
return this->emitGetPtrLocal(Offset, E);
} else if (auto GlobalIndex = P.getGlobal(D)) {
if (IsReference)
return this->emitGetGlobal(classifyPrim(E), *GlobalIndex, E);
if (IsReference) {
if (!Ctx.getLangOpts().CPlusPlus11)
return this->emitGetGlobal(classifyPrim(E), *GlobalIndex, E);
return this->emitGetGlobalUnchecked(classifyPrim(E), *GlobalIndex, E);
}

return this->emitGetPtrGlobal(*GlobalIndex, E);
} else if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
Expand Down Expand Up @@ -4954,7 +5024,7 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
if ((VD->hasGlobalStorage() || VD->isLocalVarDecl() ||
VD->isStaticDataMember()) &&
typeShouldBeVisited(VD->getType())) {
auto VarState = this->visitVarDecl(VD);
auto VarState = this->visitVarDecl(VD, true);
if (VarState.notCreated())
return true;
if (!VarState)
Expand All @@ -4967,7 +5037,7 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
if (const auto *VD = dyn_cast<VarDecl>(D);
VD && VD->getAnyInitializer() &&
VD->getType().isConstant(Ctx.getASTContext()) && !VD->isWeak()) {
auto VarState = this->visitVarDecl(VD);
auto VarState = this->visitVarDecl(VD, true);
if (VarState.notCreated())
return true;
if (!VarState)
Expand Down
7 changes: 6 additions & 1 deletion clang/lib/AST/Interp/Compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ template <class Emitter> class SourceLocScope;
template <class Emitter> class LoopScope;
template <class Emitter> class LabelScope;
template <class Emitter> class SwitchScope;
template <class Emitter> class StmtExprScope;

template <class Emitter> class Compiler;
struct InitLink {
Expand Down Expand Up @@ -183,6 +184,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
bool VisitExtVectorElementExpr(const ExtVectorElementExpr *E);
bool VisitObjCBoxedExpr(const ObjCBoxedExpr *E);
bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E);
bool VisitStmtExpr(const StmtExpr *E);

// Statements.
bool visitCompoundStmt(const CompoundStmt *S);
Expand Down Expand Up @@ -259,7 +261,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
/// intact.
bool delegate(const Expr *E);
/// Creates and initializes a variable from the given decl.
VarCreationState visitVarDecl(const VarDecl *VD);
VarCreationState visitVarDecl(const VarDecl *VD, bool Toplevel = false);
/// Visit an APValue.
bool visitAPValue(const APValue &Val, PrimType ValType, const Expr *E);
bool visitAPValueInitializer(const APValue &Val, const Expr *E);
Expand Down Expand Up @@ -333,6 +335,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
friend class LoopScope<Emitter>;
friend class LabelScope<Emitter>;
friend class SwitchScope<Emitter>;
friend class StmtExprScope<Emitter>;

/// Emits a zero initializer.
bool visitZeroInitializer(PrimType T, QualType QT, const Expr *E);
Expand Down Expand Up @@ -397,6 +400,8 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
/// Flag indicating if return value is to be discarded.
bool DiscardResult = false;

bool InStmtExpr = false;

/// Flag inidicating if we're initializing an already created
/// variable. This is set in visitInitializer().
bool Initializing = false;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
}

bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
++EvalID;
bool Recursing = !Stk.empty();
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);

Expand All @@ -65,6 +66,7 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
}

bool Context::evaluate(State &Parent, const Expr *E, APValue &Result) {
++EvalID;
bool Recursing = !Stk.empty();
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);

Expand All @@ -90,6 +92,7 @@ bool Context::evaluate(State &Parent, const Expr *E, APValue &Result) {

bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
APValue &Result) {
++EvalID;
bool Recursing = !Stk.empty();
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);

Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/Interp/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ class Context final {

const Record *getRecord(const RecordDecl *D) const;

unsigned getEvalID() const { return EvalID; }

private:
/// Runs a function.
bool Run(State &Parent, const Function *Func, APValue &Result);
Expand All @@ -119,6 +121,8 @@ class Context final {
InterpStack Stk;
/// Constexpr program.
std::unique_ptr<Program> P;
/// ID identifying an evaluation.
unsigned EvalID = 0;
};

} // namespace interp
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -355,11 +355,11 @@ Descriptor::Descriptor(const DeclTy &D)
}

QualType Descriptor::getType() const {
if (auto *E = asExpr())
if (const auto *E = asExpr())
return E->getType();
if (auto *D = asValueDecl())
if (const auto *D = asValueDecl())
return D->getType();
if (auto *T = dyn_cast<TypeDecl>(asDecl()))
if (const auto *T = dyn_cast<TypeDecl>(asDecl()))
return QualType(T->getTypeForDecl(), 0);
llvm_unreachable("Invalid descriptor type");
}
Expand Down
27 changes: 20 additions & 7 deletions clang/lib/AST/Interp/Disasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,28 @@ LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const {
}
Desc->dump(OS);

if (Desc->IsTemporary) {
if (GP.isInitialized() && Desc->IsTemporary) {
if (const auto *MTE =
dyn_cast_if_present<MaterializeTemporaryExpr>(Desc->asExpr());
MTE && MTE->getLifetimeExtendedTemporaryDecl()) {
const APValue *V = MTE->getLifetimeExtendedTemporaryDecl()->getValue();
if (V->isInt())
OS << " (global temporary value: " << V->getInt() << ")";
else
OS << " (huh?)";
if (const APValue *V =
MTE->getLifetimeExtendedTemporaryDecl()->getValue()) {
OS << " (global temporary value: ";
{
ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_MAGENTA, true});
std::string VStr;
llvm::raw_string_ostream SS(VStr);
V->dump(SS, Ctx.getASTContext());

for (unsigned I = 0; I != VStr.size(); ++I) {
if (VStr[I] == '\n')
VStr[I] = ' ';
}
VStr.pop_back(); // Remove the newline (or now space) at the end.
OS << VStr;
}
OS << ')';
}
}
}

Expand Down Expand Up @@ -205,7 +218,7 @@ LLVM_DUMP_METHOD void Descriptor::dump(llvm::raw_ostream &OS) const {
if (const auto *ND = dyn_cast_if_present<NamedDecl>(asDecl()))
ND->printQualifiedName(OS);
else if (asExpr())
OS << "expr (TODO)";
OS << "Expr " << (const void *)asExpr();
}

// Print a few interesting bits about the descriptor.
Expand Down
30 changes: 29 additions & 1 deletion clang/lib/AST/Interp/EvalEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD,
EvalResult.setInvalid();

S.EvaluatingDecl = nullptr;
updateGlobalTemporaries();
return std::move(this->EvalResult);
}

Expand All @@ -83,7 +84,7 @@ EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; }
Scope::Local EvalEmitter::createLocal(Descriptor *D) {
// Allocate memory for a local.
auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize());
auto *B = new (Memory.get()) Block(D, /*isStatic=*/false);
auto *B = new (Memory.get()) Block(Ctx.getEvalID(), D, /*isStatic=*/false);
B->invokeCtor();

// Initialize local variable inline descriptor.
Expand Down Expand Up @@ -150,6 +151,11 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {

// Implicitly convert lvalue to rvalue, if requested.
if (ConvertResultToRValue) {
// Never allow reading from a non-const pointer, unless the memory
// has been created in this evaluation.
if (!Ptr.isConst() && Ptr.block()->getEvalID() != Ctx.getEvalID())
return false;

if (std::optional<APValue> V = Ptr.toRValue(Ctx)) {
EvalResult.setValue(*V);
} else {
Expand Down Expand Up @@ -237,6 +243,28 @@ bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) {
return true;
}

/// Global temporaries (LifetimeExtendedTemporary) carry their value
/// around as an APValue, which codegen accesses.
/// We set their value once when creating them, but we don't update it
/// afterwards when code changes it later.
/// This is what we do here.
void EvalEmitter::updateGlobalTemporaries() {
for (const auto &[E, Temp] : S.SeenGlobalTemporaries) {
if (std::optional<unsigned> GlobalIndex = P.getGlobal(E)) {
const Pointer &Ptr = P.getPtrGlobal(*GlobalIndex);
APValue *Cached = Temp->getOrCreateValue(true);

if (std::optional<PrimType> T = Ctx.classify(E->getType())) {
TYPE_SWITCH(*T, { *Cached = Ptr.deref<T>().toAPValue(); });
} else {
if (std::optional<APValue> APV = Ptr.toRValue(Ctx))
*Cached = *APV;
}
}
}
S.SeenGlobalTemporaries.clear();
}

//===----------------------------------------------------------------------===//
// Opcode evaluators
//===----------------------------------------------------------------------===//
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/Interp/EvalEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ class EvalEmitter : public SourceMapper {
return reinterpret_cast<Block *>(It->second.get());
}

void updateGlobalTemporaries();

// The emitter always tracks the current instruction and sets OpPC to a token
// value which is mapped to the location of the opcode being evaluated.
CodePtr OpPC;
Expand Down
26 changes: 16 additions & 10 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,

bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
assert(Ptr.isLive() && "Pointer is not live");
if (!Ptr.isConst())
if (!Ptr.isConst() || Ptr.isMutable())
return true;

// The This pointer is writable in constructors and destructors,
Expand All @@ -422,9 +422,14 @@ bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {

bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
assert(Ptr.isLive() && "Pointer is not live");
if (!Ptr.isMutable()) {
if (!Ptr.isMutable())
return true;

// In C++14 onwards, it is permitted to read a mutable member whose
// lifetime began within the evaluation.
if (S.getLangOpts().CPlusPlus14 &&
Ptr.block()->getEvalID() == S.Ctx.getEvalID())
return true;
}

const SourceInfo &Loc = S.Current->getSource(OpPC);
const FieldDecl *Field = Ptr.getField();
Expand Down Expand Up @@ -476,23 +481,24 @@ bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return false;
}

bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (!CheckLive(S, OpPC, Ptr, AK_Read))
bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
AccessKinds AK) {
if (!CheckLive(S, OpPC, Ptr, AK))
return false;
if (!CheckConstant(S, OpPC, Ptr))
return false;

if (!CheckDummy(S, OpPC, Ptr, AK_Read))
if (!CheckDummy(S, OpPC, Ptr, AK))
return false;
if (!CheckExtern(S, OpPC, Ptr))
return false;
if (!CheckRange(S, OpPC, Ptr, AK_Read))
if (!CheckRange(S, OpPC, Ptr, AK))
return false;
if (!CheckActive(S, OpPC, Ptr, AK_Read))
if (!CheckActive(S, OpPC, Ptr, AK))
return false;
if (!CheckInitialized(S, OpPC, Ptr, AK_Read))
if (!CheckInitialized(S, OpPC, Ptr, AK))
return false;
if (!CheckTemporary(S, OpPC, Ptr, AK_Read))
if (!CheckTemporary(S, OpPC, Ptr, AK))
return false;
if (!CheckMutable(S, OpPC, Ptr))
return false;
Expand Down
99 changes: 66 additions & 33 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc);
bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr);

/// Checks if a value can be loaded from a block.
bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
AccessKinds AK = AK_Read);

bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
AccessKinds AK);
Expand Down Expand Up @@ -724,9 +725,7 @@ bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool Inc(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckDummy(S, OpPC, Ptr, AK_Increment))
return false;
if (!CheckInitialized(S, OpPC, Ptr, AK_Increment))
if (!CheckLoad(S, OpPC, Ptr, AK_Increment))
return false;

return IncDecHelper<T, IncDecOp::Inc, PushVal::Yes>(S, OpPC, Ptr);
Expand All @@ -738,9 +737,7 @@ bool Inc(InterpState &S, CodePtr OpPC) {
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool IncPop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckDummy(S, OpPC, Ptr, AK_Increment))
return false;
if (!CheckInitialized(S, OpPC, Ptr, AK_Increment))
if (!CheckLoad(S, OpPC, Ptr, AK_Increment))
return false;

return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr);
Expand All @@ -753,9 +750,7 @@ bool IncPop(InterpState &S, CodePtr OpPC) {
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool Dec(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckDummy(S, OpPC, Ptr, AK_Decrement))
return false;
if (!CheckInitialized(S, OpPC, Ptr, AK_Decrement))
if (!CheckLoad(S, OpPC, Ptr, AK_Decrement))
return false;

return IncDecHelper<T, IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr);
Expand All @@ -767,9 +762,7 @@ bool Dec(InterpState &S, CodePtr OpPC) {
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool DecPop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckDummy(S, OpPC, Ptr, AK_Decrement))
return false;
if (!CheckInitialized(S, OpPC, Ptr, AK_Decrement))
if (!CheckLoad(S, OpPC, Ptr, AK_Decrement))
return false;

return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr);
Expand Down Expand Up @@ -797,42 +790,31 @@ bool IncDecFloatHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr,

inline bool Incf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (Ptr.isDummy())
return false;
if (!CheckInitialized(S, OpPC, Ptr, AK_Increment))
if (!CheckLoad(S, OpPC, Ptr, AK_Increment))
return false;

return IncDecFloatHelper<IncDecOp::Inc, PushVal::Yes>(S, OpPC, Ptr, RM);
}

inline bool IncfPop(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (Ptr.isDummy())
return false;
if (!CheckInitialized(S, OpPC, Ptr, AK_Increment))
if (!CheckLoad(S, OpPC, Ptr, AK_Increment))
return false;

return IncDecFloatHelper<IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr, RM);
}

inline bool Decf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (Ptr.isDummy())
return false;

if (!CheckInitialized(S, OpPC, Ptr, AK_Decrement))
if (!CheckLoad(S, OpPC, Ptr, AK_Decrement))
return false;

return IncDecFloatHelper<IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr, RM);
}

inline bool DecfPop(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (Ptr.isDummy())
return false;
if (!CheckInitialized(S, OpPC, Ptr, AK_Decrement))
if (!CheckLoad(S, OpPC, Ptr, AK_Decrement))
return false;

return IncDecFloatHelper<IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr, RM);
Expand Down Expand Up @@ -940,6 +922,7 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
return true;
}

// Reject comparisons to weak pointers.
for (const auto &P : {LHS, RHS}) {
if (P.isZero())
continue;
Expand All @@ -952,6 +935,20 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
}

if (!Pointer::hasSameBase(LHS, RHS)) {
if (LHS.isOnePastEnd() && !RHS.isOnePastEnd() && !RHS.isZero() &&
RHS.getOffset() == 0) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_past_end)
<< LHS.toDiagnosticString(S.getCtx());
return false;
} else if (RHS.isOnePastEnd() && !LHS.isOnePastEnd() && !LHS.isZero() &&
LHS.getOffset() == 0) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_past_end)
<< RHS.toDiagnosticString(S.getCtx());
return false;
}

S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Unordered)));
return true;
} else {
Expand Down Expand Up @@ -1255,7 +1252,10 @@ bool GetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
/// Same as GetGlobal, but without the checks.
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool GetGlobalUnchecked(InterpState &S, CodePtr OpPC, uint32_t I) {
auto *B = S.P.getGlobal(I);
const Pointer &Ptr = S.P.getPtrGlobal(I);
if (!Ptr.isInitialized())
return false;
const Block *B = S.P.getGlobal(I);
S.Stk.push<T>(B->deref<T>());
return true;
}
Expand All @@ -1280,16 +1280,20 @@ bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitGlobalTemp(InterpState &S, CodePtr OpPC, uint32_t I,
const LifetimeExtendedTemporaryDecl *Temp) {
assert(Temp);
const Pointer &Ptr = S.P.getGlobal(I);

const T Value = S.Stk.peek<T>();
APValue APV = Value.toAPValue();
APValue *Cached = Temp->getOrCreateValue(true);
*Cached = APV;

const Pointer &P = S.P.getGlobal(I);
P.deref<T>() = S.Stk.pop<T>();
P.initialize();
assert(Ptr.getDeclDesc()->asExpr());

S.SeenGlobalTemporaries.push_back(
std::make_pair(Ptr.getDeclDesc()->asExpr(), Temp));

Ptr.deref<T>() = S.Stk.pop<T>();
Ptr.initialize();
return true;
}

Expand All @@ -1302,6 +1306,9 @@ inline bool InitGlobalTempComp(InterpState &S, CodePtr OpPC,
const Pointer &P = S.Stk.peek<Pointer>();
APValue *Cached = Temp->getOrCreateValue(true);

S.SeenGlobalTemporaries.push_back(
std::make_pair(P.getDeclDesc()->asExpr(), Temp));

if (std::optional<APValue> APV = P.toRValue(S.getCtx())) {
*Cached = *APV;
return true;
Expand Down Expand Up @@ -2626,6 +2633,13 @@ inline bool Invalid(InterpState &S, CodePtr OpPC) {
return false;
}

inline bool Unsupported(InterpState &S, CodePtr OpPC) {
const SourceLocation &Loc = S.Current->getLocation(OpPC);
S.FFDiag(Loc, diag::note_constexpr_stmt_expr_unsupported)
<< S.Current->getRange(OpPC);
return false;
}

/// Do nothing and just abort execution.
inline bool Error(InterpState &S, CodePtr OpPC) { return false; }

Expand Down Expand Up @@ -2697,6 +2711,25 @@ inline bool DecayPtr(InterpState &S, CodePtr OpPC) {
return true;
}

inline bool CheckDecl(InterpState &S, CodePtr OpPC, const VarDecl *VD) {
// An expression E is a core constant expression unless the evaluation of E
// would evaluate one of the following: [C++23] - a control flow that passes
// through a declaration of a variable with static or thread storage duration
// unless that variable is usable in constant expressions.
assert(VD->isLocalVarDecl() &&
VD->isStaticLocal()); // Checked before emitting this.

if (VD == S.EvaluatingDecl)
return true;

if (!VD->isUsableInConstantExpressions(S.getCtx())) {
S.CCEDiag(VD->getLocation(), diag::note_constexpr_static_local)
<< (VD->getTSCSpec() == TSCS_unspecified ? 0 : 1) << VD;
return false;
}
return true;
}

//===----------------------------------------------------------------------===//
// Read opcode arguments
//===----------------------------------------------------------------------===//
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/AST/Interp/InterpBlock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ bool Block::hasPointer(const Pointer *P) const {
#endif

DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk)
: Root(Root), B(Blk->Desc, Blk->IsStatic, Blk->IsExtern, /*isDead=*/true) {
: Root(Root),
B(~0u, Blk->Desc, Blk->IsStatic, Blk->IsExtern, /*isDead=*/true) {
// Add the block to the chain of dead blocks.
if (Root)
Root->Prev = this;
Expand Down
33 changes: 21 additions & 12 deletions clang/lib/AST/Interp/InterpBlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,19 @@ enum PrimType : unsigned;
class Block final {
public:
/// Creates a new block.
Block(const std::optional<unsigned> &DeclID, const Descriptor *Desc,
bool IsStatic = false, bool IsExtern = false)
: DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), Desc(Desc) {
assert(Desc);
}

Block(const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false)
: DeclID((unsigned)-1), IsStatic(IsStatic), IsExtern(IsExtern),
Block(unsigned EvalID, const std::optional<unsigned> &DeclID,
const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false)
: EvalID(EvalID), DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern),
Desc(Desc) {
assert(Desc);
}
assert(Desc);
}

Block(unsigned EvalID, const Descriptor *Desc, bool IsStatic = false,
bool IsExtern = false)
: EvalID(EvalID), DeclID((unsigned)-1), IsStatic(IsStatic),
IsExtern(IsExtern), Desc(Desc) {
assert(Desc);
}

/// Returns the block's descriptor.
const Descriptor *getDescriptor() const { return Desc; }
Expand All @@ -75,7 +77,11 @@ class Block final {
unsigned getSize() const { return Desc->getAllocSize(); }
/// Returns the declaration ID.
std::optional<unsigned> getDeclID() const { return DeclID; }
/// Returns whether the data of this block has been initialized via
/// invoking the Ctor func.
bool isInitialized() const { return IsInitialized; }
/// The Evaluation ID this block was created in.
unsigned getEvalID() const { return EvalID; }

/// Returns a pointer to the stored data.
/// You are allowed to read Desc->getSize() bytes from this address.
Expand Down Expand Up @@ -130,8 +136,10 @@ class Block final {
friend class DeadBlock;
friend class InterpState;

Block(const Descriptor *Desc, bool IsExtern, bool IsStatic, bool IsDead)
: IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true), Desc(Desc) {
Block(unsigned EvalID, const Descriptor *Desc, bool IsExtern, bool IsStatic,
bool IsDead)
: EvalID(EvalID), IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true),
Desc(Desc) {
assert(Desc);
}

Expand All @@ -146,6 +154,7 @@ class Block final {
bool hasPointer(const Pointer *P) const;
#endif

const unsigned EvalID = ~0u;
/// Start of the chain of pointers.
Pointer *Pointers = nullptr;
/// Unique identifier of the declaration.
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/AST/Interp/InterpFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ InterpFrame::InterpFrame(InterpState &S, const Function *Func,
Locals = std::make_unique<char[]>(FrameSize);
for (auto &Scope : Func->scopes()) {
for (auto &Local : Scope.locals()) {
Block *B = new (localBlock(Local.Offset)) Block(Local.Desc);
Block *B =
new (localBlock(Local.Offset)) Block(S.Ctx.getEvalID(), Local.Desc);
B->invokeCtor();
new (localInlineDesc(Local.Offset)) InlineDescriptor(Local.Desc);
}
Expand Down Expand Up @@ -220,7 +221,7 @@ Pointer InterpFrame::getParamPointer(unsigned Off) {
const auto &Desc = Func->getParamDescriptor(Off);
size_t BlockSize = sizeof(Block) + Desc.second->getAllocSize();
auto Memory = std::make_unique<char[]>(BlockSize);
auto *B = new (Memory.get()) Block(Desc.second);
auto *B = new (Memory.get()) Block(S.Ctx.getEvalID(), Desc.second);

// Copy the initial value.
TYPE_SWITCH(Desc.first, new (B->data()) T(stackRef<T>(Off)));
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/Interp/InterpState.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ class InterpState final : public State, public SourceMapper {
SourceLocation EvalLocation;
/// Declaration we're initializing/evaluting, if any.
const VarDecl *EvaluatingDecl = nullptr;

llvm::SmallVector<
std::pair<const Expr *, const LifetimeExtendedTemporaryDecl *>>
SeenGlobalTemporaries;
};

} // namespace interp
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def ArgDeclRef : ArgType { let Name = "const DeclRefExpr *"; }
def ArgDesc : ArgType { let Name = "const Descriptor *"; }
def ArgCCI : ArgType { let Name = "const ComparisonCategoryInfo *"; }
def ArgDecl : ArgType { let Name = "const Decl*"; }
def ArgVarDecl : ArgType { let Name = "const VarDecl*"; }

//===----------------------------------------------------------------------===//
// Classes of types instructions operate on.
Expand Down Expand Up @@ -382,6 +383,10 @@ def GetLocal : AccessOpcode { let HasCustomEval = 1; }
// [] -> [Pointer]
def SetLocal : AccessOpcode { let HasCustomEval = 1; }

def CheckDecl : Opcode {
let Args = [ArgVarDecl];
}

// [] -> [Value]
def GetGlobal : AccessOpcode;
def GetGlobalUnchecked : AccessOpcode;
Expand Down Expand Up @@ -709,6 +714,7 @@ def Dup : Opcode {

// [] -> []
def Invalid : Opcode {}
def Unsupported : Opcode {}
def Error : Opcode {}
def InvalidCast : Opcode {
let Args = [ArgCastKind];
Expand Down
7 changes: 4 additions & 3 deletions clang/lib/AST/Interp/Pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ APValue Pointer::toAPValue() const {

if (isDummy() || isUnknownSizeArray() || Desc->asExpr())
return APValue(Base, CharUnits::Zero(), Path,
/*IsOnePastEnd=*/false, /*IsNullPtr=*/false);
/*IsOnePastEnd=*/isOnePastEnd(), /*IsNullPtr=*/false);

// TODO: compute the offset into the object.
CharUnits Offset = CharUnits::Zero();
Expand Down Expand Up @@ -181,7 +181,8 @@ APValue Pointer::toAPValue() const {
// Just invert the order of the elements.
std::reverse(Path.begin(), Path.end());

return APValue(Base, Offset, Path, /*IsOnePastEnd=*/false, /*IsNullPtr=*/false);
return APValue(Base, Offset, Path, /*IsOnePastEnd=*/isOnePastEnd(),
/*IsNullPtr=*/false);
}

void Pointer::print(llvm::raw_ostream &OS) const {
Expand Down Expand Up @@ -348,7 +349,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx) const {

// Invalid pointers.
if (Ptr.isDummy() || !Ptr.isLive() || !Ptr.isBlockPointer() ||
(!Ptr.isUnknownSizeArray() && Ptr.isOnePastEnd()))
Ptr.isPastEnd())
return false;

// Primitive values.
Expand Down
10 changes: 9 additions & 1 deletion clang/lib/AST/Interp/Pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -556,10 +556,18 @@ class Pointer {
if (isUnknownSizeArray())
return false;

return isElementPastEnd() ||
return isElementPastEnd() || isPastEnd() ||
(getSize() == getOffset() && !isZeroSizeArray());
}

/// Checks if the pointer points past the end of the object.
bool isPastEnd() const {
if (isIntegralPointer())
return false;

return !isZero() && Offset > PointeeStorage.BS.Pointee->getSize();
}

/// Checks if the pointer is an out-of-bounds element pointer.
bool isElementPastEnd() const { return Offset == PastEndMark; }

Expand Down
22 changes: 18 additions & 4 deletions clang/lib/AST/Interp/Program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ unsigned Program::createGlobalString(const StringLiteral *S) {
// The byte length does not include the null terminator.
unsigned I = Globals.size();
unsigned Sz = Desc->getAllocSize();
auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true,
auto *G = new (Allocator, Sz) Global(Ctx.getEvalID(), Desc, /*isStatic=*/true,
/*isExtern=*/false);
G->block()->invokeCtor();

Expand Down Expand Up @@ -126,6 +126,12 @@ std::optional<unsigned> Program::getGlobal(const ValueDecl *VD) {
return std::nullopt;
}

std::optional<unsigned> Program::getGlobal(const Expr *E) {
if (auto It = GlobalIndices.find(E); It != GlobalIndices.end())
return It->second;
return std::nullopt;
}

std::optional<unsigned> Program::getOrCreateGlobal(const ValueDecl *VD,
const Expr *Init) {
if (auto Idx = getGlobal(VD))
Expand Down Expand Up @@ -164,7 +170,8 @@ std::optional<unsigned> Program::getOrCreateDummy(const ValueDecl *VD) {
unsigned I = Globals.size();

auto *G = new (Allocator, Desc->getAllocSize())
Global(getCurrentDecl(), Desc, /*IsStatic=*/true, /*IsExtern=*/false);
Global(Ctx.getEvalID(), getCurrentDecl(), Desc, /*IsStatic=*/true,
/*IsExtern=*/false);
G->block()->invokeCtor();

Globals.push_back(G);
Expand Down Expand Up @@ -195,7 +202,14 @@ std::optional<unsigned> Program::createGlobal(const ValueDecl *VD,
}

std::optional<unsigned> Program::createGlobal(const Expr *E) {
return createGlobal(E, E->getType(), /*isStatic=*/true, /*isExtern=*/false);
if (auto Idx = getGlobal(E))
return Idx;
if (auto Idx = createGlobal(E, E->getType(), /*isStatic=*/true,
/*isExtern=*/false)) {
GlobalIndices[E] = *Idx;
return *Idx;
}
return std::nullopt;
}

std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
Expand All @@ -218,7 +232,7 @@ std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
unsigned I = Globals.size();

auto *G = new (Allocator, Desc->getAllocSize())
Global(getCurrentDecl(), Desc, IsStatic, IsExtern);
Global(Ctx.getEvalID(), getCurrentDecl(), Desc, IsStatic, IsExtern);
G->block()->invokeCtor();

// Initialize InlineDescriptor fields.
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/Program.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class Program final {

/// Finds a global's index.
std::optional<unsigned> getGlobal(const ValueDecl *VD);
std::optional<unsigned> getGlobal(const Expr *E);

/// Returns or creates a global an creates an index to it.
std::optional<unsigned> getOrCreateGlobal(const ValueDecl *VD,
Expand Down
82 changes: 36 additions & 46 deletions clang/lib/Analysis/ThreadSafety.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/ImmutableMap.h"
Expand Down Expand Up @@ -1035,10 +1034,9 @@ class ThreadSafetyAnalyzer {

template <class AttrType>
void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, const Expr *Exp,
const NamedDecl *D, const CFGBlock *PredBlock,
const CFGBlock *CurrBlock,
const ASTContext &TrylockAttrContext,
Expr *TrylockAttrSuccessValue, bool Neg);
const NamedDecl *D,
const CFGBlock *PredBlock, const CFGBlock *CurrBlock,
Expr *BrE, bool Neg);

const CallExpr* getTrylockCallExpr(const Stmt *Cond, LocalVarContext C,
bool &Negate);
Expand Down Expand Up @@ -1361,18 +1359,17 @@ void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
const Expr *Exp, const NamedDecl *D,
const CFGBlock *PredBlock,
const CFGBlock *CurrBlock,
const ASTContext &TrylockAttrContext,
Expr *TrylockAttrSuccess,
bool Neg) {
// Evaluate the trylock's success value as a boolean.
bool trylockSuccessValue = false;
if (!TrylockAttrSuccess->EvaluateAsBooleanCondition(
trylockSuccessValue, TrylockAttrContext,
/*InConstantContext=*/true)) {
llvm_unreachable("Trylock success value could not be evaluated.");
}

const int branchnum = Neg ? !!trylockSuccessValue : !trylockSuccessValue;
Expr *BrE, bool Neg) {
// Find out which branch has the lock
bool branch = false;
if (const auto *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE))
branch = BLE->getValue();
else if (const auto *ILE = dyn_cast_or_null<IntegerLiteral>(BrE))
branch = ILE->getValue().getBoolValue();

int branchnum = branch ? 0 : 1;
if (Neg)
branchnum = !branchnum;

// If we've taken the trylock branch, then add the lock
int i = 0;
Expand All @@ -1393,15 +1390,8 @@ static bool getStaticBooleanValue(Expr *E, bool &TCond) {
} else if (const auto *ILE = dyn_cast<IntegerLiteral>(E)) {
TCond = ILE->getValue().getBoolValue();
return true;
} else if (auto *CE = dyn_cast<ImplicitCastExpr>(E)) {
} else if (auto *CE = dyn_cast<ImplicitCastExpr>(E))
return getStaticBooleanValue(CE->getSubExpr(), TCond);
} else if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
if (auto *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl())) {
llvm::APSInt IV = ECD->getInitVal();
TCond = IV.getBoolValue();
return true;
}
}
return false;
}

Expand Down Expand Up @@ -1507,27 +1497,27 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
// If the condition is a call to a Trylock function, then grab the attributes
for (const auto *Attr : FunDecl->attrs()) {
switch (Attr->getKind()) {
case attr::TryAcquireCapability: {
auto *A = cast<TryAcquireCapabilityAttr>(Attr);
getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
Exp, FunDecl, PredBlock, CurrBlock, FunDecl->getASTContext(),
A->getSuccessValue(), Negate);
break;
};
case attr::ExclusiveTrylockFunction: {
const auto *A = cast<ExclusiveTrylockFunctionAttr>(Attr);
getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,
FunDecl->getASTContext(), A->getSuccessValue(), Negate);
break;
}
case attr::SharedTrylockFunction: {
const auto *A = cast<SharedTrylockFunctionAttr>(Attr);
getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,
FunDecl->getASTContext(), A->getSuccessValue(), Negate);
break;
}
default:
break;
case attr::TryAcquireCapability: {
auto *A = cast<TryAcquireCapabilityAttr>(Attr);
getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
Exp, FunDecl, PredBlock, CurrBlock, A->getSuccessValue(),
Negate);
break;
};
case attr::ExclusiveTrylockFunction: {
const auto *A = cast<ExclusiveTrylockFunctionAttr>(Attr);
getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,
A->getSuccessValue(), Negate);
break;
}
case attr::SharedTrylockFunction: {
const auto *A = cast<SharedTrylockFunctionAttr>(Attr);
getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,
A->getSuccessValue(), Negate);
break;
}
default:
break;
}
}

Expand Down
110 changes: 54 additions & 56 deletions clang/lib/Basic/Cuda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,23 +72,21 @@ CudaVersion ToCudaVersion(llvm::VersionTuple Version) {
}

namespace {
struct CudaArchToStringMap {
CudaArch arch;
struct OffloadArchToStringMap {
OffloadArch arch;
const char *arch_name;
const char *virtual_arch_name;
};
} // namespace

#define SM2(sm, ca) \
{ CudaArch::SM_##sm, "sm_" #sm, ca }
#define SM2(sm, ca) {OffloadArch::SM_##sm, "sm_" #sm, ca}
#define SM(sm) SM2(sm, "compute_" #sm)
#define GFX(gpu) \
{ CudaArch::GFX##gpu, "gfx" #gpu, "compute_amdgcn" }
static const CudaArchToStringMap arch_names[] = {
#define GFX(gpu) {OffloadArch::GFX##gpu, "gfx" #gpu, "compute_amdgcn"}
static const OffloadArchToStringMap arch_names[] = {
// clang-format off
{CudaArch::UNUSED, "", ""},
{OffloadArch::UNUSED, "", ""},
SM2(20, "compute_20"), SM2(21, "compute_20"), // Fermi
SM(30), {CudaArch::SM_32_, "sm_32", "compute_32"}, SM(35), SM(37), // Kepler
SM(30), {OffloadArch::SM_32_, "sm_32", "compute_32"}, SM(35), SM(37), // Kepler
SM(50), SM(52), SM(53), // Maxwell
SM(60), SM(61), SM(62), // Pascal
SM(70), SM(72), // Volta
Expand All @@ -112,7 +110,7 @@ static const CudaArchToStringMap arch_names[] = {
GFX(803), // gfx803
GFX(805), // gfx805
GFX(810), // gfx810
{CudaArch::GFX9_GENERIC, "gfx9-generic", "compute_amdgcn"},
{OffloadArch::GFX9_GENERIC, "gfx9-generic", "compute_amdgcn"},
GFX(900), // gfx900
GFX(902), // gfx902
GFX(904), // gfx903
Expand All @@ -124,126 +122,126 @@ static const CudaArchToStringMap arch_names[] = {
GFX(940), // gfx940
GFX(941), // gfx941
GFX(942), // gfx942
{CudaArch::GFX10_1_GENERIC, "gfx10-1-generic", "compute_amdgcn"},
{OffloadArch::GFX10_1_GENERIC, "gfx10-1-generic", "compute_amdgcn"},
GFX(1010), // gfx1010
GFX(1011), // gfx1011
GFX(1012), // gfx1012
GFX(1013), // gfx1013
{CudaArch::GFX10_3_GENERIC, "gfx10-3-generic", "compute_amdgcn"},
{OffloadArch::GFX10_3_GENERIC, "gfx10-3-generic", "compute_amdgcn"},
GFX(1030), // gfx1030
GFX(1031), // gfx1031
GFX(1032), // gfx1032
GFX(1033), // gfx1033
GFX(1034), // gfx1034
GFX(1035), // gfx1035
GFX(1036), // gfx1036
{CudaArch::GFX11_GENERIC, "gfx11-generic", "compute_amdgcn"},
{OffloadArch::GFX11_GENERIC, "gfx11-generic", "compute_amdgcn"},
GFX(1100), // gfx1100
GFX(1101), // gfx1101
GFX(1102), // gfx1102
GFX(1103), // gfx1103
GFX(1150), // gfx1150
GFX(1151), // gfx1151
GFX(1152), // gfx1152
{CudaArch::GFX12_GENERIC, "gfx12-generic", "compute_amdgcn"},
{OffloadArch::GFX12_GENERIC, "gfx12-generic", "compute_amdgcn"},
GFX(1200), // gfx1200
GFX(1201), // gfx1201
{CudaArch::AMDGCNSPIRV, "amdgcnspirv", "compute_amdgcn"},
{CudaArch::Generic, "generic", ""},
{OffloadArch::AMDGCNSPIRV, "amdgcnspirv", "compute_amdgcn"},
{OffloadArch::Generic, "generic", ""},
// clang-format on
};
#undef SM
#undef SM2
#undef GFX

const char *CudaArchToString(CudaArch A) {
const char *OffloadArchToString(OffloadArch A) {
auto result = std::find_if(
std::begin(arch_names), std::end(arch_names),
[A](const CudaArchToStringMap &map) { return A == map.arch; });
[A](const OffloadArchToStringMap &map) { return A == map.arch; });
if (result == std::end(arch_names))
return "unknown";
return result->arch_name;
}

const char *CudaArchToVirtualArchString(CudaArch A) {
const char *OffloadArchToVirtualArchString(OffloadArch A) {
auto result = std::find_if(
std::begin(arch_names), std::end(arch_names),
[A](const CudaArchToStringMap &map) { return A == map.arch; });
[A](const OffloadArchToStringMap &map) { return A == map.arch; });
if (result == std::end(arch_names))
return "unknown";
return result->virtual_arch_name;
}

CudaArch StringToCudaArch(llvm::StringRef S) {
OffloadArch StringToOffloadArch(llvm::StringRef S) {
auto result = std::find_if(
std::begin(arch_names), std::end(arch_names),
[S](const CudaArchToStringMap &map) { return S == map.arch_name; });
[S](const OffloadArchToStringMap &map) { return S == map.arch_name; });
if (result == std::end(arch_names))
return CudaArch::UNKNOWN;
return OffloadArch::UNKNOWN;
return result->arch;
}

CudaVersion MinVersionForCudaArch(CudaArch A) {
if (A == CudaArch::UNKNOWN)
CudaVersion MinVersionForOffloadArch(OffloadArch A) {
if (A == OffloadArch::UNKNOWN)
return CudaVersion::UNKNOWN;

// AMD GPUs do not depend on CUDA versions.
if (IsAMDGpuArch(A))
if (IsAMDOffloadArch(A))
return CudaVersion::CUDA_70;

switch (A) {
case CudaArch::SM_20:
case CudaArch::SM_21:
case CudaArch::SM_30:
case CudaArch::SM_32_:
case CudaArch::SM_35:
case CudaArch::SM_37:
case CudaArch::SM_50:
case CudaArch::SM_52:
case CudaArch::SM_53:
case OffloadArch::SM_20:
case OffloadArch::SM_21:
case OffloadArch::SM_30:
case OffloadArch::SM_32_:
case OffloadArch::SM_35:
case OffloadArch::SM_37:
case OffloadArch::SM_50:
case OffloadArch::SM_52:
case OffloadArch::SM_53:
return CudaVersion::CUDA_70;
case CudaArch::SM_60:
case CudaArch::SM_61:
case CudaArch::SM_62:
case OffloadArch::SM_60:
case OffloadArch::SM_61:
case OffloadArch::SM_62:
return CudaVersion::CUDA_80;
case CudaArch::SM_70:
case OffloadArch::SM_70:
return CudaVersion::CUDA_90;
case CudaArch::SM_72:
case OffloadArch::SM_72:
return CudaVersion::CUDA_91;
case CudaArch::SM_75:
case OffloadArch::SM_75:
return CudaVersion::CUDA_100;
case CudaArch::SM_80:
case OffloadArch::SM_80:
return CudaVersion::CUDA_110;
case CudaArch::SM_86:
case OffloadArch::SM_86:
return CudaVersion::CUDA_111;
case CudaArch::SM_87:
case OffloadArch::SM_87:
return CudaVersion::CUDA_114;
case CudaArch::SM_89:
case CudaArch::SM_90:
case OffloadArch::SM_89:
case OffloadArch::SM_90:
return CudaVersion::CUDA_118;
case CudaArch::SM_90a:
case OffloadArch::SM_90a:
return CudaVersion::CUDA_120;
default:
llvm_unreachable("invalid enum");
}
}

CudaVersion MaxVersionForCudaArch(CudaArch A) {
CudaVersion MaxVersionForOffloadArch(OffloadArch A) {
// AMD GPUs do not depend on CUDA versions.
if (IsAMDGpuArch(A))
if (IsAMDOffloadArch(A))
return CudaVersion::NEW;

switch (A) {
case CudaArch::UNKNOWN:
case OffloadArch::UNKNOWN:
return CudaVersion::UNKNOWN;
case CudaArch::SM_20:
case CudaArch::SM_21:
case OffloadArch::SM_20:
case OffloadArch::SM_21:
return CudaVersion::CUDA_80;
case CudaArch::SM_30:
case CudaArch::SM_32_:
case OffloadArch::SM_30:
case OffloadArch::SM_32_:
return CudaVersion::CUDA_102;
case CudaArch::SM_35:
case CudaArch::SM_37:
case OffloadArch::SM_35:
case OffloadArch::SM_37:
return CudaVersion::CUDA_118;
default:
return CudaVersion::NEW;
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Basic/LangOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ void LangOptions::setLangDefaults(LangOptions &Opts, Language Lang,
Opts.C11 = Std.isC11();
Opts.C17 = Std.isC17();
Opts.C23 = Std.isC23();
Opts.C2y = Std.isC2y();
Opts.CPlusPlus = Std.isCPlusPlus();
Opts.CPlusPlus11 = Std.isCPlusPlus11();
Opts.CPlusPlus14 = Std.isCPlusPlus14();
Expand Down
247 changes: 119 additions & 128 deletions clang/lib/Basic/OpenMPKinds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -702,139 +702,130 @@ bool clang::needsTaskBasedThreadLimit(OpenMPDirectiveKind DKind) {
DKind == OMPD_target_parallel_loop;
}

void clang::getOpenMPCaptureRegions(
SmallVectorImpl<OpenMPDirectiveKind> &CaptureRegions,
OpenMPDirectiveKind DKind) {
assert(unsigned(DKind) < llvm::omp::Directive_enumSize);
bool clang::isOpenMPExecutableDirective(OpenMPDirectiveKind DKind) {
if (DKind == OMPD_error)
return true;
Category Cat = getDirectiveCategory(DKind);
return Cat == Category::Executable || Cat == Category::Subsidiary;
}

bool clang::isOpenMPCapturingDirective(OpenMPDirectiveKind DKind) {
if (isOpenMPExecutableDirective(DKind)) {
switch (DKind) {
case OMPD_atomic:
case OMPD_barrier:
case OMPD_cancel:
case OMPD_cancellation_point:
case OMPD_critical:
case OMPD_depobj:
case OMPD_error:
case OMPD_flush:
case OMPD_masked:
case OMPD_master:
case OMPD_section:
case OMPD_taskwait:
case OMPD_taskyield:
return false;
default:
return !isOpenMPLoopTransformationDirective(DKind);
}
}
// Non-executable directives.
switch (DKind) {
case OMPD_metadirective:
CaptureRegions.push_back(OMPD_metadirective);
break;
case OMPD_parallel:
case OMPD_parallel_for:
case OMPD_parallel_for_simd:
case OMPD_parallel_master:
case OMPD_parallel_masked:
case OMPD_parallel_sections:
case OMPD_distribute_parallel_for:
case OMPD_distribute_parallel_for_simd:
case OMPD_parallel_loop:
CaptureRegions.push_back(OMPD_parallel);
break;
case OMPD_target_teams:
case OMPD_target_teams_distribute:
case OMPD_target_teams_distribute_simd:
CaptureRegions.push_back(OMPD_task);
CaptureRegions.push_back(OMPD_target);
CaptureRegions.push_back(OMPD_teams);
break;
case OMPD_teams:
case OMPD_teams_distribute:
case OMPD_teams_distribute_simd:
CaptureRegions.push_back(OMPD_teams);
break;
case OMPD_target:
case OMPD_target_simd:
CaptureRegions.push_back(OMPD_task);
CaptureRegions.push_back(OMPD_target);
break;
case OMPD_teams_loop:
case OMPD_teams_distribute_parallel_for:
case OMPD_teams_distribute_parallel_for_simd:
CaptureRegions.push_back(OMPD_teams);
CaptureRegions.push_back(OMPD_parallel);
break;
case OMPD_target_parallel:
case OMPD_target_parallel_for:
case OMPD_target_parallel_for_simd:
case OMPD_target_parallel_loop:
CaptureRegions.push_back(OMPD_task);
CaptureRegions.push_back(OMPD_target);
CaptureRegions.push_back(OMPD_parallel);
break;
case OMPD_task:
case OMPD_target_enter_data:
case OMPD_target_exit_data:
case OMPD_target_update:
CaptureRegions.push_back(OMPD_task);
break;
case OMPD_taskloop:
case OMPD_taskloop_simd:
case OMPD_master_taskloop:
case OMPD_master_taskloop_simd:
case OMPD_masked_taskloop:
case OMPD_masked_taskloop_simd:
CaptureRegions.push_back(OMPD_taskloop);
break;
case OMPD_parallel_masked_taskloop:
case OMPD_parallel_masked_taskloop_simd:
case OMPD_parallel_master_taskloop:
case OMPD_parallel_master_taskloop_simd:
CaptureRegions.push_back(OMPD_parallel);
CaptureRegions.push_back(OMPD_taskloop);
break;
case OMPD_target_teams_loop:
case OMPD_target_teams_distribute_parallel_for:
case OMPD_target_teams_distribute_parallel_for_simd:
CaptureRegions.push_back(OMPD_task);
CaptureRegions.push_back(OMPD_target);
CaptureRegions.push_back(OMPD_teams);
CaptureRegions.push_back(OMPD_parallel);
break;
case OMPD_nothing:
CaptureRegions.push_back(OMPD_nothing);
break;
case OMPD_loop:
// TODO: 'loop' may require different capture regions depending on the bind
// clause or the parent directive when there is no bind clause. Use
// OMPD_unknown for now.
case OMPD_simd:
case OMPD_for:
case OMPD_for_simd:
case OMPD_sections:
case OMPD_section:
case OMPD_single:
case OMPD_master:
case OMPD_critical:
case OMPD_taskgroup:
case OMPD_distribute:
case OMPD_ordered:
case OMPD_atomic:
case OMPD_target_data:
case OMPD_distribute_simd:
case OMPD_scope:
case OMPD_dispatch:
CaptureRegions.push_back(OMPD_unknown);
break;
case OMPD_tile:
case OMPD_unroll:
// loop transformations do not introduce captures.
break;
case OMPD_threadprivate:
case OMPD_allocate:
case OMPD_taskyield:
case OMPD_barrier:
case OMPD_error:
case OMPD_taskwait:
case OMPD_cancellation_point:
case OMPD_cancel:
case OMPD_flush:
case OMPD_depobj:
case OMPD_scan:
case OMPD_declare_reduction:
case OMPD_declare_mapper:
case OMPD_declare_simd:
case OMPD_declare_target:
case OMPD_end_declare_target:
case OMPD_requires:
case OMPD_declare_variant:
case OMPD_begin_declare_variant:
case OMPD_end_declare_variant:
llvm_unreachable("OpenMP Directive is not allowed");
case OMPD_unknown:
return true;
default:
llvm_unreachable("Unknown OpenMP directive");
break;
}
return false;
}

void clang::getOpenMPCaptureRegions(
SmallVectorImpl<OpenMPDirectiveKind> &CaptureRegions,
OpenMPDirectiveKind DKind) {
assert(unsigned(DKind) < llvm::omp::Directive_enumSize);
assert(isOpenMPCapturingDirective(DKind) && "Expecting capturing directive");

auto GetRegionsForLeaf = [&](OpenMPDirectiveKind LKind) {
assert(isLeafConstruct(LKind) && "Epecting leaf directive");
// Whether a leaf would require OMPD_unknown if it occured on its own.
switch (LKind) {
case OMPD_metadirective:
CaptureRegions.push_back(OMPD_metadirective);
break;
case OMPD_nothing:
CaptureRegions.push_back(OMPD_nothing);
break;
case OMPD_parallel:
CaptureRegions.push_back(OMPD_parallel);
break;
case OMPD_target:
CaptureRegions.push_back(OMPD_task);
CaptureRegions.push_back(OMPD_target);
break;
case OMPD_task:
case OMPD_target_enter_data:
case OMPD_target_exit_data:
case OMPD_target_update:
CaptureRegions.push_back(OMPD_task);
break;
case OMPD_teams:
CaptureRegions.push_back(OMPD_teams);
break;
case OMPD_taskloop:
CaptureRegions.push_back(OMPD_taskloop);
break;
case OMPD_loop:
// TODO: 'loop' may require different capture regions depending on the
// bind clause or the parent directive when there is no bind clause.
// If any of the directives that push regions here are parents of 'loop',
// assume 'parallel'. Otherwise do nothing.
if (!CaptureRegions.empty() &&
!llvm::is_contained(CaptureRegions, OMPD_parallel))
CaptureRegions.push_back(OMPD_parallel);
else
return true;
break;
case OMPD_dispatch:
case OMPD_distribute:
case OMPD_for:
case OMPD_masked:
case OMPD_master:
case OMPD_ordered:
case OMPD_scope:
case OMPD_sections:
case OMPD_simd:
case OMPD_single:
case OMPD_target_data:
case OMPD_taskgroup:
// These directives (when standalone) use OMPD_unknown as the region,
// but when they're constituents of a compound directive, and other
// leafs from that directive have specific regions, then these directives
// add no additional regions.
return true;
default:
llvm::errs() << getOpenMPDirectiveName(LKind) << '\n';
llvm_unreachable("Unexpected directive");
}
return false;
};

bool MayNeedUnknownRegion = false;
for (OpenMPDirectiveKind L : getLeafConstructsOrSelf(DKind))
MayNeedUnknownRegion |= GetRegionsForLeaf(L);

// We need OMPD_unknown when no regions were added, and specific leaf
// constructs were present. Push a single OMPD_unknown as the capture
/// region.
if (CaptureRegions.empty() && MayNeedUnknownRegion)
CaptureRegions.push_back(OMPD_unknown);

// OMPD_unknown is only expected as the only region. If other regions
// are present OMPD_unknown should not be present.
assert((CaptureRegions[0] == OMPD_unknown ||
!llvm::is_contained(CaptureRegions, OMPD_unknown)) &&
"Misplaced OMPD_unknown");
}

bool clang::checkFailClauseParameter(OpenMPClauseKind FailClauseParameter) {
Expand Down
160 changes: 80 additions & 80 deletions clang/lib/Basic/Targets/NVPTX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ NVPTXTargetInfo::NVPTXTargetInfo(const llvm::Triple &Triple,
// Define available target features
// These must be defined in sorted order!
NoAsmVariants = true;
GPU = CudaArch::UNUSED;
GPU = OffloadArch::UNUSED;

// PTX supports f16 as a fundamental type.
HasLegalHalfType = true;
Expand Down Expand Up @@ -175,117 +175,117 @@ void NVPTXTargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__NVPTX__");

// Skip setting architecture dependent macros if undefined.
if (GPU == CudaArch::UNUSED && !HostTarget)
if (GPU == OffloadArch::UNUSED && !HostTarget)
return;

if (Opts.CUDAIsDevice || Opts.OpenMPIsTargetDevice || !HostTarget) {
// Set __CUDA_ARCH__ for the GPU specified.
std::string CUDAArchCode = [this] {
switch (GPU) {
case CudaArch::GFX600:
case CudaArch::GFX601:
case CudaArch::GFX602:
case CudaArch::GFX700:
case CudaArch::GFX701:
case CudaArch::GFX702:
case CudaArch::GFX703:
case CudaArch::GFX704:
case CudaArch::GFX705:
case CudaArch::GFX801:
case CudaArch::GFX802:
case CudaArch::GFX803:
case CudaArch::GFX805:
case CudaArch::GFX810:
case CudaArch::GFX9_GENERIC:
case CudaArch::GFX900:
case CudaArch::GFX902:
case CudaArch::GFX904:
case CudaArch::GFX906:
case CudaArch::GFX908:
case CudaArch::GFX909:
case CudaArch::GFX90a:
case CudaArch::GFX90c:
case CudaArch::GFX940:
case CudaArch::GFX941:
case CudaArch::GFX942:
case CudaArch::GFX10_1_GENERIC:
case CudaArch::GFX1010:
case CudaArch::GFX1011:
case CudaArch::GFX1012:
case CudaArch::GFX1013:
case CudaArch::GFX10_3_GENERIC:
case CudaArch::GFX1030:
case CudaArch::GFX1031:
case CudaArch::GFX1032:
case CudaArch::GFX1033:
case CudaArch::GFX1034:
case CudaArch::GFX1035:
case CudaArch::GFX1036:
case CudaArch::GFX11_GENERIC:
case CudaArch::GFX1100:
case CudaArch::GFX1101:
case CudaArch::GFX1102:
case CudaArch::GFX1103:
case CudaArch::GFX1150:
case CudaArch::GFX1151:
case CudaArch::GFX1152:
case CudaArch::GFX12_GENERIC:
case CudaArch::GFX1200:
case CudaArch::GFX1201:
case CudaArch::AMDGCNSPIRV:
case CudaArch::Generic:
case CudaArch::LAST:
case OffloadArch::GFX600:
case OffloadArch::GFX601:
case OffloadArch::GFX602:
case OffloadArch::GFX700:
case OffloadArch::GFX701:
case OffloadArch::GFX702:
case OffloadArch::GFX703:
case OffloadArch::GFX704:
case OffloadArch::GFX705:
case OffloadArch::GFX801:
case OffloadArch::GFX802:
case OffloadArch::GFX803:
case OffloadArch::GFX805:
case OffloadArch::GFX810:
case OffloadArch::GFX9_GENERIC:
case OffloadArch::GFX900:
case OffloadArch::GFX902:
case OffloadArch::GFX904:
case OffloadArch::GFX906:
case OffloadArch::GFX908:
case OffloadArch::GFX909:
case OffloadArch::GFX90a:
case OffloadArch::GFX90c:
case OffloadArch::GFX940:
case OffloadArch::GFX941:
case OffloadArch::GFX942:
case OffloadArch::GFX10_1_GENERIC:
case OffloadArch::GFX1010:
case OffloadArch::GFX1011:
case OffloadArch::GFX1012:
case OffloadArch::GFX1013:
case OffloadArch::GFX10_3_GENERIC:
case OffloadArch::GFX1030:
case OffloadArch::GFX1031:
case OffloadArch::GFX1032:
case OffloadArch::GFX1033:
case OffloadArch::GFX1034:
case OffloadArch::GFX1035:
case OffloadArch::GFX1036:
case OffloadArch::GFX11_GENERIC:
case OffloadArch::GFX1100:
case OffloadArch::GFX1101:
case OffloadArch::GFX1102:
case OffloadArch::GFX1103:
case OffloadArch::GFX1150:
case OffloadArch::GFX1151:
case OffloadArch::GFX1152:
case OffloadArch::GFX12_GENERIC:
case OffloadArch::GFX1200:
case OffloadArch::GFX1201:
case OffloadArch::AMDGCNSPIRV:
case OffloadArch::Generic:
case OffloadArch::LAST:
break;
case CudaArch::UNKNOWN:
case OffloadArch::UNKNOWN:
assert(false && "No GPU arch when compiling CUDA device code.");
return "";
case CudaArch::UNUSED:
case CudaArch::SM_20:
case OffloadArch::UNUSED:
case OffloadArch::SM_20:
return "200";
case CudaArch::SM_21:
case OffloadArch::SM_21:
return "210";
case CudaArch::SM_30:
case OffloadArch::SM_30:
return "300";
case CudaArch::SM_32_:
case OffloadArch::SM_32_:
return "320";
case CudaArch::SM_35:
case OffloadArch::SM_35:
return "350";
case CudaArch::SM_37:
case OffloadArch::SM_37:
return "370";
case CudaArch::SM_50:
case OffloadArch::SM_50:
return "500";
case CudaArch::SM_52:
case OffloadArch::SM_52:
return "520";
case CudaArch::SM_53:
case OffloadArch::SM_53:
return "530";
case CudaArch::SM_60:
case OffloadArch::SM_60:
return "600";
case CudaArch::SM_61:
case OffloadArch::SM_61:
return "610";
case CudaArch::SM_62:
case OffloadArch::SM_62:
return "620";
case CudaArch::SM_70:
case OffloadArch::SM_70:
return "700";
case CudaArch::SM_72:
case OffloadArch::SM_72:
return "720";
case CudaArch::SM_75:
case OffloadArch::SM_75:
return "750";
case CudaArch::SM_80:
case OffloadArch::SM_80:
return "800";
case CudaArch::SM_86:
case OffloadArch::SM_86:
return "860";
case CudaArch::SM_87:
case OffloadArch::SM_87:
return "870";
case CudaArch::SM_89:
case OffloadArch::SM_89:
return "890";
case CudaArch::SM_90:
case CudaArch::SM_90a:
case OffloadArch::SM_90:
case OffloadArch::SM_90a:
return "900";
}
llvm_unreachable("unhandled CudaArch");
llvm_unreachable("unhandled OffloadArch");
}();
Builder.defineMacro("__CUDA_ARCH__", CUDAArchCode);
if (GPU == CudaArch::SM_90a)
if (GPU == OffloadArch::SM_90a)
Builder.defineMacro("__CUDA_ARCH_FEAT_SM90_ALL", "1");
}
}
Expand Down
21 changes: 11 additions & 10 deletions clang/lib/Basic/Targets/NVPTX.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ static const int NVPTXDWARFAddrSpaceMap[] = {

class LLVM_LIBRARY_VISIBILITY NVPTXTargetInfo : public TargetInfo {
static const char *const GCCRegNames[];
CudaArch GPU;
OffloadArch GPU;
uint32_t PTXVersion;
std::unique_ptr<TargetInfo> HostTarget;

Expand All @@ -79,8 +79,8 @@ class LLVM_LIBRARY_VISIBILITY NVPTXTargetInfo : public TargetInfo {
initFeatureMap(llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags,
StringRef CPU,
const std::vector<std::string> &FeaturesVec) const override {
if (GPU != CudaArch::UNUSED)
Features[CudaArchToString(GPU)] = true;
if (GPU != OffloadArch::UNUSED)
Features[OffloadArchToString(GPU)] = true;
Features["ptx" + std::to_string(PTXVersion)] = true;
return TargetInfo::initFeatureMap(Features, Diags, CPU, FeaturesVec);
}
Expand All @@ -105,6 +105,7 @@ class LLVM_LIBRARY_VISIBILITY NVPTXTargetInfo : public TargetInfo {
case 'l':
case 'f':
case 'd':
case 'q':
Info.setAllowsRegister();
return true;
}
Expand All @@ -121,18 +122,18 @@ class LLVM_LIBRARY_VISIBILITY NVPTXTargetInfo : public TargetInfo {
}

bool isValidCPUName(StringRef Name) const override {
return StringToCudaArch(Name) != CudaArch::UNKNOWN;
return StringToOffloadArch(Name) != OffloadArch::UNKNOWN;
}

void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override {
for (int i = static_cast<int>(CudaArch::SM_20);
i < static_cast<int>(CudaArch::Generic); ++i)
Values.emplace_back(CudaArchToString(static_cast<CudaArch>(i)));
for (int i = static_cast<int>(OffloadArch::SM_20);
i < static_cast<int>(OffloadArch::Generic); ++i)
Values.emplace_back(OffloadArchToString(static_cast<OffloadArch>(i)));
}

bool setCPU(const std::string &Name) override {
GPU = StringToCudaArch(Name);
return GPU != CudaArch::UNKNOWN;
GPU = StringToOffloadArch(Name);
return GPU != OffloadArch::UNKNOWN;
}

void setSupportedOpenCLOpts() override {
Expand Down Expand Up @@ -183,7 +184,7 @@ class LLVM_LIBRARY_VISIBILITY NVPTXTargetInfo : public TargetInfo {
bool hasBitIntType() const override { return true; }
bool hasBFloat16Type() const override { return true; }

CudaArch getGPU() const { return GPU; }
OffloadArch getGPU() const { return GPU; }
};
} // namespace targets
} // namespace clang
Expand Down
23 changes: 12 additions & 11 deletions clang/lib/Basic/Targets/PPC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -928,17 +928,18 @@ 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_METHOD, INDEX, OP, VALUE) .Case(NAME, true)
return llvm::StringSwitch<bool>(CPUName)
#include "llvm/TargetParser/PPCTargetParser.def"
.Default(false);
}

assert(Triple.isOSLinux() &&
assert((Triple.isOSAIX() || 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)

#define PPC_CPU(NAME, Linux_SUPPORT_METHOD, LinuxID, AIX_SUPPORT_METHOD, \
AIXID) \
.Case(NAME, {Linux_SUPPORT_METHOD, AIX_SUPPORT_METHOD})

std::pair<unsigned, unsigned> SuppportMethod =
llvm::StringSwitch<std::pair<unsigned, unsigned>>(CPUName)
#include "llvm/TargetParser/PPCTargetParser.def"
.Default(false);
.Default({BUILTIN_PPC_UNSUPPORTED, BUILTIN_PPC_UNSUPPORTED});
return Triple.isOSLinux()
? (SuppportMethod.first != BUILTIN_PPC_UNSUPPORTED)
: (SuppportMethod.second != BUILTIN_PPC_UNSUPPORTED);
}
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/BackendConsumer.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class BackendConsumer : public ASTConsumer {
void HandleVTable(CXXRecordDecl *RD) override;

// Links each entry in LinkModules into our module. Returns true on error.
bool LinkInModules(llvm::Module *M, bool ShouldLinkFiles = true);
bool LinkInModules(llvm::Module *M);

/// Get the best possible source location to represent a diagnostic that
/// may have associated debug info.
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/CodeGen/BackendUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
#include "llvm/Transforms/Instrumentation/LowerAllowCheckPass.h"
#include "llvm/Transforms/Instrumentation/MemProfiler.h"
#include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
#include "llvm/Transforms/Instrumentation/NumericalStabilitySanitizer.h"
#include "llvm/Transforms/Instrumentation/PGOInstrumentation.h"
#include "llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h"
#include "llvm/Transforms/Instrumentation/SanitizerCoverage.h"
Expand Down Expand Up @@ -707,6 +708,9 @@ static void addSanitizers(const Triple &TargetTriple,
MPM.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass()));
}

if (LangOpts.Sanitize.has(SanitizerKind::NumericalStability))
MPM.addPass(NumericalStabilitySanitizerPass());

auto ASanPass = [&](SanitizerMask Mask, bool CompileKernel) {
if (LangOpts.Sanitize.has(Mask)) {
bool UseGlobalGC = asanUseGlobalsGC(TargetTriple, CodeGenOpts);
Expand Down Expand Up @@ -1035,7 +1039,7 @@ void EmitAssemblyHelper::RunOptimizationPipeline(

// Link against bitcodes supplied via the -mlink-builtin-bitcode option
if (CodeGenOpts.LinkBitcodePostopt)
MPM.addPass(LinkInModulesPass(BC, false));
MPM.addPass(LinkInModulesPass(BC));

// Add a verifier pass if requested. We don't have to do this if the action
// requires code generation because there will already be a verifier pass in
Expand Down
14 changes: 7 additions & 7 deletions clang/lib/CodeGen/CGBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class CGBuilderTy : public CGBuilderBaseTy {
template <bool IsInBounds>
Address createConstGEP2_32(Address Addr, unsigned Idx0, unsigned Idx1,
const llvm::Twine &Name) {
const llvm::DataLayout &DL = BB->getParent()->getParent()->getDataLayout();
const llvm::DataLayout &DL = BB->getDataLayout();
llvm::GetElementPtrInst *GEP;
if (IsInBounds)
GEP = cast<llvm::GetElementPtrInst>(CreateConstInBoundsGEP2_32(
Expand Down Expand Up @@ -218,7 +218,7 @@ class CGBuilderTy : public CGBuilderBaseTy {
Address CreateStructGEP(Address Addr, unsigned Index,
const llvm::Twine &Name = "") {
llvm::StructType *ElTy = cast<llvm::StructType>(Addr.getElementType());
const llvm::DataLayout &DL = BB->getParent()->getParent()->getDataLayout();
const llvm::DataLayout &DL = BB->getDataLayout();
const llvm::StructLayout *Layout = DL.getStructLayout(ElTy);
auto Offset = CharUnits::fromQuantity(Layout->getElementOffset(Index));

Expand All @@ -240,7 +240,7 @@ class CGBuilderTy : public CGBuilderBaseTy {
Address CreateConstArrayGEP(Address Addr, uint64_t Index,
const llvm::Twine &Name = "") {
llvm::ArrayType *ElTy = cast<llvm::ArrayType>(Addr.getElementType());
const llvm::DataLayout &DL = BB->getParent()->getParent()->getDataLayout();
const llvm::DataLayout &DL = BB->getDataLayout();
CharUnits EltSize =
CharUnits::fromQuantity(DL.getTypeAllocSize(ElTy->getElementType()));

Expand All @@ -260,7 +260,7 @@ class CGBuilderTy : public CGBuilderBaseTy {
Address CreateConstInBoundsGEP(Address Addr, uint64_t Index,
const llvm::Twine &Name = "") {
llvm::Type *ElTy = Addr.getElementType();
const llvm::DataLayout &DL = BB->getParent()->getParent()->getDataLayout();
const llvm::DataLayout &DL = BB->getDataLayout();
CharUnits EltSize = CharUnits::fromQuantity(DL.getTypeAllocSize(ElTy));

return Address(
Expand All @@ -277,7 +277,7 @@ class CGBuilderTy : public CGBuilderBaseTy {
Address CreateConstGEP(Address Addr, uint64_t Index,
const llvm::Twine &Name = "") {
llvm::Type *ElTy = Addr.getElementType();
const llvm::DataLayout &DL = BB->getParent()->getParent()->getDataLayout();
const llvm::DataLayout &DL = BB->getDataLayout();
CharUnits EltSize = CharUnits::fromQuantity(DL.getTypeAllocSize(ElTy));

return Address(CreateGEP(ElTy, Addr.getBasePointer(), getSize(Index), Name),
Expand All @@ -290,7 +290,7 @@ class CGBuilderTy : public CGBuilderBaseTy {
using CGBuilderBaseTy::CreateGEP;
Address CreateGEP(CodeGenFunction &CGF, Address Addr, llvm::Value *Index,
const llvm::Twine &Name = "") {
const llvm::DataLayout &DL = BB->getParent()->getParent()->getDataLayout();
const llvm::DataLayout &DL = BB->getDataLayout();
CharUnits EltSize =
CharUnits::fromQuantity(DL.getTypeAllocSize(Addr.getElementType()));

Expand Down Expand Up @@ -412,7 +412,7 @@ class CGBuilderTy : public CGBuilderBaseTy {
unsigned FieldIndex,
llvm::MDNode *DbgInfo) {
llvm::StructType *ElTy = cast<llvm::StructType>(Addr.getElementType());
const llvm::DataLayout &DL = BB->getParent()->getParent()->getDataLayout();
const llvm::DataLayout &DL = BB->getDataLayout();
const llvm::StructLayout *Layout = DL.getStructLayout(ElTy);
auto Offset = CharUnits::fromQuantity(Layout->getElementOffset(Index));

Expand Down
61 changes: 37 additions & 24 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10246,11 +10246,15 @@ Value *CodeGenFunction::EmitSVETupleSetOrGet(const SVETypeFlags &TypeFlags,
llvm::Type *Ty,
ArrayRef<Value *> Ops) {
assert((TypeFlags.isTupleSet() || TypeFlags.isTupleGet()) &&
"Expects TypleFlag isTupleSet or TypeFlags.isTupleSet()");
"Expects TypleFlags.isTupleSet() or TypeFlags.isTupleGet()");

unsigned I = cast<ConstantInt>(Ops[1])->getSExtValue();
auto *SingleVecTy = dyn_cast<llvm::ScalableVectorType>(
TypeFlags.isTupleSet() ? Ops[2]->getType() : Ty);
TypeFlags.isTupleSet() ? Ops[2]->getType() : Ty);

if (!SingleVecTy)
return nullptr;

Value *Idx = ConstantInt::get(CGM.Int64Ty,
I * SingleVecTy->getMinNumElements());

Expand All @@ -10265,6 +10269,10 @@ Value *CodeGenFunction::EmitSVETupleCreate(const SVETypeFlags &TypeFlags,
assert(TypeFlags.isTupleCreate() && "Expects TypleFlag isTupleCreate");

auto *SrcTy = dyn_cast<llvm::ScalableVectorType>(Ops[0]->getType());

if (!SrcTy)
return nullptr;

unsigned MinElts = SrcTy->getMinNumElements();
Value *Call = llvm::PoisonValue::get(Ty);
for (unsigned I = 0; I < Ops.size(); I++) {
Expand Down Expand Up @@ -16740,10 +16748,10 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID,
auto GenAIXPPCBuiltinCpuExpr = [&](unsigned SupportMethod, unsigned FieldIdx,
unsigned Mask, CmpInst::Predicate CompOp,
unsigned OpValue) -> Value * {
if (SupportMethod == AIX_BUILTIN_PPC_FALSE)
if (SupportMethod == BUILTIN_PPC_FALSE)
return llvm::ConstantInt::getFalse(ConvertType(E->getType()));

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

assert(SupportMethod <= SYS_CALL && "Invalid value for SupportMethod.");
Expand Down Expand Up @@ -16795,34 +16803,39 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID,
StringRef CPUStr = cast<clang::StringLiteral>(CPUExpr)->getString();
llvm::Triple Triple = getTarget().getTriple();

if (Triple.isOSAIX()) {
unsigned SupportMethod, FieldIdx, CpuIdValue;
CmpInst::Predicate CompareOp;
typedef std::tuple<unsigned, unsigned, CmpInst::Predicate, unsigned>
CPUType;
std::tie(SupportMethod, FieldIdx, CompareOp, CpuIdValue) =
static_cast<CPUType>(StringSwitch<CPUType>(CPUStr)
#define PPC_AIX_CPU(NAME, SUPPORT_METHOD, INDEX, COMPARE_OP, VALUE) \
.Case(NAME, {SUPPORT_METHOD, INDEX, COMPARE_OP, VALUE})
unsigned LinuxSupportMethod, LinuxIDValue, AIXSupportMethod, AIXIDValue;
typedef std::tuple<unsigned, unsigned, unsigned, unsigned> CPUInfo;

std::tie(LinuxSupportMethod, LinuxIDValue, AIXSupportMethod, AIXIDValue) =
static_cast<CPUInfo>(StringSwitch<CPUInfo>(CPUStr)
#define PPC_CPU(NAME, Linux_SUPPORT_METHOD, LinuxID, AIX_SUPPORT_METHOD, \
AIXID) \
.Case(NAME, {Linux_SUPPORT_METHOD, LinuxID, AIX_SUPPORT_METHOD, AIXID})
#include "llvm/TargetParser/PPCTargetParser.def"
.Default({AIX_BUILTIN_PPC_FALSE, 0,
CmpInst::Predicate(), 0}));
return GenAIXPPCBuiltinCpuExpr(SupportMethod, FieldIdx, 0, CompareOp,
CpuIdValue);
.Default({BUILTIN_PPC_UNSUPPORTED, 0,
BUILTIN_PPC_UNSUPPORTED, 0}));

if (Triple.isOSAIX()) {
assert((AIXSupportMethod != BUILTIN_PPC_UNSUPPORTED) &&
"Invalid CPU name. Missed by SemaChecking?");
return GenAIXPPCBuiltinCpuExpr(AIXSupportMethod, AIX_SYSCON_IMPL_IDX, 0,
ICmpInst::ICMP_EQ, AIXIDValue);
}

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"
.Default(-1U);
assert(NumCPUID < -1U && "Invalid CPU name. Missed by SemaChecking?");

assert((LinuxSupportMethod != BUILTIN_PPC_UNSUPPORTED) &&
"Invalid CPU name. Missed by SemaChecking?");

if (LinuxSupportMethod == BUILTIN_PPC_FALSE)
return llvm::ConstantInt::getFalse(ConvertType(E->getType()));

Value *Op0 = llvm::ConstantInt::get(Int32Ty, PPC_FAWORD_CPUID);
llvm::Function *F = CGM.getIntrinsic(Intrinsic::ppc_fixed_addr_ld);
Value *TheCall = Builder.CreateCall(F, {Op0}, "cpu_is");
return Builder.CreateICmpEQ(TheCall,
llvm::ConstantInt::get(Int32Ty, NumCPUID));
llvm::ConstantInt::get(Int32Ty, LinuxIDValue));
}
case Builtin::BI__builtin_cpu_supports: {
llvm::Triple Triple = getTarget().getTriple();
Expand All @@ -16840,7 +16853,7 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID,
VALUE) \
.Case(NAME, {SUPPORT_METHOD, INDEX, MASK, COMP_OP, VALUE})
#include "llvm/TargetParser/PPCTargetParser.def"
.Default({AIX_BUILTIN_PPC_FALSE, 0, 0,
.Default({BUILTIN_PPC_FALSE, 0, 0,
CmpInst::Predicate(), 0}));
return GenAIXPPCBuiltinCpuExpr(SupportMethod, FieldIdx, Mask, CompOp,
Value);
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/CGCoroutine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ static void createCoroData(CodeGenFunction &CGF,
return;
}

CurCoro.Data = std::unique_ptr<CGCoroData>(new CGCoroData);
CurCoro.Data = std::make_unique<CGCoroData>();
CurCoro.Data->CoroId = CoroId;
CurCoro.Data->CoroIdExpr = CoroIdExpr;
}
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/CGDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,10 @@ CodeGenModule::EmitCXXGlobalInitFunc() {
// No Itanium initializer in header like modules.
if (M->isHeaderLikeModule())
continue;
// We're allowed to skip the initialization if we are sure it doesn't
// do any thing.
if (!M->isNamedModuleInterfaceHasInit())
continue;
llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false);
SmallString<256> FnName;
{
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/CodeGen/CGOpenMPRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2553,6 +2553,15 @@ void CGOpenMPRuntime::emitForDispatchInit(
Args);
}

void CGOpenMPRuntime::emitForDispatchDeinit(CodeGenFunction &CGF,
SourceLocation Loc) {
if (!CGF.HaveInsertPoint())
return;
// Call __kmpc_dispatch_deinit(ident_t *loc, kmp_int32 tid);
llvm::Value *Args[] = {emitUpdateLocation(CGF, Loc), getThreadID(CGF, Loc)};
CGF.EmitRuntimeCall(OMPBuilder.createDispatchDeinitFunction(), Args);
}

static void emitForStaticInitCall(
CodeGenFunction &CGF, llvm::Value *UpdateLocation, llvm::Value *ThreadId,
llvm::FunctionCallee ForStaticInitFunction, OpenMPSchedType Schedule,
Expand Down Expand Up @@ -11996,6 +12005,11 @@ void CGOpenMPSIMDRuntime::emitForDispatchInit(
llvm_unreachable("Not supported in SIMD-only mode");
}

void CGOpenMPSIMDRuntime::emitForDispatchDeinit(CodeGenFunction &CGF,
SourceLocation Loc) {
llvm_unreachable("Not supported in SIMD-only mode");
}

void CGOpenMPSIMDRuntime::emitForStaticInit(
CodeGenFunction &CGF, SourceLocation Loc, OpenMPDirectiveKind DKind,
const OpenMPScheduleTy &ScheduleKind, const StaticRTInput &Values) {
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/CodeGen/CGOpenMPRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,14 @@ class CGOpenMPRuntime {
unsigned IVSize, bool IVSigned, bool Ordered,
const DispatchRTInput &DispatchValues);

/// This is used for non static scheduled types and when the ordered
/// clause is present on the loop construct.
///
/// \param CGF Reference to current CodeGenFunction.
/// \param Loc Clang source location.
///
virtual void emitForDispatchDeinit(CodeGenFunction &CGF, SourceLocation Loc);

/// Struct with the values to be passed to the static runtime function
struct StaticRTInput {
/// Size of the iteration variable in bits.
Expand Down Expand Up @@ -1829,6 +1837,14 @@ class CGOpenMPSIMDRuntime final : public CGOpenMPRuntime {
unsigned IVSize, bool IVSigned, bool Ordered,
const DispatchRTInput &DispatchValues) override;

/// This is used for non static scheduled types and when the ordered
/// clause is present on the loop construct.
///
/// \param CGF Reference to current CodeGenFunction.
/// \param Loc Clang source location.
///
void emitForDispatchDeinit(CodeGenFunction &CGF, SourceLocation Loc) override;

/// Call the appropriate runtime routine to initialize it before start
/// of loop.
///
Expand Down
173 changes: 86 additions & 87 deletions clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2227,113 +2227,112 @@ bool CGOpenMPRuntimeGPU::hasAllocateAttributeForGlobalVar(const VarDecl *VD,
return false;
}

// Get current CudaArch and ignore any unknown values
static CudaArch getCudaArch(CodeGenModule &CGM) {
// Get current OffloadArch and ignore any unknown values
static OffloadArch getOffloadArch(CodeGenModule &CGM) {
if (!CGM.getTarget().hasFeature("ptx"))
return CudaArch::UNKNOWN;
return OffloadArch::UNKNOWN;
for (const auto &Feature : CGM.getTarget().getTargetOpts().FeatureMap) {
if (Feature.getValue()) {
CudaArch Arch = StringToCudaArch(Feature.getKey());
if (Arch != CudaArch::UNKNOWN)
OffloadArch Arch = StringToOffloadArch(Feature.getKey());
if (Arch != OffloadArch::UNKNOWN)
return Arch;
}
}
return CudaArch::UNKNOWN;
return OffloadArch::UNKNOWN;
}

/// Check to see if target architecture supports unified addressing which is
/// a restriction for OpenMP requires clause "unified_shared_memory".
void CGOpenMPRuntimeGPU::processRequiresDirective(
const OMPRequiresDecl *D) {
void CGOpenMPRuntimeGPU::processRequiresDirective(const OMPRequiresDecl *D) {
for (const OMPClause *Clause : D->clauselists()) {
if (Clause->getClauseKind() == OMPC_unified_shared_memory) {
CudaArch Arch = getCudaArch(CGM);
OffloadArch Arch = getOffloadArch(CGM);
switch (Arch) {
case CudaArch::SM_20:
case CudaArch::SM_21:
case CudaArch::SM_30:
case CudaArch::SM_32_:
case CudaArch::SM_35:
case CudaArch::SM_37:
case CudaArch::SM_50:
case CudaArch::SM_52:
case CudaArch::SM_53: {
case OffloadArch::SM_20:
case OffloadArch::SM_21:
case OffloadArch::SM_30:
case OffloadArch::SM_32_:
case OffloadArch::SM_35:
case OffloadArch::SM_37:
case OffloadArch::SM_50:
case OffloadArch::SM_52:
case OffloadArch::SM_53: {
SmallString<256> Buffer;
llvm::raw_svector_ostream Out(Buffer);
Out << "Target architecture " << CudaArchToString(Arch)
Out << "Target architecture " << OffloadArchToString(Arch)
<< " does not support unified addressing";
CGM.Error(Clause->getBeginLoc(), Out.str());
return;
}
case CudaArch::SM_60:
case CudaArch::SM_61:
case CudaArch::SM_62:
case CudaArch::SM_70:
case CudaArch::SM_72:
case CudaArch::SM_75:
case CudaArch::SM_80:
case CudaArch::SM_86:
case CudaArch::SM_87:
case CudaArch::SM_89:
case CudaArch::SM_90:
case CudaArch::SM_90a:
case CudaArch::GFX600:
case CudaArch::GFX601:
case CudaArch::GFX602:
case CudaArch::GFX700:
case CudaArch::GFX701:
case CudaArch::GFX702:
case CudaArch::GFX703:
case CudaArch::GFX704:
case CudaArch::GFX705:
case CudaArch::GFX801:
case CudaArch::GFX802:
case CudaArch::GFX803:
case CudaArch::GFX805:
case CudaArch::GFX810:
case CudaArch::GFX9_GENERIC:
case CudaArch::GFX900:
case CudaArch::GFX902:
case CudaArch::GFX904:
case CudaArch::GFX906:
case CudaArch::GFX908:
case CudaArch::GFX909:
case CudaArch::GFX90a:
case CudaArch::GFX90c:
case CudaArch::GFX940:
case CudaArch::GFX941:
case CudaArch::GFX942:
case CudaArch::GFX10_1_GENERIC:
case CudaArch::GFX1010:
case CudaArch::GFX1011:
case CudaArch::GFX1012:
case CudaArch::GFX1013:
case CudaArch::GFX10_3_GENERIC:
case CudaArch::GFX1030:
case CudaArch::GFX1031:
case CudaArch::GFX1032:
case CudaArch::GFX1033:
case CudaArch::GFX1034:
case CudaArch::GFX1035:
case CudaArch::GFX1036:
case CudaArch::GFX11_GENERIC:
case CudaArch::GFX1100:
case CudaArch::GFX1101:
case CudaArch::GFX1102:
case CudaArch::GFX1103:
case CudaArch::GFX1150:
case CudaArch::GFX1151:
case CudaArch::GFX1152:
case CudaArch::GFX12_GENERIC:
case CudaArch::GFX1200:
case CudaArch::GFX1201:
case CudaArch::AMDGCNSPIRV:
case CudaArch::Generic:
case CudaArch::UNUSED:
case CudaArch::UNKNOWN:
case OffloadArch::SM_60:
case OffloadArch::SM_61:
case OffloadArch::SM_62:
case OffloadArch::SM_70:
case OffloadArch::SM_72:
case OffloadArch::SM_75:
case OffloadArch::SM_80:
case OffloadArch::SM_86:
case OffloadArch::SM_87:
case OffloadArch::SM_89:
case OffloadArch::SM_90:
case OffloadArch::SM_90a:
case OffloadArch::GFX600:
case OffloadArch::GFX601:
case OffloadArch::GFX602:
case OffloadArch::GFX700:
case OffloadArch::GFX701:
case OffloadArch::GFX702:
case OffloadArch::GFX703:
case OffloadArch::GFX704:
case OffloadArch::GFX705:
case OffloadArch::GFX801:
case OffloadArch::GFX802:
case OffloadArch::GFX803:
case OffloadArch::GFX805:
case OffloadArch::GFX810:
case OffloadArch::GFX9_GENERIC:
case OffloadArch::GFX900:
case OffloadArch::GFX902:
case OffloadArch::GFX904:
case OffloadArch::GFX906:
case OffloadArch::GFX908:
case OffloadArch::GFX909:
case OffloadArch::GFX90a:
case OffloadArch::GFX90c:
case OffloadArch::GFX940:
case OffloadArch::GFX941:
case OffloadArch::GFX942:
case OffloadArch::GFX10_1_GENERIC:
case OffloadArch::GFX1010:
case OffloadArch::GFX1011:
case OffloadArch::GFX1012:
case OffloadArch::GFX1013:
case OffloadArch::GFX10_3_GENERIC:
case OffloadArch::GFX1030:
case OffloadArch::GFX1031:
case OffloadArch::GFX1032:
case OffloadArch::GFX1033:
case OffloadArch::GFX1034:
case OffloadArch::GFX1035:
case OffloadArch::GFX1036:
case OffloadArch::GFX11_GENERIC:
case OffloadArch::GFX1100:
case OffloadArch::GFX1101:
case OffloadArch::GFX1102:
case OffloadArch::GFX1103:
case OffloadArch::GFX1150:
case OffloadArch::GFX1151:
case OffloadArch::GFX1152:
case OffloadArch::GFX12_GENERIC:
case OffloadArch::GFX1200:
case OffloadArch::GFX1201:
case OffloadArch::AMDGCNSPIRV:
case OffloadArch::Generic:
case OffloadArch::UNUSED:
case OffloadArch::UNKNOWN:
break;
case CudaArch::LAST:
llvm_unreachable("Unexpected Cuda arch.");
case OffloadArch::LAST:
llvm_unreachable("Unexpected GPU arch.");
}
}
}
Expand Down
Loading