1 change: 1 addition & 0 deletions clang/lib/AST/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ add_clang_library(clangAST
Interp/Record.cpp
Interp/Source.cpp
Interp/State.cpp
Interp/MemberPointer.cpp
Interp/InterpShared.cpp
ItaniumCXXABI.cpp
ItaniumMangle.cpp
Expand Down
40 changes: 33 additions & 7 deletions clang/lib/AST/DeclBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,17 @@ void *Decl::operator new(std::size_t Size, const ASTContext &Context,
GlobalDeclID ID, std::size_t Extra) {
// Allocate an extra 8 bytes worth of storage, which ensures that the
// resulting pointer will still be 8-byte aligned.
static_assert(sizeof(unsigned) * 2 >= alignof(Decl),
"Decl won't be misaligned");
static_assert(sizeof(uint64_t) >= alignof(Decl), "Decl won't be misaligned");
void *Start = Context.Allocate(Size + Extra + 8);
void *Result = (char*)Start + 8;

unsigned *PrefixPtr = (unsigned *)Result - 2;
uint64_t *PrefixPtr = (uint64_t *)Result - 1;

// Zero out the first 4 bytes; this is used to store the owning module ID.
PrefixPtr[0] = 0;
*PrefixPtr = ID.get();

// Store the global declaration ID in the second 4 bytes.
PrefixPtr[1] = ID.get();
// We leave the upper 16 bits to store the module IDs. 48 bits should be
// sufficient to store a declaration ID.
assert(*PrefixPtr < llvm::maskTrailingOnes<uint64_t>(48));

return Result;
}
Expand All @@ -111,6 +110,29 @@ void *Decl::operator new(std::size_t Size, const ASTContext &Ctx,
return ::operator new(Size + Extra, Ctx);
}

GlobalDeclID Decl::getGlobalID() const {
if (!isFromASTFile())
return GlobalDeclID();
// See the comments in `Decl::operator new` for details.
uint64_t ID = *((const uint64_t *)this - 1);
return GlobalDeclID(ID & llvm::maskTrailingOnes<uint64_t>(48));
}

unsigned Decl::getOwningModuleID() const {
if (!isFromASTFile())
return 0;

uint64_t ID = *((const uint64_t *)this - 1);
return ID >> 48;
}

void Decl::setOwningModuleID(unsigned ID) {
assert(isFromASTFile() && "Only works on a deserialized declaration");
uint64_t *IDAddress = (uint64_t *)this - 1;
*IDAddress &= llvm::maskTrailingOnes<uint64_t>(48);
*IDAddress |= (uint64_t)ID << 48;
}

Module *Decl::getOwningModuleSlow() const {
assert(isFromASTFile() && "Not from AST file?");
return getASTContext().getExternalSource()->getModule(getOwningModuleID());
Expand Down Expand Up @@ -2163,3 +2185,7 @@ DependentDiagnostic *DependentDiagnostic::Create(ASTContext &C,

return DD;
}

unsigned DeclIDBase::getLocalDeclIndex() const {
return ID & llvm::maskTrailingOnes<DeclID>(32);
}
4 changes: 2 additions & 2 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ void LazyASTUnresolvedSet::getFromExternalSource(ASTContext &C) const {
assert(Source && "getFromExternalSource with no external source");

for (ASTUnresolvedSet::iterator I = Impl.begin(); I != Impl.end(); ++I)
I.setDecl(cast<NamedDecl>(Source->GetExternalDecl(
GlobalDeclID(reinterpret_cast<uintptr_t>(I.getDecl()) >> 2))));
I.setDecl(
cast<NamedDecl>(Source->GetExternalDecl(GlobalDeclID(I.getDeclID()))));
Impl.Decls.setLazy(false);
}

Expand Down
27 changes: 21 additions & 6 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15209,11 +15209,21 @@ bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
APFloat &ResI = Result.getComplexFloatImag();
if (LHSReal) {
assert(!RHSReal && "Cannot have two real operands for a complex op!");
ResR = A * C;
ResI = A * D;
ResR = A;
ResI = A;
// ResR = A * C;
// ResI = A * D;
if (!handleFloatFloatBinOp(Info, E, ResR, BO_Mul, C) ||
!handleFloatFloatBinOp(Info, E, ResI, BO_Mul, D))
return false;
} else if (RHSReal) {
ResR = C * A;
ResI = C * B;
// ResR = C * A;
// ResI = C * B;
ResR = C;
ResI = C;
if (!handleFloatFloatBinOp(Info, E, ResR, BO_Mul, A) ||
!handleFloatFloatBinOp(Info, E, ResI, BO_Mul, B))
return false;
} else {
// In the fully general case, we need to handle NaNs and infinities
// robustly.
Expand Down Expand Up @@ -15289,8 +15299,13 @@ bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
APFloat &ResR = Result.getComplexFloatReal();
APFloat &ResI = Result.getComplexFloatImag();
if (RHSReal) {
ResR = A / C;
ResI = B / C;
ResR = A;
ResI = B;
// ResR = A / C;
// ResI = B / C;
if (!handleFloatFloatBinOp(Info, E, ResR, BO_Div, C) ||
!handleFloatFloatBinOp(Info, E, ResI, BO_Div, C))
return false;
} else {
if (LHSReal) {
// No real optimizations we can do here, stub out with zero.
Expand Down
209 changes: 166 additions & 43 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions clang/lib/AST/Interp/ByteCodeExprGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
/// Visit an APValue.
bool visitAPValue(const APValue &Val, PrimType ValType, const Expr *E);
bool visitAPValueInitializer(const APValue &Val, const Expr *E);
/// Visit the given decl as if we have a reference to it.
bool visitDeclRef(const ValueDecl *D, const Expr *E);

/// Visits an expression and converts it to a boolean.
bool visitBool(const Expr *E);
Expand Down
15 changes: 9 additions & 6 deletions clang/lib/AST/Interp/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,12 @@ std::optional<PrimType> Context::classify(QualType T) const {
if (T->isFloatingType())
return PT_Float;

if (T->isSpecificBuiltinType(BuiltinType::BoundMember) ||
T->isMemberPointerType())
return PT_MemberPtr;

if (T->isFunctionPointerType() || T->isFunctionReferenceType() ||
T->isFunctionType() || T->isSpecificBuiltinType(BuiltinType::BoundMember))
T->isFunctionType())
return PT_FnPtr;

if (T->isReferenceType() || T->isPointerType() ||
Expand All @@ -177,9 +181,6 @@ std::optional<PrimType> Context::classify(QualType T) const {
if (const auto *DT = dyn_cast<DecltypeType>(T))
return classify(DT->getUnderlyingType());

if (const auto *DT = dyn_cast<MemberPointerType>(T))
return classify(DT->getPointeeType());

return std::nullopt;
}

Expand Down Expand Up @@ -292,10 +293,12 @@ unsigned Context::collectBaseOffset(const RecordDecl *BaseDecl,
}
if (CurDecl == FinalDecl)
break;

// break;
}

assert(OffsetSum > 0);
return OffsetSum;
}

const Record *Context::getRecord(const RecordDecl *D) const {
return P->getOrCreateRecord(D);
}
2 changes: 2 additions & 0 deletions clang/lib/AST/Interp/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ class Context final {
unsigned collectBaseOffset(const RecordDecl *BaseDecl,
const RecordDecl *DerivedDecl) const;

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

private:
/// Runs a function.
bool Run(State &Parent, const Function *Func, APValue &Result);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "Floating.h"
#include "FunctionPointer.h"
#include "IntegralAP.h"
#include "MemberPointer.h"
#include "Pointer.h"
#include "PrimType.h"
#include "Record.h"
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/Disasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "Integral.h"
#include "IntegralAP.h"
#include "InterpFrame.h"
#include "MemberPointer.h"
#include "Opcode.h"
#include "PrimType.h"
#include "Program.h"
Expand Down Expand Up @@ -122,6 +123,8 @@ static const char *primTypeToString(PrimType T) {
return "Ptr";
case PT_FnPtr:
return "FnPtr";
case PT_MemberPtr:
return "MemberPtr";
}
llvm_unreachable("Unhandled PrimType");
}
Expand Down
35 changes: 19 additions & 16 deletions clang/lib/AST/Interp/EvalEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ void EvalEmitter::cleanup() { S.cleanup(); }
EvaluationResult EvalEmitter::interpretExpr(const Expr *E,
bool ConvertResultToRValue) {
S.setEvalLocation(E->getExprLoc());
this->ConvertResultToRValue = ConvertResultToRValue;
this->ConvertResultToRValue = ConvertResultToRValue && !isa<ConstantExpr>(E);
this->CheckFullyInitialized = isa<ConstantExpr>(E);
EvalResult.setSource(E);

if (!this->visitExpr(E)) {
Expand All @@ -55,10 +56,14 @@ EvaluationResult EvalEmitter::interpretExpr(const Expr *E,
EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD,
bool CheckFullyInitialized) {
this->CheckFullyInitialized = CheckFullyInitialized;
this->ConvertResultToRValue =
VD->getAnyInitializer() &&
(VD->getAnyInitializer()->getType()->isAnyComplexType() ||
VD->getAnyInitializer()->getType()->isVectorType());

if (const Expr *Init = VD->getAnyInitializer()) {
QualType T = VD->getType();
this->ConvertResultToRValue = !Init->isGLValue() && !T->isPointerType() &&
!T->isObjCObjectPointerType();
} else
this->ConvertResultToRValue = false;

EvalResult.setSource(VD);

if (!this->visitDecl(VD) && EvalResult.empty())
Expand Down Expand Up @@ -137,6 +142,10 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
return true;

const Pointer &Ptr = S.Stk.pop<Pointer>();

if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
return false;

// Implicitly convert lvalue to rvalue, if requested.
if (ConvertResultToRValue) {
if (std::optional<APValue> V = Ptr.toRValue(Ctx)) {
Expand All @@ -145,17 +154,7 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
return false;
}
} else {
if (CheckFullyInitialized) {
if (!EvalResult.checkFullyInitialized(S, Ptr))
return false;

std::optional<APValue> RValueResult = Ptr.toRValue(Ctx);
if (!RValueResult)
return false;
EvalResult.setValue(*RValueResult);
} else {
EvalResult.setValue(Ptr.toAPValue());
}
EvalResult.setValue(Ptr.toAPValue());
}

return true;
Expand All @@ -175,6 +174,10 @@ bool EvalEmitter::emitRetVoid(const SourceInfo &Info) {

bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
const auto &Ptr = S.Stk.pop<Pointer>();

if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
return false;

if (std::optional<APValue> APV = Ptr.toRValue(S.getCtx())) {
EvalResult.setValue(*APV);
return true;
Expand Down
23 changes: 12 additions & 11 deletions clang/lib/AST/Interp/EvaluationResult.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,21 +141,22 @@ bool EvaluationResult::checkFullyInitialized(InterpState &S,
const Pointer &Ptr) const {
assert(Source);
assert(empty());

// Our Source must be a VarDecl.
const Decl *SourceDecl = Source.dyn_cast<const Decl *>();
assert(SourceDecl);
const auto *VD = cast<VarDecl>(SourceDecl);
assert(VD->getType()->isRecordType() || VD->getType()->isArrayType());
SourceLocation InitLoc = VD->getAnyInitializer()->getExprLoc();

assert(!Ptr.isZero());

SourceLocation InitLoc;
if (const auto *D = Source.dyn_cast<const Decl *>())
InitLoc = cast<VarDecl>(D)->getAnyInitializer()->getExprLoc();
else if (const auto *E = Source.dyn_cast<const Expr *>())
InitLoc = E->getExprLoc();

if (const Record *R = Ptr.getRecord())
return CheckFieldsInitialized(S, InitLoc, Ptr, R);
const auto *CAT =
cast<ConstantArrayType>(Ptr.getType()->getAsArrayTypeUnsafe());
return CheckArrayInitialized(S, InitLoc, Ptr, CAT);

if (const auto *CAT = dyn_cast_if_present<ConstantArrayType>(
Ptr.getType()->getAsArrayTypeUnsafe()))
return CheckArrayInitialized(S, InitLoc, Ptr, CAT);

return true;
}

} // namespace interp
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/AST/Interp/Function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ SourceInfo Function::getSource(CodePtr PC) const {
unsigned Offset = PC - getCodeBegin();
using Elem = std::pair<unsigned, SourceInfo>;
auto It = llvm::lower_bound(SrcMap, Elem{Offset, {}}, llvm::less_first());
assert(It != SrcMap.end());
if (It == SrcMap.end())
return SrcMap.back().second;
return It->second;
}

Expand Down
39 changes: 31 additions & 8 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,26 @@ bool CheckSubobject(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
return false;
}

bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
uint32_t Offset) {
uint32_t MinOffset = Ptr.getDeclDesc()->getMetadataSize();
uint32_t PtrOffset = Ptr.getByteOffset();

// We subtract Offset from PtrOffset. The result must be at least
// MinOffset.
if (Offset < PtrOffset && (PtrOffset - Offset) >= MinOffset)
return true;

const auto *E = cast<CastExpr>(S.Current->getExpr(OpPC));
QualType TargetQT = E->getType()->getPointeeType();
QualType MostDerivedQT = Ptr.getDeclPtr().getType();

S.CCEDiag(E, diag::note_constexpr_invalid_downcast)
<< MostDerivedQT << TargetQT;

return false;
}

bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
assert(Ptr.isLive() && "Pointer is not live");
if (!Ptr.isConst())
Expand Down Expand Up @@ -493,10 +513,12 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (!CheckLive(S, OpPC, Ptr, AK_MemberCall))
return false;
if (!CheckExtern(S, OpPC, Ptr))
return false;
if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))
return false;
if (!Ptr.isDummy()) {
if (!CheckExtern(S, OpPC, Ptr))
return false;
if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))
return false;
}
return true;
}

Expand All @@ -516,7 +538,7 @@ bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
return false;
}

if (!F->isConstexpr()) {
if (!F->isConstexpr() || !F->hasBody()) {
const SourceLocation &Loc = S.Current->getLocation(OpPC);
if (S.getLangOpts().CPlusPlus11) {
const FunctionDecl *DiagDecl = F->getDecl();
Expand Down Expand Up @@ -550,9 +572,10 @@ bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
S.checkingPotentialConstantExpression())
return false;

// If the declaration is defined _and_ declared 'constexpr', the below
// diagnostic doesn't add anything useful.
if (DiagDecl->isDefined() && DiagDecl->isConstexpr())
// If the declaration is defined, declared 'constexpr' _and_ has a body,
// the below diagnostic doesn't add anything useful.
if (DiagDecl->isDefined() && DiagDecl->isConstexpr() &&
DiagDecl->hasBody())
return false;

S.FFDiag(Loc, diag::note_constexpr_invalid_function, 1)
Expand Down
148 changes: 144 additions & 4 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "InterpFrame.h"
#include "InterpStack.h"
#include "InterpState.h"
#include "MemberPointer.h"
#include "Opcode.h"
#include "PrimType.h"
#include "Program.h"
Expand Down Expand Up @@ -75,6 +76,11 @@ bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
bool CheckSubobject(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
CheckSubobjectKind CSK);

/// Checks if the dowcast using the given offset is possible with the given
/// pointer.
bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
uint32_t Offset);

/// Checks if a pointer points to const storage.
bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr);

Expand Down Expand Up @@ -725,6 +731,9 @@ using CompareFn = llvm::function_ref<bool(ComparisonCategoryResult)>;

template <typename T>
bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) {
assert((!std::is_same_v<T, MemberPointer>) &&
"Non-equality comparisons on member pointer types should already be "
"rejected in Sema.");
using BoolT = PrimConv<PT_Bool>::T;
const T &RHS = S.Stk.pop<T>();
const T &LHS = S.Stk.pop<T>();
Expand Down Expand Up @@ -834,6 +843,47 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
}
}

template <>
inline bool CmpHelperEQ<MemberPointer>(InterpState &S, CodePtr OpPC,
CompareFn Fn) {
const auto &RHS = S.Stk.pop<MemberPointer>();
const auto &LHS = S.Stk.pop<MemberPointer>();

// If either operand is a pointer to a weak function, the comparison is not
// constant.
for (const auto &MP : {LHS, RHS}) {
if (const CXXMethodDecl *MD = MP.getMemberFunction(); MD && MD->isWeak()) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_mem_pointer_weak_comparison) << MD;
return false;
}
}

// C++11 [expr.eq]p2:
// If both operands are null, they compare equal. Otherwise if only one is
// null, they compare unequal.
if (LHS.isZero() && RHS.isZero()) {
S.Stk.push<Boolean>(Fn(ComparisonCategoryResult::Equal));
return true;
}
if (LHS.isZero() || RHS.isZero()) {
S.Stk.push<Boolean>(Fn(ComparisonCategoryResult::Unordered));
return true;
}

// We cannot compare against virtual declarations at compile time.
for (const auto &MP : {LHS, RHS}) {
if (const CXXMethodDecl *MD = MP.getMemberFunction();
MD && MD->isVirtual()) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.CCEDiag(Loc, diag::note_constexpr_compare_virtual_mem_ptr) << MD;
}
}

S.Stk.push<Boolean>(Boolean::from(Fn(LHS.compare(RHS))));
return true;
}

template <PrimType Name, class T = typename PrimConv<Name>::T>
bool EQ(InterpState &S, CodePtr OpPC) {
return CmpHelperEQ<T>(S, OpPC, [](ComparisonCategoryResult R) {
Expand Down Expand Up @@ -1233,9 +1283,32 @@ inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
return true;
}

/// 1) Pops a Pointer from the stack
/// 1) Peeks a Pointer
/// 2) Pushes Pointer.atField(Off) on the stack
inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) {
const Pointer &Ptr = S.Stk.peek<Pointer>();

if (S.getLangOpts().CPlusPlus && S.inConstantContext() &&
!CheckNull(S, OpPC, Ptr, CSK_Field))
return false;

if (!CheckExtern(S, OpPC, Ptr))
return false;
if (!CheckRange(S, OpPC, Ptr, CSK_Field))
return false;
if (!CheckArray(S, OpPC, Ptr))
return false;
if (!CheckSubobject(S, OpPC, Ptr, CSK_Field))
return false;

if (Ptr.isBlockPointer() && Off > Ptr.block()->getSize())
return false;

S.Stk.push<Pointer>(Ptr.atField(Off));
return true;
}

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

if (S.getLangOpts().CPlusPlus && S.inConstantContext() &&
Expand Down Expand Up @@ -1300,6 +1373,9 @@ inline bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off) {
return false;
if (!CheckSubobject(S, OpPC, Ptr, CSK_Derived))
return false;
if (!CheckDowncast(S, OpPC, Ptr, Off))
return false;

S.Stk.push<Pointer>(Ptr.atFieldSub(Off));
return true;
}
Expand All @@ -1324,6 +1400,12 @@ inline bool GetPtrBasePop(InterpState &S, CodePtr OpPC, uint32_t Off) {
return true;
}

inline bool GetMemberPtrBasePop(InterpState &S, CodePtr OpPC, int32_t Off) {
const auto &Ptr = S.Stk.pop<MemberPointer>();
S.Stk.push<MemberPointer>(Ptr.atInstanceBase(Off));
return true;
}

inline bool GetPtrThisBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
if (S.checkingPotentialConstantExpression())
return false;
Expand Down Expand Up @@ -1532,6 +1614,24 @@ inline bool Memcpy(InterpState &S, CodePtr OpPC) {
return DoMemcpy(S, OpPC, Src, Dest);
}

inline bool ToMemberPtr(InterpState &S, CodePtr OpPC) {
const auto &Member = S.Stk.pop<MemberPointer>();
const auto &Base = S.Stk.pop<Pointer>();

S.Stk.push<MemberPointer>(Member.takeInstance(Base));
return true;
}

inline bool CastMemberPtrPtr(InterpState &S, CodePtr OpPC) {
const auto &MP = S.Stk.pop<MemberPointer>();

if (std::optional<Pointer> Ptr = MP.toPointer(S.Ctx)) {
S.Stk.push<Pointer>(*Ptr);
return true;
}
return false;
}

//===----------------------------------------------------------------------===//
// AddOffset, SubOffset
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -1696,8 +1796,10 @@ inline bool SubPtr(InterpState &S, CodePtr OpPC) {
return true;
}

T A = T::from(LHS.getIndex());
T B = T::from(RHS.getIndex());
T A = LHS.isElementPastEnd() ? T::from(LHS.getNumElems())
: T::from(LHS.getIndex());
T B = RHS.isElementPastEnd() ? T::from(RHS.getNumElems())
: T::from(RHS.getIndex());
return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, A.bitWidth(), A, B);
}

Expand Down Expand Up @@ -1835,6 +1937,9 @@ template <PrimType Name, class T = typename PrimConv<Name>::T>
bool CastPointerIntegral(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (Ptr.isDummy())
return false;

const SourceInfo &E = S.Current->getSource(OpPC);
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
<< 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
Expand All @@ -1847,6 +1952,9 @@ static inline bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC,
uint32_t BitWidth) {
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (Ptr.isDummy())
return false;

const SourceInfo &E = S.Current->getSource(OpPC);
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
<< 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
Expand All @@ -1860,6 +1968,9 @@ static inline bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC,
uint32_t BitWidth) {
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (Ptr.isDummy())
return false;

const SourceInfo &E = S.Current->getSource(OpPC);
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
<< 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
Expand All @@ -1869,6 +1980,13 @@ static inline bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC,
return true;
}

static inline bool VoidPtrCast(InterpState &S, CodePtr OpPC) {
const SourceInfo &E = S.Current->getSource(OpPC);
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
<< 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
return true;
}

//===----------------------------------------------------------------------===//
// Zero, Nullptr
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -2115,7 +2233,7 @@ inline bool ArrayDecay(InterpState &S, CodePtr OpPC) {
if (!CheckRange(S, OpPC, Ptr, CSK_ArrayToPointer))
return false;

if (!Ptr.isUnknownSizeArray() || Ptr.isDummy()) {
if (Ptr.isRoot() || !Ptr.isUnknownSizeArray() || Ptr.isDummy()) {
S.Stk.push<Pointer>(Ptr.atIndex(0));
return true;
}
Expand Down Expand Up @@ -2329,6 +2447,28 @@ inline bool GetIntPtr(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
return true;
}

inline bool GetMemberPtr(InterpState &S, CodePtr OpPC, const Decl *D) {
S.Stk.push<MemberPointer>(D);
return true;
}

inline bool GetMemberPtrBase(InterpState &S, CodePtr OpPC) {
const auto &MP = S.Stk.pop<MemberPointer>();

S.Stk.push<Pointer>(MP.getBase());
return true;
}

inline bool GetMemberPtrDecl(InterpState &S, CodePtr OpPC) {
const auto &MP = S.Stk.pop<MemberPointer>();

const auto *FD = cast<FunctionDecl>(MP.getDecl());
const auto *Func = S.getContext().getOrCreateFunction(FD);

S.Stk.push<FunctionPointer>(Func);
return true;
}

/// Just emit a diagnostic. The expression that caused emission of this
/// op is not valid in a constant context.
inline bool Invalid(InterpState &S, CodePtr OpPC) {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/InterpFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "Function.h"
#include "InterpStack.h"
#include "InterpState.h"
#include "MemberPointer.h"
#include "Pointer.h"
#include "PrimType.h"
#include "Program.h"
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/InterpStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "Boolean.h"
#include "Floating.h"
#include "Integral.h"
#include "MemberPointer.h"
#include "Pointer.h"
#include <cassert>
#include <cstdlib>
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/InterpStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "FunctionPointer.h"
#include "IntegralAP.h"
#include "MemberPointer.h"
#include "PrimType.h"
#include <memory>
#include <vector>
Expand Down Expand Up @@ -188,6 +189,8 @@ class InterpStack final {
return PT_IntAP;
else if constexpr (std::is_same_v<T, IntegralAP<false>>)
return PT_IntAP;
else if constexpr (std::is_same_v<T, MemberPointer>)
return PT_MemberPtr;

llvm_unreachable("unknown type push()'ed into InterpStack");
}
Expand Down
76 changes: 76 additions & 0 deletions clang/lib/AST/Interp/MemberPointer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//===------------------------- MemberPointer.cpp ----------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "MemberPointer.h"
#include "Context.h"
#include "FunctionPointer.h"
#include "Program.h"
#include "Record.h"

namespace clang {
namespace interp {

std::optional<Pointer> MemberPointer::toPointer(const Context &Ctx) const {
if (!Dcl || isa<FunctionDecl>(Dcl))
return Base;
const FieldDecl *FD = cast<FieldDecl>(Dcl);
assert(FD);

if (!Base.isBlockPointer())
return std::nullopt;

Pointer CastedBase =
(PtrOffset < 0 ? Base.atField(-PtrOffset) : Base.atFieldSub(PtrOffset));

const Record *BaseRecord = CastedBase.getRecord();
if (!BaseRecord)
return std::nullopt;

assert(BaseRecord);
if (FD->getParent() == BaseRecord->getDecl())
return CastedBase.atField(BaseRecord->getField(FD)->Offset);

const RecordDecl *FieldParent = FD->getParent();
const Record *FieldRecord = Ctx.getRecord(FieldParent);

unsigned Offset = 0;
Offset += FieldRecord->getField(FD)->Offset;
Offset += CastedBase.block()->getDescriptor()->getMetadataSize();

if (Offset > CastedBase.block()->getSize())
return std::nullopt;

if (const RecordDecl *BaseDecl = Base.getDeclPtr().getRecord()->getDecl();
BaseDecl != FieldParent)
Offset += Ctx.collectBaseOffset(FieldParent, BaseDecl);

if (Offset > CastedBase.block()->getSize())
return std::nullopt;

assert(Offset <= CastedBase.block()->getSize());
return Pointer(const_cast<Block *>(Base.block()), Offset, Offset);
}

FunctionPointer MemberPointer::toFunctionPointer(const Context &Ctx) const {
return FunctionPointer(Ctx.getProgram().getFunction(cast<FunctionDecl>(Dcl)));
}

APValue MemberPointer::toAPValue() const {
if (isZero())
return APValue(static_cast<ValueDecl *>(nullptr), /*IsDerivedMember=*/false,
/*Path=*/{});

if (hasBase())
return Base.toAPValue();

return APValue(cast<ValueDecl>(getDecl()), /*IsDerivedMember=*/false,
/*Path=*/{});
}

} // namespace interp
} // namespace clang
112 changes: 112 additions & 0 deletions clang/lib/AST/Interp/MemberPointer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//===------------------------- MemberPointer.h ------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_AST_INTERP_MEMBER_POINTER_H
#define LLVM_CLANG_AST_INTERP_MEMBER_POINTER_H

#include "Pointer.h"
#include <optional>

namespace clang {
class ASTContext;
namespace interp {

class Context;
class FunctionPointer;

class MemberPointer final {
private:
Pointer Base;
const Decl *Dcl = nullptr;
int32_t PtrOffset = 0;

MemberPointer(Pointer Base, const Decl *Dcl, int32_t PtrOffset)
: Base(Base), Dcl(Dcl), PtrOffset(PtrOffset) {}

public:
MemberPointer() = default;
MemberPointer(Pointer Base, const Decl *Dcl) : Base(Base), Dcl(Dcl) {}
MemberPointer(uint32_t Address, const Descriptor *D) {
// We only reach this for Address == 0, when creating a null member pointer.
assert(Address == 0);
}

MemberPointer(const Decl *D) : Dcl(D) {
assert((isa<FieldDecl, IndirectFieldDecl, CXXMethodDecl>(D)));
}

uint64_t getIntegerRepresentation() const {
assert(
false &&
"getIntegerRepresentation() shouldn't be reachable for MemberPointers");
return 17;
}

std::optional<Pointer> toPointer(const Context &Ctx) const;

FunctionPointer toFunctionPointer(const Context &Ctx) const;

Pointer getBase() const {
if (PtrOffset < 0)
return Base.atField(-PtrOffset);
return Base.atFieldSub(PtrOffset);
}
bool isMemberFunctionPointer() const {
return isa_and_nonnull<CXXMethodDecl>(Dcl);
}
const CXXMethodDecl *getMemberFunction() const {
return dyn_cast_if_present<CXXMethodDecl>(Dcl);
}
const FieldDecl *getField() const {
return dyn_cast_if_present<FieldDecl>(Dcl);
}

bool hasDecl() const { return Dcl; }
const Decl *getDecl() const { return Dcl; }

MemberPointer atInstanceBase(unsigned Offset) const {
if (Base.isZero())
return MemberPointer(Base, Dcl, Offset);
return MemberPointer(this->Base, Dcl, Offset + PtrOffset);
}

MemberPointer takeInstance(Pointer Instance) const {
assert(this->Base.isZero());
return MemberPointer(Instance, this->Dcl, this->PtrOffset);
}

APValue toAPValue() const;

bool isZero() const { return Base.isZero() && !Dcl; }
bool hasBase() const { return !Base.isZero(); }

void print(llvm::raw_ostream &OS) const {
OS << "MemberPtr(" << Base << " " << (const void *)Dcl << " + " << PtrOffset
<< ")";
}

std::string toDiagnosticString(const ASTContext &Ctx) const {
return "FIXME";
}

ComparisonCategoryResult compare(const MemberPointer &RHS) const {
if (this->Dcl == RHS.Dcl)
return ComparisonCategoryResult::Equal;
return ComparisonCategoryResult::Unordered;
}
};

inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, MemberPointer FP) {
FP.print(OS);
return OS;
}

} // namespace interp
} // namespace clang

#endif
63 changes: 28 additions & 35 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def IntAPS : Type;
def Float : Type;
def Ptr : Type;
def FnPtr : Type;
def MemberPtr : Type;

//===----------------------------------------------------------------------===//
// Types transferred to the interpreter.
Expand Down Expand Up @@ -61,6 +62,7 @@ def ArgOffsetOfExpr : ArgType { let Name = "const OffsetOfExpr *"; }
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*"; }

//===----------------------------------------------------------------------===//
// Classes of types instructions operate on.
Expand Down Expand Up @@ -93,7 +95,7 @@ def AluTypeClass : TypeClass {
}

def PtrTypeClass : TypeClass {
let Types = [Ptr, FnPtr];
let Types = [Ptr, FnPtr, MemberPtr];
}

def BoolTypeClass : TypeClass {
Expand Down Expand Up @@ -208,7 +210,6 @@ def CallBI : Opcode {

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

def CallVar : Opcode {
Expand Down Expand Up @@ -280,54 +281,37 @@ def Null : Opcode {
//===----------------------------------------------------------------------===//
// Pointer generation
//===----------------------------------------------------------------------===//
class OffsetOpcode : Opcode {
let Args = [ArgUint32];
}

// [] -> [Pointer]
def GetPtrLocal : Opcode {
// Offset of local.
let Args = [ArgUint32];
def GetPtrLocal : OffsetOpcode {
bit HasCustomEval = 1;
}
// [] -> [Pointer]
def GetPtrParam : Opcode {
// Offset of parameter.
let Args = [ArgUint32];
}
def GetPtrParam : OffsetOpcode;
// [] -> [Pointer]
def GetPtrGlobal : Opcode {
// Index of global.
let Args = [ArgUint32];
}
def GetPtrGlobal : OffsetOpcode;
// [Pointer] -> [Pointer]
def GetPtrField : Opcode {
// Offset of field.
let Args = [ArgUint32];
}
def GetPtrField : OffsetOpcode;
def GetPtrFieldPop : OffsetOpcode;
// [Pointer] -> [Pointer]
def GetPtrActiveField : Opcode {
// Offset of field.
let Args = [ArgUint32];
}
def GetPtrActiveField : OffsetOpcode;
// [] -> [Pointer]
def GetPtrActiveThisField : Opcode {
// Offset of field.
let Args = [ArgUint32];
}
def GetPtrActiveThisField : OffsetOpcode;
// [] -> [Pointer]
def GetPtrThisField : Opcode {
// Offset of field.
let Args = [ArgUint32];
}
def GetPtrThisField : OffsetOpcode;
// [Pointer] -> [Pointer]
def GetPtrBase : Opcode {
// Offset of field, which is a base.
let Args = [ArgUint32];
}
def GetPtrBase : OffsetOpcode;
// [Pointer] -> [Pointer]
def GetPtrBasePop : Opcode {
def GetPtrBasePop : OffsetOpcode;
def GetMemberPtrBasePop : Opcode {
// Offset of field, which is a base.
let Args = [ArgUint32];
let Args = [ArgSint32];
}


def FinishInitPop : Opcode;
def FinishInit : Opcode;

Expand Down Expand Up @@ -681,6 +665,7 @@ def CastPointerIntegralAPS : Opcode {
let HasGroup = 0;
let Args = [ArgUint32];
}
def VoidPtrCast : Opcode;

def DecayPtr : Opcode {
let Types = [PtrTypeClass, PtrTypeClass];
Expand Down Expand Up @@ -751,6 +736,14 @@ def CheckNonNullArg : Opcode {

def Memcpy : Opcode;

def ToMemberPtr : Opcode;
def CastMemberPtrPtr : Opcode;
def GetMemberPtr : Opcode {
let Args = [ArgDecl];
}
def GetMemberPtrBase : Opcode;
def GetMemberPtrDecl : Opcode;

//===----------------------------------------------------------------------===//
// Debugging.
//===----------------------------------------------------------------------===//
Expand Down
47 changes: 25 additions & 22 deletions clang/lib/AST/Interp/Pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "Function.h"
#include "Integral.h"
#include "InterpBlock.h"
#include "MemberPointer.h"
#include "PrimType.h"
#include "Record.h"

Expand Down Expand Up @@ -63,53 +64,55 @@ Pointer::~Pointer() {
}

void Pointer::operator=(const Pointer &P) {
if (!this->isIntegralPointer() || !P.isBlockPointer())
assert(P.StorageKind == StorageKind || (this->isZero() && P.isZero()));

// If the current storage type is Block, we need to remove
// this pointer from the block.
bool WasBlockPointer = isBlockPointer();
StorageKind = P.StorageKind;
if (StorageKind == Storage::Block) {
Block *Old = PointeeStorage.BS.Pointee;
if (WasBlockPointer && PointeeStorage.BS.Pointee)
if (WasBlockPointer && Old) {
PointeeStorage.BS.Pointee->removePointer(this);
Old->cleanup();
}
}

Offset = P.Offset;
StorageKind = P.StorageKind;
Offset = P.Offset;

if (P.isBlockPointer()) {
PointeeStorage.BS = P.PointeeStorage.BS;
PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee;

if (PointeeStorage.BS.Pointee)
PointeeStorage.BS.Pointee->addPointer(this);

if (WasBlockPointer && Old)
Old->cleanup();

} else if (StorageKind == Storage::Int) {
} else if (P.isIntegralPointer()) {
PointeeStorage.Int = P.PointeeStorage.Int;
} else {
assert(false && "Unhandled storage kind");
}
}

void Pointer::operator=(Pointer &&P) {
if (!this->isIntegralPointer() || !P.isBlockPointer())
assert(P.StorageKind == StorageKind || (this->isZero() && P.isZero()));

// If the current storage type is Block, we need to remove
// this pointer from the block.
bool WasBlockPointer = isBlockPointer();
StorageKind = P.StorageKind;
if (StorageKind == Storage::Block) {
Block *Old = PointeeStorage.BS.Pointee;
if (WasBlockPointer && PointeeStorage.BS.Pointee)
if (WasBlockPointer && Old) {
PointeeStorage.BS.Pointee->removePointer(this);
Old->cleanup();
}
}

Offset = P.Offset;
StorageKind = P.StorageKind;
Offset = P.Offset;

if (P.isBlockPointer()) {
PointeeStorage.BS = P.PointeeStorage.BS;
PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee;

if (PointeeStorage.BS.Pointee)
PointeeStorage.BS.Pointee->addPointer(this);

if (WasBlockPointer && Old)
Old->cleanup();

} else if (StorageKind == Storage::Int) {
} else if (P.isIntegralPointer()) {
PointeeStorage.Int = P.PointeeStorage.Int;
} else {
assert(false && "Unhandled storage kind");
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/Pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,7 @@ class Pointer {
private:
friend class Block;
friend class DeadBlock;
friend class MemberPointer;
friend struct InitMap;

Pointer(Block *Pointee, unsigned Base, uint64_t Offset);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/PrimType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "Floating.h"
#include "FunctionPointer.h"
#include "IntegralAP.h"
#include "MemberPointer.h"
#include "Pointer.h"

using namespace clang;
Expand Down
8 changes: 7 additions & 1 deletion clang/lib/AST/Interp/PrimType.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class Pointer;
class Boolean;
class Floating;
class FunctionPointer;
class MemberPointer;
template <bool Signed> class IntegralAP;
template <unsigned Bits, bool Signed> class Integral;

Expand All @@ -44,10 +45,11 @@ enum PrimType : unsigned {
PT_Float = 11,
PT_Ptr = 12,
PT_FnPtr = 13,
PT_MemberPtr = 14,
};

inline constexpr bool isPtrType(PrimType T) {
return T == PT_Ptr || T == PT_FnPtr;
return T == PT_Ptr || T == PT_FnPtr || T == PT_MemberPtr;
}

enum class CastKind : uint8_t {
Expand Down Expand Up @@ -91,6 +93,9 @@ template <> struct PrimConv<PT_Ptr> { using T = Pointer; };
template <> struct PrimConv<PT_FnPtr> {
using T = FunctionPointer;
};
template <> struct PrimConv<PT_MemberPtr> {
using T = MemberPointer;
};

/// Returns the size of a primitive type in bytes.
size_t primSize(PrimType Type);
Expand Down Expand Up @@ -131,6 +136,7 @@ static inline bool aligned(const void *P) {
TYPE_SWITCH_CASE(PT_Bool, B) \
TYPE_SWITCH_CASE(PT_Ptr, B) \
TYPE_SWITCH_CASE(PT_FnPtr, B) \
TYPE_SWITCH_CASE(PT_MemberPtr, B) \
} \
} while (0)

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/OpenACCClause.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ OpenACCClause::child_range OpenACCClause::children() {
#define VISIT_CLAUSE(CLAUSE_NAME) \
case OpenACCClauseKind::CLAUSE_NAME: \
return cast<OpenACC##CLAUSE_NAME##Clause>(this)->children();
#define CLAUSE_ALIAS(ALIAS_NAME, CLAUSE_NAME) \
#define CLAUSE_ALIAS(ALIAS_NAME, CLAUSE_NAME, DEPRECATED) \
case OpenACCClauseKind::ALIAS_NAME: \
return cast<OpenACC##CLAUSE_NAME##Clause>(this)->children();

Expand Down
16 changes: 0 additions & 16 deletions clang/lib/AST/ParentMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,22 +97,6 @@ static void BuildParentMap(MapTy& M, Stmt* S,
BuildParentMap(M, SubStmt, OVMode);
}
break;
case Stmt::CXXDefaultArgExprClass:
if (auto *Arg = dyn_cast<CXXDefaultArgExpr>(S)) {
if (Arg->hasRewrittenInit()) {
M[Arg->getExpr()] = S;
BuildParentMap(M, Arg->getExpr(), OVMode);
}
}
break;
case Stmt::CXXDefaultInitExprClass:
if (auto *Init = dyn_cast<CXXDefaultInitExpr>(S)) {
if (Init->hasRewrittenInit()) {
M[Init->getExpr()] = S;
BuildParentMap(M, Init->getExpr(), OVMode);
}
}
break;
default:
for (Stmt *SubStmt : S->children()) {
if (SubStmt) {
Expand Down
21 changes: 13 additions & 8 deletions clang/lib/AST/TextNodeDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,9 @@ void TextNodeDumper::dumpTemplateArgument(const TemplateArgument &TA) {
}
OS << " '" << Str << "'";

if (!Context)
return;

if (TemplateArgument CanonTA = Context->getCanonicalTemplateArgument(TA);
!CanonTA.structurallyEquals(TA)) {
llvm::SmallString<128> CanonStr;
Expand Down Expand Up @@ -1139,15 +1142,17 @@ void TextNodeDumper::dumpTemplateName(TemplateName TN, StringRef Label) {
}
OS << " '" << Str << "'";

if (TemplateName CanonTN = Context->getCanonicalTemplateName(TN);
CanonTN != TN) {
llvm::SmallString<128> CanonStr;
{
llvm::raw_svector_ostream SS(CanonStr);
CanonTN.print(SS, PrintPolicy);
if (Context) {
if (TemplateName CanonTN = Context->getCanonicalTemplateName(TN);
CanonTN != TN) {
llvm::SmallString<128> CanonStr;
{
llvm::raw_svector_ostream SS(CanonStr);
CanonTN.print(SS, PrintPolicy);
}
if (CanonStr != Str)
OS << ":'" << CanonStr << "'";
}
if (CanonStr != Str)
OS << ":'" << CanonStr << "'";
}
}
dumpBareTemplateName(TN);
Expand Down
38 changes: 37 additions & 1 deletion clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2749,6 +2749,43 @@ bool QualType::isTriviallyCopyableType(const ASTContext &Context) const {
/*IsCopyConstructible=*/false);
}

// FIXME: each call will trigger a full computation, cache the result.
bool QualType::isBitwiseCloneableType(const ASTContext &Context) const {
auto CanonicalType = getCanonicalType();
if (CanonicalType.hasNonTrivialObjCLifetime())
return false;
if (CanonicalType->isArrayType())
return Context.getBaseElementType(CanonicalType)
.isBitwiseCloneableType(Context);

if (CanonicalType->isIncompleteType())
return false;
const auto *RD = CanonicalType->getAsRecordDecl(); // struct/union/class
if (!RD)
return true;

// Never allow memcpy when we're adding poisoned padding bits to the struct.
// Accessing these posioned bits will trigger false alarms on
// SanitizeAddressFieldPadding etc.
if (RD->mayInsertExtraPadding())
return false;

for (auto *const Field : RD->fields()) {
if (!Field->getType().isBitwiseCloneableType(Context))
return false;
}

if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
for (auto Base : CXXRD->bases())
if (!Base.getType().isBitwiseCloneableType(Context))
return false;
for (auto VBase : CXXRD->vbases())
if (!VBase.getType().isBitwiseCloneableType(Context))
return false;
}
return true;
}

bool QualType::isTriviallyCopyConstructibleType(
const ASTContext &Context) const {
return isTriviallyCopyableTypeImpl(*this, Context,
Expand Down Expand Up @@ -4444,7 +4481,6 @@ static CachedProperties computeCachedProperties(const Type *T) {
#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class,Base) case Type::Class:
#include "clang/AST/TypeNodes.inc"
// Treat instantiation-dependent types as external.
if (!T->isInstantiationDependentType()) T->dump();
assert(T->isInstantiationDependentType());
return CachedProperties(Linkage::External, false);

Expand Down
50 changes: 9 additions & 41 deletions clang/lib/Analysis/CFG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -556,10 +556,6 @@ class CFGBuilder {

private:
// Visitors to walk an AST and construct the CFG.
CFGBlock *VisitCXXDefaultArgExpr(CXXDefaultArgExpr *Default,
AddStmtChoice asc);
CFGBlock *VisitCXXDefaultInitExpr(CXXDefaultInitExpr *Default,
AddStmtChoice asc);
CFGBlock *VisitInitListExpr(InitListExpr *ILE, AddStmtChoice asc);
CFGBlock *VisitAddrLabelExpr(AddrLabelExpr *A, AddStmtChoice asc);
CFGBlock *VisitAttributedStmt(AttributedStmt *A, AddStmtChoice asc);
Expand Down Expand Up @@ -2258,10 +2254,16 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc,
asc, ExternallyDestructed);

case Stmt::CXXDefaultArgExprClass:
return VisitCXXDefaultArgExpr(cast<CXXDefaultArgExpr>(S), asc);

case Stmt::CXXDefaultInitExprClass:
return VisitCXXDefaultInitExpr(cast<CXXDefaultInitExpr>(S), asc);
// FIXME: The expression inside a CXXDefaultArgExpr is owned by the
// called function's declaration, not by the caller. If we simply add
// this expression to the CFG, we could end up with the same Expr
// appearing multiple times (PR13385).
//
// It's likewise possible for multiple CXXDefaultInitExprs for the same
// expression to be used in the same function (through aggregate
// initialization).
return VisitStmt(S, asc);

case Stmt::CXXBindTemporaryExprClass:
return VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), asc);
Expand Down Expand Up @@ -2431,40 +2433,6 @@ CFGBlock *CFGBuilder::VisitChildren(Stmt *S) {
return B;
}

CFGBlock *CFGBuilder::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *Arg,
AddStmtChoice asc) {
if (Arg->hasRewrittenInit()) {
if (asc.alwaysAdd(*this, Arg)) {
autoCreateBlock();
appendStmt(Block, Arg);
}
return VisitStmt(Arg->getExpr(), asc);
}

// We can't add the default argument if it's not rewritten because the
// expression inside a CXXDefaultArgExpr is owned by the called function's
// declaration, not by the caller, we could end up with the same expression
// appearing multiple times.
return VisitStmt(Arg, asc);
}

CFGBlock *CFGBuilder::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *Init,
AddStmtChoice asc) {
if (Init->hasRewrittenInit()) {
if (asc.alwaysAdd(*this, Init)) {
autoCreateBlock();
appendStmt(Block, Init);
}
return VisitStmt(Init->getExpr(), asc);
}

// We can't add the default initializer if it's not rewritten because multiple
// CXXDefaultInitExprs for the same sub-expression to be used in the same
// function (through aggregate initialization). we could end up with the same
// expression appearing multiple times.
return VisitStmt(Init, asc);
}

CFGBlock *CFGBuilder::VisitInitListExpr(InitListExpr *ILE, AddStmtChoice asc) {
if (asc.alwaysAdd(*this, ILE)) {
autoCreateBlock();
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Basic/Cuda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ static const CudaArchToStringMap arch_names[] = {
GFX(1103), // gfx1103
GFX(1150), // gfx1150
GFX(1151), // gfx1151
GFX(1152), // gfx1152
{CudaArch::GFX12_GENERIC, "gfx12-generic", "compute_amdgcn"},
GFX(1200), // gfx1200
GFX(1201), // gfx1201
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/Basic/Targets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -673,8 +673,11 @@ std::unique_ptr<TargetInfo> AllocateTarget(const llvm::Triple &Triple,
}
case llvm::Triple::spirv64: {
if (os != llvm::Triple::UnknownOS ||
Triple.getEnvironment() != llvm::Triple::UnknownEnvironment)
Triple.getEnvironment() != llvm::Triple::UnknownEnvironment) {
if (os == llvm::Triple::OSType::AMDHSA)
return std::make_unique<SPIRV64AMDGCNTargetInfo>(Triple, Opts);
return nullptr;
}
return std::make_unique<SPIRV64TargetInfo>(Triple, Opts);
}
case llvm::Triple::wasm32:
Expand Down
105 changes: 33 additions & 72 deletions clang/lib/Basic/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1050,57 +1050,18 @@ bool AArch64TargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
return true;
}

bool AArch64TargetInfo::initFeatureMap(
llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags, StringRef CPU,
const std::vector<std::string> &FeaturesVec) const {
std::vector<std::string> UpdatedFeaturesVec;
// Parse the CPU and add any implied features.
std::optional<llvm::AArch64::CpuInfo> CpuInfo = llvm::AArch64::parseCpu(CPU);
if (CpuInfo) {
auto Exts = CpuInfo->getImpliedExtensions();
std::vector<StringRef> CPUFeats;
llvm::AArch64::getExtensionFeatures(Exts, CPUFeats);
for (auto F : CPUFeats) {
assert((F[0] == '+' || F[0] == '-') && "Expected +/- in target feature!");
UpdatedFeaturesVec.push_back(F.str());
}
}

// Process target and dependent features. This is done in two loops collecting
// them into UpdatedFeaturesVec: first to add dependent '+'features, second to
// add target '+/-'features that can later disable some of features added on
// the first loop. Function Multi Versioning features begin with '?'.
for (const auto &Feature : FeaturesVec)
if (((Feature[0] == '?' || Feature[0] == '+')) &&
AArch64TargetInfo::doesFeatureAffectCodeGen(Feature.substr(1))) {
StringRef DepFeatures =
AArch64TargetInfo::getFeatureDependencies(Feature.substr(1));
SmallVector<StringRef, 1> AttrFeatures;
DepFeatures.split(AttrFeatures, ",");
for (auto F : AttrFeatures)
UpdatedFeaturesVec.push_back(F.str());
}
for (const auto &Feature : FeaturesVec)
if (Feature[0] != '?') {
std::string UpdatedFeature = Feature;
if (Feature[0] == '+') {
std::optional<llvm::AArch64::ExtensionInfo> Extension =
llvm::AArch64::parseArchExtension(Feature.substr(1));
if (Extension)
UpdatedFeature = Extension->Feature.str();
}
UpdatedFeaturesVec.push_back(UpdatedFeature);
}

return TargetInfo::initFeatureMap(Features, Diags, CPU, UpdatedFeaturesVec);
}

// Parse AArch64 Target attributes, which are a comma separated list of:
// "arch=<arch>" - parsed to features as per -march=..
// "cpu=<cpu>" - parsed to features as per -mcpu=.., with CPU set to <cpu>
// "tune=<cpu>" - TuneCPU set to <cpu>
// "feature", "no-feature" - Add (or remove) feature.
// "+feature", "+nofeature" - Add (or remove) feature.
//
// A feature may correspond to an Extension (anything with a corresponding
// AEK_), in which case an ExtensionSet is used to parse it and expand its
// dependencies. Otherwise the feature is passed through (e.g. +v8.1a,
// +outline-atomics, -fmv, etc). Features coming from the command line are
// already parsed, therefore their dependencies do not need expansion.
ParsedTargetAttr AArch64TargetInfo::parseTargetAttr(StringRef Features) const {
ParsedTargetAttr Ret;
if (Features == "default")
Expand All @@ -1110,23 +1071,26 @@ ParsedTargetAttr AArch64TargetInfo::parseTargetAttr(StringRef Features) const {
bool FoundArch = false;

auto SplitAndAddFeatures = [](StringRef FeatString,
std::vector<std::string> &Features) {
std::vector<std::string> &Features,
llvm::AArch64::ExtensionSet &FeatureBits) {
SmallVector<StringRef, 8> SplitFeatures;
FeatString.split(SplitFeatures, StringRef("+"), -1, false);
for (StringRef Feature : SplitFeatures) {
StringRef FeatureName = llvm::AArch64::getArchExtFeature(Feature);
if (!FeatureName.empty())
Features.push_back(FeatureName.str());
if (FeatureBits.parseModifier(Feature, /* AllowNoDashForm = */ true))
continue;
// Pass through features that are not extensions, e.g. +v8.1a,
// +outline-atomics, -fmv, etc.
if (Feature.starts_with("no"))
Features.push_back("-" + Feature.drop_front(2).str());
else
// Pushing the original feature string to give a sema error later on
// when they get checked.
if (Feature.starts_with("no"))
Features.push_back("-" + Feature.drop_front(2).str());
else
Features.push_back("+" + Feature.str());
Features.push_back("+" + Feature.str());
}
};

llvm::AArch64::ExtensionSet FeatureBits;
// Reconstruct the bitset from the command line option features.
FeatureBits.reconstructFromParsedFeatures(getTargetOpts().FeaturesAsWritten);

for (auto &Feature : AttrFeatures) {
Feature = Feature.trim();
if (Feature.starts_with("fpmath="))
Expand All @@ -1149,9 +1113,9 @@ ParsedTargetAttr AArch64TargetInfo::parseTargetAttr(StringRef Features) const {
// Ret.Features.
if (!AI)
continue;
Ret.Features.push_back(AI->ArchFeature.str());
FeatureBits.addArchDefaults(*AI);
// Add any extra features, after the +
SplitAndAddFeatures(Split.second, Ret.Features);
SplitAndAddFeatures(Split.second, Ret.Features, FeatureBits);
} else if (Feature.starts_with("cpu=")) {
if (!Ret.CPU.empty())
Ret.Duplicate = "cpu=";
Expand All @@ -1161,33 +1125,30 @@ ParsedTargetAttr AArch64TargetInfo::parseTargetAttr(StringRef Features) const {
std::pair<StringRef, StringRef> Split =
Feature.split("=").second.trim().split("+");
Ret.CPU = Split.first;
SplitAndAddFeatures(Split.second, Ret.Features);
if (auto CpuInfo = llvm::AArch64::parseCpu(Ret.CPU)) {
FeatureBits.addCPUDefaults(*CpuInfo);
SplitAndAddFeatures(Split.second, Ret.Features, FeatureBits);
}
}
} else if (Feature.starts_with("tune=")) {
if (!Ret.Tune.empty())
Ret.Duplicate = "tune=";
else
Ret.Tune = Feature.split("=").second.trim();
} else if (Feature.starts_with("+")) {
SplitAndAddFeatures(Feature, Ret.Features);
} else if (Feature.starts_with("no-")) {
StringRef FeatureName =
llvm::AArch64::getArchExtFeature(Feature.split("-").second);
if (!FeatureName.empty())
Ret.Features.push_back("-" + FeatureName.drop_front(1).str());
else
Ret.Features.push_back("-" + Feature.split("-").second.str());
SplitAndAddFeatures(Feature, Ret.Features, FeatureBits);
} else {
// Try parsing the string to the internal target feature name. If it is
// invalid, add the original string (which could already be an internal
// name). These should be checked later by isValidFeatureName.
StringRef FeatureName = llvm::AArch64::getArchExtFeature(Feature);
if (!FeatureName.empty())
Ret.Features.push_back(FeatureName.str());
if (FeatureBits.parseModifier(Feature, /* AllowNoDashForm = */ true))
continue;
// Pass through features that are not extensions, e.g. +v8.1a,
// +outline-atomics, -fmv, etc.
if (Feature.starts_with("no-"))
Ret.Features.push_back("-" + Feature.drop_front(3).str());
else
Ret.Features.push_back("+" + Feature.str());
}
}
FeatureBits.toLLVMFeatureList(Ret.Features);
return Ret;
}

Expand Down
4 changes: 0 additions & 4 deletions clang/lib/Basic/Targets/AArch64.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,6 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo {
unsigned multiVersionSortPriority(StringRef Name) const override;
unsigned multiVersionFeatureCost() const override;

bool
initFeatureMap(llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags,
StringRef CPU,
const std::vector<std::string> &FeaturesVec) const override;
bool useFP16ConversionIntrinsics() const override {
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Basic/Targets/LoongArch.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class LLVM_LIBRARY_VISIBILITY LoongArch64TargetInfo
LongWidth = LongAlign = PointerWidth = PointerAlign = 64;
IntMaxType = Int64Type = SignedLong;
HasUnalignedAccess = true;
resetDataLayout("e-m:e-p:64:64-i64:64-i128:128-n64-S128");
resetDataLayout("e-m:e-p:64:64-i64:64-i128:128-n32:64-S128");
// TODO: select appropriate ABI.
setABI("lp64d");
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Basic/Targets/NVPTX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ void NVPTXTargetInfo::getTargetDefines(const LangOptions &Opts,
case CudaArch::GFX1103:
case CudaArch::GFX1150:
case CudaArch::GFX1151:
case CudaArch::GFX1152:
case CudaArch::GFX12_GENERIC:
case CudaArch::GFX1200:
case CudaArch::GFX1201:
Expand Down
75 changes: 75 additions & 0 deletions clang/lib/Basic/Targets/SPIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
//===----------------------------------------------------------------------===//

#include "SPIR.h"
#include "AMDGPU.h"
#include "Targets.h"
#include "llvm/TargetParser/TargetParser.h"

using namespace clang;
using namespace clang::targets;
Expand Down Expand Up @@ -54,3 +56,76 @@ void SPIRV64TargetInfo::getTargetDefines(const LangOptions &Opts,
BaseSPIRVTargetInfo::getTargetDefines(Opts, Builder);
DefineStd(Builder, "SPIRV64", Opts);
}

static const AMDGPUTargetInfo AMDGPUTI(llvm::Triple("amdgcn-amd-amdhsa"), {});

ArrayRef<const char *> SPIRV64AMDGCNTargetInfo::getGCCRegNames() const {
return AMDGPUTI.getGCCRegNames();
}

bool SPIRV64AMDGCNTargetInfo::initFeatureMap(
llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags, StringRef,
const std::vector<std::string> &FeatureVec) const {
llvm::AMDGPU::fillAMDGPUFeatureMap({}, getTriple(), Features);

return TargetInfo::initFeatureMap(Features, Diags, {}, FeatureVec);
}

bool SPIRV64AMDGCNTargetInfo::validateAsmConstraint(
const char *&Name, TargetInfo::ConstraintInfo &Info) const {
return AMDGPUTI.validateAsmConstraint(Name, Info);
}

std::string
SPIRV64AMDGCNTargetInfo::convertConstraint(const char *&Constraint) const {
return AMDGPUTI.convertConstraint(Constraint);
}

ArrayRef<Builtin::Info> SPIRV64AMDGCNTargetInfo::getTargetBuiltins() const {
return AMDGPUTI.getTargetBuiltins();
}

void SPIRV64AMDGCNTargetInfo::getTargetDefines(const LangOptions &Opts,
MacroBuilder &Builder) const {
BaseSPIRVTargetInfo::getTargetDefines(Opts, Builder);
DefineStd(Builder, "SPIRV64", Opts);

Builder.defineMacro("__AMD__");
Builder.defineMacro("__AMDGPU__");
Builder.defineMacro("__AMDGCN__");
}

void SPIRV64AMDGCNTargetInfo::setAuxTarget(const TargetInfo *Aux) {
assert(Aux && "Cannot invoke setAuxTarget without a valid auxiliary target!");

// This is a 1:1 copy of AMDGPUTargetInfo::setAuxTarget()
assert(HalfFormat == Aux->HalfFormat);
assert(FloatFormat == Aux->FloatFormat);
assert(DoubleFormat == Aux->DoubleFormat);

// On x86_64 long double is 80-bit extended precision format, which is
// not supported by AMDGPU. 128-bit floating point format is also not
// supported by AMDGPU. Therefore keep its own format for these two types.
auto SaveLongDoubleFormat = LongDoubleFormat;
auto SaveFloat128Format = Float128Format;
auto SaveLongDoubleWidth = LongDoubleWidth;
auto SaveLongDoubleAlign = LongDoubleAlign;
copyAuxTarget(Aux);
LongDoubleFormat = SaveLongDoubleFormat;
Float128Format = SaveFloat128Format;
LongDoubleWidth = SaveLongDoubleWidth;
LongDoubleAlign = SaveLongDoubleAlign;
// For certain builtin types support on the host target, claim they are
// supported to pass the compilation of the host code during the device-side
// compilation.
// FIXME: As the side effect, we also accept `__float128` uses in the device
// code. To reject these builtin types supported in the host target but not in
// the device target, one approach would support `device_builtin` attribute
// so that we could tell the device builtin types from the host ones. This
// also solves the different representations of the same builtin type, such
// as `size_t` in the MSVC environment.
if (Aux->hasFloat128Type()) {
HasFloat128 = true;
Float128Format = DoubleFormat;
}
}
51 changes: 51 additions & 0 deletions clang/lib/Basic/Targets/SPIR.h
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,57 @@ class LLVM_LIBRARY_VISIBILITY SPIRV64TargetInfo : public BaseSPIRVTargetInfo {
MacroBuilder &Builder) const override;
};

class LLVM_LIBRARY_VISIBILITY SPIRV64AMDGCNTargetInfo final
: public BaseSPIRVTargetInfo {
public:
SPIRV64AMDGCNTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts)
: BaseSPIRVTargetInfo(Triple, Opts) {
assert(Triple.getArch() == llvm::Triple::spirv64 &&
"Invalid architecture for 64-bit AMDGCN SPIR-V.");
assert(Triple.getVendor() == llvm::Triple::VendorType::AMD &&
"64-bit AMDGCN SPIR-V target must use AMD vendor");
assert(getTriple().getOS() == llvm::Triple::OSType::AMDHSA &&
"64-bit AMDGCN SPIR-V target must use AMDHSA OS");
assert(getTriple().getEnvironment() == llvm::Triple::UnknownEnvironment &&
"64-bit SPIR-V target must use unknown environment type");
PointerWidth = PointerAlign = 64;
SizeType = TargetInfo::UnsignedLong;
PtrDiffType = IntPtrType = TargetInfo::SignedLong;

resetDataLayout("e-i64:64-v16:16-v24:32-v32:32-v48:64-"
"v96:128-v192:256-v256:256-v512:512-v1024:1024-G1-P4-A0");

BFloat16Width = BFloat16Align = 16;
BFloat16Format = &llvm::APFloat::BFloat();

HasLegalHalfType = true;
HasFloat16 = true;
HalfArgsAndReturns = true;
}

bool hasBFloat16Type() const override { return true; }

ArrayRef<const char *> getGCCRegNames() const override;

bool initFeatureMap(llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags,
StringRef,
const std::vector<std::string> &) const override;

bool validateAsmConstraint(const char *&Name,
TargetInfo::ConstraintInfo &Info) const override;

std::string convertConstraint(const char *&Constraint) const override;

ArrayRef<Builtin::Info> getTargetBuiltins() const override;

void getTargetDefines(const LangOptions &Opts,
MacroBuilder &Builder) const override;

void setAuxTarget(const TargetInfo *Aux) override;

bool hasInt128Type() const override { return TargetInfo::hasInt128Type(); }
};

} // namespace targets
} // namespace clang
#endif // LLVM_CLANG_LIB_BASIC_TARGETS_SPIR_H
7 changes: 7 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6012,6 +6012,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
llvm::Triple::getArchTypePrefix(getTarget().getTriple().getArch());
if (!Prefix.empty()) {
IntrinsicID = Intrinsic::getIntrinsicForClangBuiltin(Prefix.data(), Name);
if (IntrinsicID == Intrinsic::not_intrinsic && Prefix == "spv" &&
getTarget().getTriple().getOS() == llvm::Triple::OSType::AMDHSA)
IntrinsicID = Intrinsic::getIntrinsicForClangBuiltin("amdgcn", Name);
// NOTE we don't need to perform a compatibility flag check here since the
// intrinsics are declared in Builtins*.def via LANGBUILTIN which filter the
// MS builtins via ALL_MS_LANGUAGES and are filtered earlier.
Expand Down Expand Up @@ -6182,6 +6185,10 @@ static Value *EmitTargetArchBuiltinExpr(CodeGenFunction *CGF,
case llvm::Triple::riscv32:
case llvm::Triple::riscv64:
return CGF->EmitRISCVBuiltinExpr(BuiltinID, E, ReturnValue);
case llvm::Triple::spirv64:
if (CGF->getTarget().getTriple().getOS() != llvm::Triple::OSType::AMDHSA)
return nullptr;
return CGF->EmitAMDGPUBuiltinExpr(BuiltinID, E);
default:
return nullptr;
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1917,6 +1917,7 @@ static void getTrivialDefaultFunctionAttributes(
case CodeGenOptions::FramePointerKind::None:
// This is the default behavior.
break;
case CodeGenOptions::FramePointerKind::Reserved:
case CodeGenOptions::FramePointerKind::NonLeaf:
case CodeGenOptions::FramePointerKind::All:
FuncAttrs.addAttribute("frame-pointer",
Expand Down
43 changes: 20 additions & 23 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,16 @@ using namespace clang::CodeGen;

static uint32_t getTypeAlignIfRequired(const Type *Ty, const ASTContext &Ctx) {
auto TI = Ctx.getTypeInfo(Ty);
return TI.isAlignRequired() ? TI.Align : 0;
if (TI.isAlignRequired())
return TI.Align;

// MaxFieldAlignmentAttr is the attribute added to types
// declared after #pragma pack(n).
if (auto *Decl = Ty->getAsRecordDecl())
if (Decl->hasAttr<MaxFieldAlignmentAttr>())
return TI.Align;

return 0;
}

static uint32_t getTypeAlignIfRequired(QualType Ty, const ASTContext &Ctx) {
Expand Down Expand Up @@ -5757,28 +5766,16 @@ void CGDebugInfo::EmitPseudoVariable(CGBuilderTy &Builder,
// it is loaded upon use, so we identify such pattern here.
if (llvm::LoadInst *Load = dyn_cast<llvm::LoadInst>(Value)) {
llvm::Value *Var = Load->getPointerOperand();
if (llvm::Metadata *MDValue = llvm::ValueAsMetadata::getIfExists(Var)) {
if (llvm::Value *DbgValue = llvm::MetadataAsValue::getIfExists(
CGM.getLLVMContext(), MDValue)) {
for (llvm::User *U : DbgValue->users()) {
if (llvm::CallInst *DbgDeclare = dyn_cast<llvm::CallInst>(U)) {
if (DbgDeclare->getCalledFunction()->getIntrinsicID() ==
llvm::Intrinsic::dbg_declare &&
DbgDeclare->getArgOperand(0) == DbgValue) {
// There can be implicit type cast applied on a variable if it is
// an opaque ptr, in this case its debug info may not match the
// actual type of object being used as in the next instruction, so
// we will need to emit a pseudo variable for type-casted value.
llvm::DILocalVariable *MDNode = cast<llvm::DILocalVariable>(
cast<llvm::MetadataAsValue>(DbgDeclare->getOperand(1))
->getMetadata());
if (MDNode->getType() == Type)
return;
}
}
}
}
}
// There can be implicit type cast applied on a variable if it is an opaque
// ptr, in this case its debug info may not match the actual type of object
// being used as in the next instruction, so we will need to emit a pseudo
// variable for type-casted value.
auto DeclareTypeMatches = [&](auto *DbgDeclare) {
return DbgDeclare->getVariable()->getType() == Type;
};
if (any_of(llvm::findDbgDeclares(Var), DeclareTypeMatches) ||
any_of(llvm::findDVRDeclares(Var), DeclareTypeMatches))
return;
}

// Find the correct location to insert a sequence of instructions to
Expand Down
29 changes: 9 additions & 20 deletions clang/lib/CodeGen/CGExprAgg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -513,15 +513,6 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType,

QualType elementType =
CGF.getContext().getAsArrayType(ArrayQTy)->getElementType();

// DestPtr is an array*. Construct an elementType* by drilling
// down a level.
llvm::Value *zero = llvm::ConstantInt::get(CGF.SizeTy, 0);
llvm::Value *indices[] = { zero, zero };
llvm::Value *begin = Builder.CreateInBoundsGEP(DestPtr.getElementType(),
DestPtr.emitRawPointer(CGF),
indices, "arrayinit.begin");

CharUnits elementSize = CGF.getContext().getTypeSizeInChars(elementType);
CharUnits elementAlign =
DestPtr.getAlignment().alignmentOfArrayElement(elementSize);
Expand Down Expand Up @@ -562,6 +553,7 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType,
Address endOfInit = Address::invalid();
CodeGenFunction::CleanupDeactivationScope deactivation(CGF);

llvm::Value *begin = DestPtr.emitRawPointer(CGF);
if (dtorKind) {
CodeGenFunction::AllocaTrackerRAII allocaTracker(CGF);
// In principle we could tell the cleanup where we are more
Expand All @@ -585,19 +577,13 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType,

llvm::Value *one = llvm::ConstantInt::get(CGF.SizeTy, 1);

// The 'current element to initialize'. The invariants on this
// variable are complicated. Essentially, after each iteration of
// the loop, it points to the last initialized element, except
// that it points to the beginning of the array before any
// elements have been initialized.
llvm::Value *element = begin;

// Emit the explicit initializers.
for (uint64_t i = 0; i != NumInitElements; ++i) {
// Advance to the next element.
llvm::Value *element = begin;
if (i > 0) {
element = Builder.CreateInBoundsGEP(
llvmElementType, element, one, "arrayinit.element");
element = Builder.CreateInBoundsGEP(llvmElementType, begin,
llvm::ConstantInt::get(CGF.SizeTy, i),
"arrayinit.element");

// Tell the cleanup that it needs to destroy up to this
// element. TODO: some of these stores can be trivially
Expand All @@ -624,9 +610,12 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType,
// do { *array++ = filler; } while (array != end);

// Advance to the start of the rest of the array.
llvm::Value *element = begin;
if (NumInitElements) {
element = Builder.CreateInBoundsGEP(
llvmElementType, element, one, "arrayinit.start");
llvmElementType, element,
llvm::ConstantInt::get(CGF.SizeTy, NumInitElements),
"arrayinit.start");
if (endOfInit.isValid()) Builder.CreateStore(element, endOfInit);
}

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/CGHLSLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ void clang::CodeGen::CGHLSLRuntime::setHLSLEntryAttributes(
assert(ShaderAttr && "All entry functions must have a HLSLShaderAttr");
const StringRef ShaderAttrKindStr = "hlsl.shader";
Fn->addFnAttr(ShaderAttrKindStr,
ShaderAttr->ConvertShaderTypeToStr(ShaderAttr->getType()));
llvm::Triple::getEnvironmentTypeName(ShaderAttr->getType()));
if (HLSLNumThreadsAttr *NumThreadsAttr = FD->getAttr<HLSLNumThreadsAttr>()) {
const StringRef NumThreadsKindStr = "hlsl.numthreads";
std::string NumThreadsStr =
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3537,6 +3537,7 @@ void CGOpenMPRuntimeGPU::processRequiresDirective(
case CudaArch::GFX1103:
case CudaArch::GFX1150:
case CudaArch::GFX1151:
case CudaArch::GFX1152:
case CudaArch::GFX12_GENERIC:
case CudaArch::GFX1200:
case CudaArch::GFX1201:
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CGStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,8 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) {
CGM.ErrorUnsupported(S, "OpenMP dispatch directive");
break;
case Stmt::OMPScopeDirectiveClass:
llvm_unreachable("scope not supported with FE outlining");
CGM.ErrorUnsupported(S, "scope with FE outlining");
break;
case Stmt::OMPMaskedDirectiveClass:
EmitOMPMaskedDirective(cast<OMPMaskedDirective>(*S));
break;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1328,6 +1328,9 @@ void CodeGenModule::Release() {
case CodeGenOptions::FramePointerKind::None:
// 0 ("none") is the default.
break;
case CodeGenOptions::FramePointerKind::Reserved:
getModule().setFramePointer(llvm::FramePointerKind::Reserved);
break;
case CodeGenOptions::FramePointerKind::NonLeaf:
getModule().setFramePointer(llvm::FramePointerKind::NonLeaf);
break;
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/CodeGen/Targets/AMDGPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,11 @@ void AMDGPUABIInfo::computeInfo(CGFunctionInfo &FI) const {

Address AMDGPUABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
QualType Ty) const {
llvm_unreachable("AMDGPU does not support varargs");
const bool IsIndirect = false;
const bool AllowHigherAlign = false;
return emitVoidPtrVAArg(CGF, VAListAddr, Ty, IsIndirect,
getContext().getTypeInfoInChars(Ty),
CharUnits::fromQuantity(4), AllowHigherAlign);
}

ABIArgInfo AMDGPUABIInfo::classifyReturnType(QualType RetTy) const {
Expand Down
8 changes: 4 additions & 4 deletions clang/lib/Driver/ToolChain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ ToolChain::ToolChain(const Driver &D, const llvm::Triple &T,
}

llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
ToolChain::executeToolChainProgram(StringRef Executable) const {
ToolChain::executeToolChainProgram(StringRef Executable,
unsigned SecondsToWait) const {
llvm::SmallString<64> OutputFile;
llvm::sys::fs::createTemporaryFile("toolchain-program", "txt", OutputFile);
llvm::FileRemover OutputRemover(OutputFile.c_str());
Expand All @@ -115,9 +116,8 @@ ToolChain::executeToolChainProgram(StringRef Executable) const {
};

std::string ErrorMessage;
if (llvm::sys::ExecuteAndWait(Executable, {}, {}, Redirects,
/* SecondsToWait */ 0,
/*MemoryLimit*/ 0, &ErrorMessage))
if (llvm::sys::ExecuteAndWait(Executable, {}, {}, Redirects, SecondsToWait,
/*MemoryLimit=*/0, &ErrorMessage))
return llvm::createStringError(std::error_code(),
Executable + ": " + ErrorMessage);

Expand Down
8 changes: 6 additions & 2 deletions clang/lib/Driver/ToolChains/AMDGPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,11 @@ void amdgpu::getAMDGPUTargetFeatures(const Driver &D,
std::vector<StringRef> &Features) {
// Add target ID features to -target-feature options. No diagnostics should
// be emitted here since invalid target ID is diagnosed at other places.
StringRef TargetID = Args.getLastArgValue(options::OPT_mcpu_EQ);
StringRef TargetID;
if (Args.hasArg(options::OPT_mcpu_EQ))
TargetID = Args.getLastArgValue(options::OPT_mcpu_EQ);
else if (Args.hasArg(options::OPT_march_EQ))
TargetID = Args.getLastArgValue(options::OPT_march_EQ);
if (!TargetID.empty()) {
llvm::StringMap<bool> FeatureMap;
auto OptionalGpuArch = parseTargetID(Triple, TargetID, &FeatureMap);
Expand Down Expand Up @@ -877,7 +881,7 @@ AMDGPUToolChain::getSystemGPUArchs(const ArgList &Args) const {
else
Program = GetProgramPath("amdgpu-arch");

auto StdoutOrErr = executeToolChainProgram(Program);
auto StdoutOrErr = executeToolChainProgram(Program, /*SecondsToWait=*/10);
if (!StdoutOrErr)
return StdoutOrErr.takeError();

Expand Down
5 changes: 0 additions & 5 deletions clang/lib/Driver/ToolChains/AMDGPUOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,9 @@ void AMDGPUOpenMPToolChain::addClangTargetOptions(
Action::OffloadKind DeviceOffloadingKind) const {
HostTC.addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind);

StringRef GPUArch = DriverArgs.getLastArgValue(options::OPT_march_EQ);
assert(!GPUArch.empty() && "Must have an explicit GPU arch.");

assert(DeviceOffloadingKind == Action::OFK_OpenMP &&
"Only OpenMP offloading kinds are supported.");

CC1Args.push_back("-target-cpu");
CC1Args.push_back(DriverArgs.MakeArgStringRef(GPUArch));
CC1Args.push_back("-fcuda-is-device");

if (DriverArgs.hasArg(options::OPT_nogpulib))
Expand Down
2 changes: 0 additions & 2 deletions clang/lib/Driver/ToolChains/Arch/ARM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -799,8 +799,6 @@ llvm::ARM::FPUKind arm::getARMTargetFeatures(const Driver &D,
StringRef FrameChainOption = A->getValue();
if (FrameChainOption.starts_with("aapcs"))
Features.push_back("+aapcs-frame-chain");
if (FrameChainOption == "aapcs+leaf")
Features.push_back("+aapcs-frame-chain-leaf");
}

// CMSE: Check for target 8M (for -mcmse to be applicable) is performed later.
Expand Down
31 changes: 18 additions & 13 deletions clang/lib/Driver/ToolChains/Arch/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,6 @@ static void getRISCFeaturesFromMcpu(const Driver &D, const Arg *A,
D.Diag(clang::diag::err_drv_unsupported_option_argument)
<< A->getSpelling() << Mcpu;
}

if (llvm::RISCV::hasFastUnalignedAccess(Mcpu)) {
Features.push_back("+unaligned-scalar-mem");
Features.push_back("+unaligned-vector-mem");
}
}

void riscv::getRISCVTargetFeatures(const Driver &D, const llvm::Triple &Triple,
Expand All @@ -82,6 +77,8 @@ void riscv::getRISCVTargetFeatures(const Driver &D, const llvm::Triple &Triple,
if (!getArchFeatures(D, MArch, Features, Args))
return;

bool CPUFastUnaligned = false;

// If users give march and mcpu, get std extension feature from MArch
// and other features (ex. mirco architecture feature) from mcpu
if (Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) {
Expand All @@ -90,6 +87,9 @@ void riscv::getRISCVTargetFeatures(const Driver &D, const llvm::Triple &Triple,
CPU = llvm::sys::getHostCPUName();

getRISCFeaturesFromMcpu(D, A, Triple, CPU, Features);

if (llvm::RISCV::hasFastUnalignedAccess(CPU))
CPUFastUnaligned = true;
}

// Handle features corresponding to "-ffixed-X" options
Expand Down Expand Up @@ -169,18 +169,23 @@ void riscv::getRISCVTargetFeatures(const Driver &D, const llvm::Triple &Triple,
Features.push_back("-relax");
}

// Android requires fast unaligned access on RISCV64.
if (Triple.isAndroid()) {
// If -mstrict-align or -mno-strict-align is passed, use it. Otherwise, the
// unaligned-*-mem is enabled if the CPU supports it or the target is
// Android.
if (const Arg *A = Args.getLastArg(options::OPT_mno_strict_align,
options::OPT_mstrict_align)) {
if (A->getOption().matches(options::OPT_mno_strict_align)) {
Features.push_back("+unaligned-scalar-mem");
Features.push_back("+unaligned-vector-mem");
} else {
Features.push_back("-unaligned-scalar-mem");
Features.push_back("-unaligned-vector-mem");
}
} else if (CPUFastUnaligned || Triple.isAndroid()) {
Features.push_back("+unaligned-scalar-mem");
Features.push_back("+unaligned-vector-mem");
}

// -mstrict-align is default, unless -mno-strict-align is specified.
AddTargetFeature(Args, Features, options::OPT_mno_strict_align,
options::OPT_mstrict_align, "unaligned-scalar-mem");
AddTargetFeature(Args, Features, options::OPT_mno_strict_align,
options::OPT_mstrict_align, "unaligned-vector-mem");

// Now add any that the user explicitly requested on the command line,
// which may override the defaults.
handleTargetFeaturesGroup(D, Triple, Args, Features,
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/Driver/ToolChains/BareMetal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ void baremetal::Linker::ConstructJob(Compilation &C, const JobAction &JA,
ArgStringList CmdArgs;

auto &TC = static_cast<const toolchains::BareMetal &>(getToolChain());
const Driver &D = getToolChain().getDriver();
const llvm::Triple::ArchType Arch = TC.getArch();
const llvm::Triple &Triple = getToolChain().getEffectiveTriple();

Expand Down Expand Up @@ -466,6 +467,19 @@ void baremetal::Linker::ConstructJob(Compilation &C, const JobAction &JA,
TC.AddLinkRuntimeLib(Args, CmdArgs);
}

if (D.isUsingLTO()) {
assert(!Inputs.empty() && "Must have at least one input.");
// Find the first filename InputInfo object.
auto Input = llvm::find_if(
Inputs, [](const InputInfo &II) -> bool { return II.isFilename(); });
if (Input == Inputs.end())
// For a very rare case, all of the inputs to the linker are
// InputArg. If that happens, just use the first InputInfo.
Input = Inputs.begin();

addLTOOptions(TC, Args, CmdArgs, Output, *Input,
D.getLTOMode() == LTOK_Thin);
}
if (TC.getTriple().isRISCV())
CmdArgs.push_back("-X");

Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5678,6 +5678,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
case CodeGenOptions::FramePointerKind::None:
FPKeepKindStr = "-mframe-pointer=none";
break;
case CodeGenOptions::FramePointerKind::Reserved:
FPKeepKindStr = "-mframe-pointer=reserved";
break;
case CodeGenOptions::FramePointerKind::NonLeaf:
FPKeepKindStr = "-mframe-pointer=non-leaf";
break;
Expand Down
115 changes: 88 additions & 27 deletions clang/lib/Driver/ToolChains/CommonArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,14 @@ static bool useFramePointerForTargetByDefault(const llvm::opt::ArgList &Args,
return true;
}

static bool useLeafFramePointerForTargetByDefault(const llvm::Triple &Triple) {
if (Triple.isAArch64() || Triple.isPS() || Triple.isVE() ||
(Triple.isAndroid() && Triple.isRISCV64()))
return false;

return true;
}

static bool mustUseNonLeafFramePointerForTarget(const llvm::Triple &Triple) {
switch (Triple.getArch()) {
default:
Expand All @@ -176,38 +184,91 @@ static bool mustUseNonLeafFramePointerForTarget(const llvm::Triple &Triple) {
}
}

// True if a target-specific option requires the frame chain to be preserved,
// even if new frame records are not created.
static bool mustMaintainValidFrameChain(const llvm::opt::ArgList &Args,
const llvm::Triple &Triple) {
if (Triple.isARM() || Triple.isThumb()) {
// For 32-bit Arm, the -mframe-chain=aapcs and -mframe-chain=aapcs+leaf
// options require the frame pointer register to be reserved (or point to a
// new AAPCS-compilant frame record), even with -fno-omit-frame-pointer.
if (Arg *A = Args.getLastArg(options::OPT_mframe_chain)) {
StringRef V = A->getValue();
return V != "none";
}
return false;
}
return false;
}

// True if a target-specific option causes -fno-omit-frame-pointer to also
// cause frame records to be created in leaf functions.
static bool framePointerImpliesLeafFramePointer(const llvm::opt::ArgList &Args,
const llvm::Triple &Triple) {
if (Triple.isARM() || Triple.isThumb()) {
// For 32-bit Arm, the -mframe-chain=aapcs+leaf option causes the
// -fno-omit-frame-pointer optiion to imply -mno-omit-leaf-frame-pointer,
// but does not by itself imply either option.
if (Arg *A = Args.getLastArg(options::OPT_mframe_chain)) {
StringRef V = A->getValue();
return V == "aapcs+leaf";
}
return false;
}
return false;
}

clang::CodeGenOptions::FramePointerKind
getFramePointerKind(const llvm::opt::ArgList &Args,
const llvm::Triple &Triple) {
// We have 4 states:
// There are three things to consider here:
// * Should a frame record be created for non-leaf functions?
// * Should a frame record be created for leaf functions?
// * Is the frame pointer register reserved, i.e. must it always point to
// either a new, valid frame record or be un-modified?
//
// 00) leaf retained, non-leaf retained
// 01) leaf retained, non-leaf omitted (this is invalid)
// 10) leaf omitted, non-leaf retained
// (what -momit-leaf-frame-pointer was designed for)
// 11) leaf omitted, non-leaf omitted
// Not all combinations of these are valid:
// * It's not useful to have leaf frame records without non-leaf ones.
// * It's not useful to have frame records without reserving the frame
// pointer.
//
// "omit" options taking precedence over "no-omit" options is the only way
// to make 3 valid states representable
llvm::opt::Arg *A =
Args.getLastArg(clang::driver::options::OPT_fomit_frame_pointer,
clang::driver::options::OPT_fno_omit_frame_pointer);

bool OmitFP = A && A->getOption().matches(
clang::driver::options::OPT_fomit_frame_pointer);
bool NoOmitFP = A && A->getOption().matches(
clang::driver::options::OPT_fno_omit_frame_pointer);
bool OmitLeafFP =
Args.hasFlag(clang::driver::options::OPT_momit_leaf_frame_pointer,
clang::driver::options::OPT_mno_omit_leaf_frame_pointer,
Triple.isAArch64() || Triple.isPS() || Triple.isVE() ||
(Triple.isAndroid() && Triple.isRISCV64()));
if (NoOmitFP || mustUseNonLeafFramePointerForTarget(Triple) ||
(!OmitFP && useFramePointerForTargetByDefault(Args, Triple))) {
if (OmitLeafFP)
return clang::CodeGenOptions::FramePointerKind::NonLeaf;
return clang::CodeGenOptions::FramePointerKind::All;
}
// | Non-leaf | Leaf | Reserved |
// | N | N | N | FramePointerKind::None
// | N | N | Y | FramePointerKind::Reserved
// | N | Y | N | Invalid
// | N | Y | Y | Invalid
// | Y | N | N | Invalid
// | Y | N | Y | FramePointerKind::NonLeaf
// | Y | Y | N | Invalid
// | Y | Y | Y | FramePointerKind::All
//
// The FramePointerKind::Reserved case is currently only reachable for Arm,
// which has the -mframe-chain= option which can (in combination with
// -fno-omit-frame-pointer) specify that the frame chain must be valid,
// without requiring new frame records to be created.

bool DefaultFP = useFramePointerForTargetByDefault(Args, Triple);
bool EnableFP =
mustUseNonLeafFramePointerForTarget(Triple) ||
Args.hasFlag(clang::driver::options::OPT_fno_omit_frame_pointer,
clang::driver::options::OPT_fomit_frame_pointer, DefaultFP);

bool DefaultLeafFP =
useLeafFramePointerForTargetByDefault(Triple) ||
(EnableFP && framePointerImpliesLeafFramePointer(Args, Triple));
bool EnableLeafFP = Args.hasFlag(
clang::driver::options::OPT_mno_omit_leaf_frame_pointer,
clang::driver::options::OPT_momit_leaf_frame_pointer, DefaultLeafFP);

bool FPRegReserved = EnableFP || mustMaintainValidFrameChain(Args, Triple);

if (EnableFP) {
if (EnableLeafFP)
return clang::CodeGenOptions::FramePointerKind::All;
return clang::CodeGenOptions::FramePointerKind::NonLeaf;
}
if (FPRegReserved)
return clang::CodeGenOptions::FramePointerKind::Reserved;
return clang::CodeGenOptions::FramePointerKind::None;
}

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Driver/ToolChains/Cuda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,7 @@ NVPTXToolChain::getSystemGPUArchs(const ArgList &Args) const {
else
Program = GetProgramPath("nvptx-arch");

auto StdoutOrErr = executeToolChainProgram(Program);
auto StdoutOrErr = executeToolChainProgram(Program, /*SecondsToWait=*/10);
if (!StdoutOrErr)
return StdoutOrErr.takeError();

Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Driver/ToolChains/Flang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,9 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA,
case CodeGenOptions::FramePointerKind::None:
FPKeepKindStr = "-mframe-pointer=none";
break;
case CodeGenOptions::FramePointerKind::Reserved:
FPKeepKindStr = "-mframe-pointer=reserved";
break;
case CodeGenOptions::FramePointerKind::NonLeaf:
FPKeepKindStr = "-mframe-pointer=non-leaf";
break;
Expand Down
5 changes: 2 additions & 3 deletions clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
using namespace clang;
using namespace clang::extractapi;
using namespace llvm;
using namespace llvm::json;

namespace {

Expand Down Expand Up @@ -1036,9 +1035,9 @@ void SymbolGraphSerializer::serializeGraphToStream(
ExtendedModule &&EM) {
Object Root = serializeGraph(ModuleName, std::move(EM));
if (Options.Compact)
OS << formatv("{0}", Value(std::move(Root))) << "\n";
OS << formatv("{0}", json::Value(std::move(Root))) << "\n";
else
OS << formatv("{0:2}", Value(std::move(Root))) << "\n";
OS << formatv("{0:2}", json::Value(std::move(Root))) << "\n";
}

void SymbolGraphSerializer::serializeMainSymbolGraph(
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Format/ContinuationIndenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1712,7 +1712,7 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State,
(!Previous || Previous->isNot(tok::kw_return) ||
(Style.Language != FormatStyle::LK_Java && PrecedenceLevel > 0)) &&
(Style.AlignAfterOpenBracket != FormatStyle::BAS_DontAlign ||
PrecedenceLevel != prec::Comma || Current.NestingLevel == 0) &&
PrecedenceLevel > prec::Comma || Current.NestingLevel == 0) &&
(!Style.isTableGen() ||
(Previous && Previous->isOneOf(TT_TableGenDAGArgListComma,
TT_TableGenDAGArgListCommaToBreak)))) {
Expand Down
9 changes: 2 additions & 7 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1358,6 +1358,8 @@ class AnnotatingParser {
Line.First->startsSequence(tok::kw_export, Keywords.kw_module) ||
Line.First->startsSequence(tok::kw_export, Keywords.kw_import)) {
Tok->setType(TT_ModulePartitionColon);
} else if (Line.First->is(tok::kw_asm)) {
Tok->setType(TT_InlineASMColon);
} else if (Contexts.back().ColonIsDictLiteral || Style.isProto()) {
Tok->setType(TT_DictLiteral);
if (Style.Language == FormatStyle::LK_TextProto) {
Expand Down Expand Up @@ -1425,13 +1427,6 @@ class AnnotatingParser {
// This handles a special macro in ObjC code where selectors including
// the colon are passed as macro arguments.
Tok->setType(TT_ObjCMethodExpr);
} else if (Contexts.back().ContextKind == tok::l_paren &&
!Line.InPragmaDirective) {
if (Style.isTableGen() && Contexts.back().IsTableGenDAGArg) {
Tok->setType(TT_TableGenDAGArgListColon);
break;
}
Tok->setType(TT_InlineASMColon);
}
break;
case tok::pipe:
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/Format/UnwrappedLineParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1181,10 +1181,10 @@ void UnwrappedLineParser::parsePPDefine() {
Line->InMacroBody = true;

if (Style.SkipMacroDefinitionBody) {
do {
while (!eof()) {
FormatTok->Finalized = true;
nextToken();
} while (!eof());
FormatTok = Tokens->getNextToken();
}
addUnwrappedLine();
return;
}
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Frontend/FrontendActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1169,8 +1169,8 @@ void PrintDependencyDirectivesSourceMinimizerAction::ExecuteAction() {
llvm::SmallVector<dependency_directives_scan::Token, 16> Tokens;
llvm::SmallVector<dependency_directives_scan::Directive, 32> Directives;
if (scanSourceForDependencyDirectives(
FromFile.getBuffer(), Tokens, Directives, CI.getLangOpts(),
&CI.getDiagnostics(), SM.getLocForStartOfFile(SM.getMainFileID()))) {
FromFile.getBuffer(), Tokens, Directives, &CI.getDiagnostics(),
SM.getLocForStartOfFile(SM.getMainFileID()))) {
assert(CI.getDiagnostics().hasErrorOccurred() &&
"no errors reported for failure");

Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Interpreter/IncrementalParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,8 @@ void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) {
if (!ND)
continue;
// Check if we need to clean up the IdResolver chain.
if (ND->getDeclName().getFETokenInfo())
if (ND->getDeclName().getFETokenInfo() && !D->getLangOpts().ObjC &&
!D->getLangOpts().CPlusPlus)
getCI()->getSema().IdResolver.RemoveDecl(ND);
}
}
Expand Down
165 changes: 91 additions & 74 deletions clang/lib/Interpreter/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Host.h"

#include <cstdarg>

using namespace clang;

// FIXME: Figure out how to unify with namespace init_convenience from
Expand Down Expand Up @@ -270,14 +273,10 @@ Interpreter::~Interpreter() {
// can't find the precise resource directory in unittests so we have to hard
// code them.
const char *const Runtimes = R"(
#define __CLANG_REPL__ 1
#ifdef __cplusplus
#define EXTERN_C extern "C"
void *__clang_Interpreter_SetValueWithAlloc(void*, void*, void*);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, void*);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, float);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, double);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, long double);
void __clang_Interpreter_SetValueNoAlloc(void*,void*,void*,unsigned long long);
struct __clang_Interpreter_NewTag{} __ci_newtag;
void* operator new(__SIZE_TYPE__, void* __p, __clang_Interpreter_NewTag) noexcept;
template <class T, class = T (*)() /*disable for arrays*/>
Expand All @@ -289,7 +288,11 @@ const char *const Runtimes = R"(
void __clang_Interpreter_SetValueCopyArr(const T (*Src)[N], void* Placement, unsigned long Size) {
__clang_Interpreter_SetValueCopyArr(Src[0], Placement, Size);
}
#else
#define EXTERN_C extern
#endif // __cplusplus

EXTERN_C void __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, ...);
)";

llvm::Expected<std::unique_ptr<Interpreter>>
Expand Down Expand Up @@ -588,15 +591,17 @@ std::unique_ptr<RuntimeInterfaceBuilder> Interpreter::FindRuntimeInterface() {
if (!LookupInterface(ValuePrintingInfo[NoAlloc],
MagicRuntimeInterface[NoAlloc]))
return nullptr;
if (!LookupInterface(ValuePrintingInfo[WithAlloc],
MagicRuntimeInterface[WithAlloc]))
return nullptr;
if (!LookupInterface(ValuePrintingInfo[CopyArray],
MagicRuntimeInterface[CopyArray]))
return nullptr;
if (!LookupInterface(ValuePrintingInfo[NewTag],
MagicRuntimeInterface[NewTag]))
return nullptr;
if (Ctx.getLangOpts().CPlusPlus) {
if (!LookupInterface(ValuePrintingInfo[WithAlloc],
MagicRuntimeInterface[WithAlloc]))
return nullptr;
if (!LookupInterface(ValuePrintingInfo[CopyArray],
MagicRuntimeInterface[CopyArray]))
return nullptr;
if (!LookupInterface(ValuePrintingInfo[NewTag],
MagicRuntimeInterface[NewTag]))
return nullptr;
}

return createInProcessRuntimeInterfaceBuilder(*this, Ctx, S);
}
Expand Down Expand Up @@ -855,69 +860,81 @@ __clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal,
return VRef.getPtr();
}

// Pointers, lvalue struct that can take as a reference.
REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
void *Val) {
extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc(
void *This, void *OutVal, void *OpaqueType, ...) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
VRef.setPtr(Val);
}
Interpreter *I = static_cast<Interpreter *>(This);
VRef = Value(I, OpaqueType);
if (VRef.isVoid())
return;

REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal,
void *OpaqueType) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
}
va_list args;
va_start(args, /*last named param*/ OpaqueType);

static void SetValueDataBasedOnQualType(Value &V, unsigned long long Data) {
QualType QT = V.getType();
if (const auto *ET = QT->getAs<EnumType>())
QT = ET->getDecl()->getIntegerType();

switch (QT->castAs<BuiltinType>()->getKind()) {
default:
llvm_unreachable("unknown type kind!");
#define X(type, name) \
case BuiltinType::name: \
V.set##name(Data); \
break;
REPL_BUILTIN_TYPES
#undef X
QualType QT = VRef.getType();
if (VRef.getKind() == Value::K_PtrOrObj) {
VRef.setPtr(va_arg(args, void *));
} else {
if (const auto *ET = QT->getAs<EnumType>())
QT = ET->getDecl()->getIntegerType();
switch (QT->castAs<BuiltinType>()->getKind()) {
default:
llvm_unreachable("unknown type kind!");
break;
// Types shorter than int are resolved as int, else va_arg has UB.
case BuiltinType::Bool:
VRef.setBool(va_arg(args, int));
break;
case BuiltinType::Char_S:
VRef.setChar_S(va_arg(args, int));
break;
case BuiltinType::SChar:
VRef.setSChar(va_arg(args, int));
break;
case BuiltinType::Char_U:
VRef.setChar_U(va_arg(args, unsigned));
break;
case BuiltinType::UChar:
VRef.setUChar(va_arg(args, unsigned));
break;
case BuiltinType::Short:
VRef.setShort(va_arg(args, int));
break;
case BuiltinType::UShort:
VRef.setUShort(va_arg(args, unsigned));
break;
case BuiltinType::Int:
VRef.setInt(va_arg(args, int));
break;
case BuiltinType::UInt:
VRef.setUInt(va_arg(args, unsigned));
break;
case BuiltinType::Long:
VRef.setLong(va_arg(args, long));
break;
case BuiltinType::ULong:
VRef.setULong(va_arg(args, unsigned long));
break;
case BuiltinType::LongLong:
VRef.setLongLong(va_arg(args, long long));
break;
case BuiltinType::ULongLong:
VRef.setULongLong(va_arg(args, unsigned long long));
break;
// Types shorter than double are resolved as double, else va_arg has UB.
case BuiltinType::Float:
VRef.setFloat(va_arg(args, double));
break;
case BuiltinType::Double:
VRef.setDouble(va_arg(args, double));
break;
case BuiltinType::LongDouble:
VRef.setLongDouble(va_arg(args, long double));
break;
// See REPL_BUILTIN_TYPES.
}
}
}

REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
unsigned long long Val) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
SetValueDataBasedOnQualType(VRef, Val);
}

REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
float Val) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
VRef.setFloat(Val);
}

REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
double Val) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
VRef.setDouble(Val);
}

REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
long double Val) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
VRef.setLongDouble(Val);
va_end(args);
}

// A trampoline to work around the fact that operator placement new cannot
Expand Down
22 changes: 9 additions & 13 deletions clang/lib/Lex/DependencyDirectivesScanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,14 @@ struct DirectiveWithTokens {
struct Scanner {
Scanner(StringRef Input,
SmallVectorImpl<dependency_directives_scan::Token> &Tokens,
DiagnosticsEngine *Diags, SourceLocation InputSourceLoc,
const LangOptions &LangOpts)
DiagnosticsEngine *Diags, SourceLocation InputSourceLoc)
: Input(Input), Tokens(Tokens), Diags(Diags),
InputSourceLoc(InputSourceLoc),
LangOpts(getLangOptsForDepScanning(LangOpts)),
TheLexer(InputSourceLoc, this->LangOpts, Input.begin(), Input.begin(),
InputSourceLoc(InputSourceLoc), LangOpts(getLangOptsForDepScanning()),
TheLexer(InputSourceLoc, LangOpts, Input.begin(), Input.begin(),
Input.end()) {}

static LangOptions
getLangOptsForDepScanning(const LangOptions &invocationLangOpts) {
LangOptions LangOpts(invocationLangOpts);
static LangOptions getLangOptsForDepScanning() {
LangOptions LangOpts;
// Set the lexer to use 'tok::at' for '@', instead of 'tok::unknown'.
LangOpts.ObjC = true;
LangOpts.LineComment = true;
Expand Down Expand Up @@ -703,7 +700,7 @@ bool Scanner::lex_Pragma(const char *&First, const char *const End) {
SmallVector<dependency_directives_scan::Token> DiscardTokens;
const char *Begin = Buffer.c_str();
Scanner PragmaScanner{StringRef(Begin, Buffer.size()), DiscardTokens, Diags,
InputSourceLoc, LangOptions()};
InputSourceLoc};

PragmaScanner.TheLexer.setParsingPreprocessorDirective(true);
if (PragmaScanner.lexPragma(Begin, Buffer.end()))
Expand Down Expand Up @@ -953,10 +950,9 @@ bool Scanner::scan(SmallVectorImpl<Directive> &Directives) {

bool clang::scanSourceForDependencyDirectives(
StringRef Input, SmallVectorImpl<dependency_directives_scan::Token> &Tokens,
SmallVectorImpl<Directive> &Directives, const LangOptions &LangOpts,
DiagnosticsEngine *Diags, SourceLocation InputSourceLoc) {
return Scanner(Input, Tokens, Diags, InputSourceLoc, LangOpts)
.scan(Directives);
SmallVectorImpl<Directive> &Directives, DiagnosticsEngine *Diags,
SourceLocation InputSourceLoc) {
return Scanner(Input, Tokens, Diags, InputSourceLoc).scan(Directives);
}

void clang::printDependencyDirectivesAsSource(
Expand Down
5 changes: 1 addition & 4 deletions clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -571,11 +571,8 @@ StmtResult Parser::ParseExprStatement(ParsedStmtContext StmtCtx) {
}

Token *CurTok = nullptr;
// If the semicolon is missing at the end of REPL input, consider if
// we want to do value printing. Note this is only enabled in C++ mode
// since part of the implementation requires C++ language features.
// Note we shouldn't eat the token since the callback needs it.
if (Tok.is(tok::annot_repl_input_end) && Actions.getLangOpts().CPlusPlus)
if (Tok.is(tok::annot_repl_input_end))
CurTok = &Tok;
else
// Otherwise, eat the semicolon.
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/Scope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,11 @@ void Scope::dumpImpl(raw_ostream &OS) const {
{CompoundStmtScope, "CompoundStmtScope"},
{ClassInheritanceScope, "ClassInheritanceScope"},
{CatchScope, "CatchScope"},
{ConditionVarScope, "ConditionVarScope"},
{OpenMPOrderClauseScope, "OpenMPOrderClauseScope"},
{LambdaScope, "LambdaScope"},
{OpenACCComputeConstructScope, "OpenACCComputeConstructScope"},
{TypeAliasScope, "TypeAliasScope"},
{FriendScope, "FriendScope"},
};

Expand Down
6 changes: 3 additions & 3 deletions clang/lib/Sema/SemaAMDGPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ bool SemaAMDGPU::CheckAMDGCNBuiltinFunctionCall(unsigned BuiltinID,
constexpr const int SizeIdx = 2;
llvm::APSInt Size;
Expr *ArgExpr = TheCall->getArg(SizeIdx);
ExprResult R = SemaRef.VerifyIntegerConstantExpression(ArgExpr, &Size);
if (R.isInvalid())
return true;
[[maybe_unused]] ExprResult R =
SemaRef.VerifyIntegerConstantExpression(ArgExpr, &Size);
assert(!R.isInvalid());
switch (Size.getSExtValue()) {
case 1:
case 2:
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Sema/SemaAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/IdentifierTable.h"
Expand Down Expand Up @@ -46,6 +47,10 @@ static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,
// Check each AvailabilityAttr to find the one for this platform.
// For multiple attributes with the same platform try to find one for this
// environment.
// The attribute is always on the FunctionDecl, not on the
// FunctionTemplateDecl.
if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
D = FTD->getTemplatedDecl();
for (const auto *A : D->attrs()) {
if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) {
// FIXME: this is copied from CheckAvailability. We should try to
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2288,7 +2288,8 @@ void Sema::ActOnPopScope(SourceLocation Loc, Scope *S) {
// Partial translation units that are created in incremental processing must
// not clean up the IdResolver because PTUs should take into account the
// declarations that came from previous PTUs.
if (!PP.isIncrementalProcessingEnabled() || getLangOpts().ObjC)
if (!PP.isIncrementalProcessingEnabled() || getLangOpts().ObjC ||
getLangOpts().CPlusPlus)
IdResolver.RemoveDecl(D);

// Warn on it if we are shadowing a declaration.
Expand Down
Loading