2 changes: 2 additions & 0 deletions clang/lib/AST/ComputeDependence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,8 @@ ExprDependence clang::computeDependence(PredefinedExpr *E) {
ExprDependence clang::computeDependence(CallExpr *E,
llvm::ArrayRef<Expr *> PreArgs) {
auto D = E->getCallee()->getDependence();
if (E->getType()->isDependentType())
D |= ExprDependence::Type;
for (auto *A : llvm::ArrayRef(E->getArgs(), E->getNumArgs())) {
if (A)
D |= A->getDependence();
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2835,7 +2835,7 @@ CharUnits VarDecl::getFlexibleArrayInitChars(const ASTContext &Ctx) const {
if (!Ty || !Ty->getDecl()->hasFlexibleArrayMember())
return CharUnits::Zero();
auto *List = dyn_cast<InitListExpr>(getInit()->IgnoreParens());
if (!List)
if (!List || List->getNumInits() == 0)
return CharUnits::Zero();
const Expr *FlexibleInit = List->getInit(List->getNumInits() - 1);
auto InitTy = Ctx.getAsConstantArrayType(FlexibleInit->getType());
Expand Down
74 changes: 73 additions & 1 deletion clang/lib/AST/DeclBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
#include "clang/AST/Type.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/ObjCRuntime.h"
#include "clang/Basic/PartialDiagnostic.h"
Expand Down Expand Up @@ -411,6 +410,79 @@ bool Decl::isFileContextDecl() const {
return DC && DC->isFileContext();
}

bool Decl::isFlexibleArrayMemberLike(
ASTContext &Ctx, const Decl *D, QualType Ty,
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel,
bool IgnoreTemplateOrMacroSubstitution) {
// For compatibility with existing code, we treat arrays of length 0 or
// 1 as flexible array members.
const auto *CAT = Ctx.getAsConstantArrayType(Ty);
if (CAT) {
using FAMKind = LangOptions::StrictFlexArraysLevelKind;

llvm::APInt Size = CAT->getSize();
if (StrictFlexArraysLevel == FAMKind::IncompleteOnly)
return false;

// GCC extension, only allowed to represent a FAM.
if (Size.isZero())
return true;

if (StrictFlexArraysLevel == FAMKind::ZeroOrIncomplete && Size.uge(1))
return false;

if (StrictFlexArraysLevel == FAMKind::OneZeroOrIncomplete && Size.uge(2))
return false;
} else if (!Ctx.getAsIncompleteArrayType(Ty)) {
return false;
}

if (const auto *OID = dyn_cast_if_present<ObjCIvarDecl>(D))
return OID->getNextIvar() == nullptr;

const auto *FD = dyn_cast_if_present<FieldDecl>(D);
if (!FD)
return false;

if (CAT) {
// GCC treats an array memeber of a union as an FAM if the size is one or
// zero.
llvm::APInt Size = CAT->getSize();
if (FD->getParent()->isUnion() && (Size.isZero() || Size.isOne()))
return true;
}

// Don't consider sizes resulting from macro expansions or template argument
// substitution to form C89 tail-padded arrays.
if (IgnoreTemplateOrMacroSubstitution) {
TypeSourceInfo *TInfo = FD->getTypeSourceInfo();
while (TInfo) {
TypeLoc TL = TInfo->getTypeLoc();

// Look through typedefs.
if (TypedefTypeLoc TTL = TL.getAsAdjusted<TypedefTypeLoc>()) {
const TypedefNameDecl *TDL = TTL.getTypedefNameDecl();
TInfo = TDL->getTypeSourceInfo();
continue;
}

if (auto CTL = TL.getAs<ConstantArrayTypeLoc>()) {
if (const Expr *SizeExpr =
dyn_cast_if_present<IntegerLiteral>(CTL.getSizeExpr());
!SizeExpr || SizeExpr->getExprLoc().isMacroID())
return false;
}

break;
}
}

// Test that the field is the last in the structure.
RecordDecl::field_iterator FI(
DeclContext::decl_iterator(const_cast<FieldDecl *>(FD)));
return ++FI == FD->getParent()->field_end();
}

TranslationUnitDecl *Decl::getTranslationUnitDecl() {
if (auto *TUD = dyn_cast<TranslationUnitDecl>(this))
return TUD;
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,19 @@ bool CXXRecordDecl::isTriviallyCopyable() const {
return true;
}

bool CXXRecordDecl::isTriviallyCopyConstructible() const {

// A trivially copy constructible class is a class that:
// -- has no non-trivial copy constructors,
if (hasNonTrivialCopyConstructor())
return false;
// -- has a trivial destructor.
if (!hasTrivialDestructor())
return false;

return true;
}

void CXXRecordDecl::markedVirtualFunctionPure() {
// C++ [class.abstract]p2:
// A class is abstract if it has at least one pure virtual function.
Expand Down
83 changes: 10 additions & 73 deletions clang/lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,85 +205,22 @@ bool Expr::isKnownToHaveBooleanValue(bool Semantic) const {
}

bool Expr::isFlexibleArrayMemberLike(
ASTContext &Context,
ASTContext &Ctx,
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel,
bool IgnoreTemplateOrMacroSubstitution) const {

// For compatibility with existing code, we treat arrays of length 0 or
// 1 as flexible array members.
const auto *CAT = Context.getAsConstantArrayType(getType());
if (CAT) {
llvm::APInt Size = CAT->getSize();

using FAMKind = LangOptions::StrictFlexArraysLevelKind;

if (StrictFlexArraysLevel == FAMKind::IncompleteOnly)
return false;

// GCC extension, only allowed to represent a FAM.
if (Size == 0)
return true;

if (StrictFlexArraysLevel == FAMKind::ZeroOrIncomplete && Size.uge(1))
return false;

if (StrictFlexArraysLevel == FAMKind::OneZeroOrIncomplete && Size.uge(2))
return false;
} else if (!Context.getAsIncompleteArrayType(getType()))
return false;

const Expr *E = IgnoreParens();
const Decl *D = nullptr;

const NamedDecl *ND = nullptr;
if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
ND = DRE->getDecl();
else if (const auto *ME = dyn_cast<MemberExpr>(E))
ND = ME->getMemberDecl();
if (const auto *ME = dyn_cast<MemberExpr>(E))
D = ME->getMemberDecl();
else if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
D = DRE->getDecl();
else if (const auto *IRE = dyn_cast<ObjCIvarRefExpr>(E))
return IRE->getDecl()->getNextIvar() == nullptr;

if (!ND)
return false;
D = IRE->getDecl();

// A flexible array member must be the last member in the class.
// FIXME: If the base type of the member expr is not FD->getParent(),
// this should not be treated as a flexible array member access.
if (const auto *FD = dyn_cast<FieldDecl>(ND)) {
// GCC treats an array memeber of a union as an FAM if the size is one or
// zero.
if (CAT) {
llvm::APInt Size = CAT->getSize();
if (FD->getParent()->isUnion() && (Size.isZero() || Size.isOne()))
return true;
}

// Don't consider sizes resulting from macro expansions or template argument
// substitution to form C89 tail-padded arrays.
if (IgnoreTemplateOrMacroSubstitution) {
TypeSourceInfo *TInfo = FD->getTypeSourceInfo();
while (TInfo) {
TypeLoc TL = TInfo->getTypeLoc();
// Look through typedefs.
if (TypedefTypeLoc TTL = TL.getAsAdjusted<TypedefTypeLoc>()) {
const TypedefNameDecl *TDL = TTL.getTypedefNameDecl();
TInfo = TDL->getTypeSourceInfo();
continue;
}
if (ConstantArrayTypeLoc CTL = TL.getAs<ConstantArrayTypeLoc>()) {
const Expr *SizeExpr = dyn_cast<IntegerLiteral>(CTL.getSizeExpr());
if (!SizeExpr || SizeExpr->getExprLoc().isMacroID())
return false;
}
break;
}
}

RecordDecl::field_iterator FI(
DeclContext::decl_iterator(const_cast<FieldDecl *>(FD)));
return ++FI == FD->getParent()->field_end();
}

return false;
return Decl::isFlexibleArrayMemberLike(Ctx, D, E->getType(),
StrictFlexArraysLevel,
IgnoreTemplateOrMacroSubstitution);
}

const ValueDecl *
Expand Down
1 change: 0 additions & 1 deletion clang/lib/AST/FormatString.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,6 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const {
return NoMatchPromotionTypeConfusion;
break;
case BuiltinType::Half:
case BuiltinType::Float16:
case BuiltinType::Float:
if (T == C.DoubleTy)
return MatchPromotion;
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,17 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
}

case CK_FloatingCast: {
if (DiscardResult)
return this->discard(SubExpr);
if (!this->visit(SubExpr))
return false;
const auto *TargetSemantics = &Ctx.getFloatSemantics(CE->getType());
return this->emitCastFP(TargetSemantics, getRoundingMode(CE), CE);
}

case CK_IntegralToFloating: {
if (DiscardResult)
return this->discard(SubExpr);
std::optional<PrimType> FromT = classify(SubExpr->getType());
if (!FromT)
return false;
Expand All @@ -135,6 +139,9 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {

case CK_FloatingToBoolean:
case CK_FloatingToIntegral: {
if (DiscardResult)
return this->discard(SubExpr);

std::optional<PrimType> ToT = classify(CE->getType());

if (!ToT)
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,8 @@ Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
}

/// Unknown-size arrays of composite elements.
Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary,
UnknownSize)
Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem,
bool IsTemporary, UnknownSize)
: Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
Size(UnknownSizeMark), MDSize(0),
AllocSize(alignof(void *) + sizeof(InitMapPtr)), ElemDesc(Elem),
Expand All @@ -286,7 +286,7 @@ Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary,
}

/// Composite records.
Descriptor::Descriptor(const DeclTy &D, Record *R, MetadataSize MD,
Descriptor::Descriptor(const DeclTy &D, const Record *R, MetadataSize MD,
bool IsConst, bool IsTemporary, bool IsMutable)
: Source(D), ElemSize(std::max<size_t>(alignof(void *), R->getFullSize())),
Size(ElemSize), MDSize(MD.value_or(0)), AllocSize(Size + MDSize),
Expand Down
7 changes: 4 additions & 3 deletions clang/lib/AST/Interp/Descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ struct Descriptor final {
static constexpr MetadataSize InlineDescMD = sizeof(InlineDescriptor);

/// Pointer to the record, if block contains records.
Record *const ElemRecord = nullptr;
const Record *const ElemRecord = nullptr;
/// Descriptor of the array element.
const Descriptor *const ElemDesc = nullptr;
/// Flag indicating if the block is mutable.
Expand Down Expand Up @@ -135,10 +135,11 @@ struct Descriptor final {
unsigned NumElems, bool IsConst, bool IsTemporary, bool IsMutable);

/// Allocates a descriptor for an array of composites of unknown size.
Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary, UnknownSize);
Descriptor(const DeclTy &D, const Descriptor *Elem, bool IsTemporary,
UnknownSize);

/// Allocates a descriptor for a record.
Descriptor(const DeclTy &D, Record *R, MetadataSize MD, bool IsConst,
Descriptor(const DeclTy &D, const Record *R, MetadataSize MD, bool IsConst,
bool IsTemporary, bool IsMutable);

Descriptor(const DeclTy &D, MetadataSize MD);
Expand Down
16 changes: 14 additions & 2 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,18 @@ void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC) {
if (CurFunc->isUnevaluatedBuiltin())
return;

// Some builtin functions require us to only look at the call site, since
// the classified parameter types do not match.
if (CurFunc->isBuiltin()) {
const auto *CE =
cast<CallExpr>(S.Current->Caller->getExpr(S.Current->getRetPC()));
for (int32_t I = CE->getNumArgs() - 1; I >= 0; --I) {
const Expr *A = CE->getArg(I);
popArg(S, A);
}
return;
}

if (S.Current->Caller && CurFunc->isVariadic()) {
// CallExpr we're look for is at the return PC of the current function, i.e.
// in the caller.
Expand Down Expand Up @@ -290,10 +302,10 @@ bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
}

bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (!CheckDummy(S, OpPC, Ptr))
return false;
if (!CheckLive(S, OpPC, Ptr, AK_Read))
return false;
if (!CheckDummy(S, OpPC, Ptr))
return false;
if (!CheckExtern(S, OpPC, Ptr))
return false;
if (!CheckRange(S, OpPC, Ptr, AK_Read))
Expand Down
6 changes: 0 additions & 6 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -1813,9 +1813,6 @@ inline bool ArrayElemPtr(InterpState &S, CodePtr OpPC) {
const T &Offset = S.Stk.pop<T>();
const Pointer &Ptr = S.Stk.peek<Pointer>();

if (!CheckArray(S, OpPC, Ptr))
return false;

if (!OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr))
return false;

Expand Down Expand Up @@ -1843,9 +1840,6 @@ inline bool ArrayElemPtrPop(InterpState &S, CodePtr OpPC) {
const T &Offset = S.Stk.pop<T>();
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (!CheckArray(S, OpPC, Ptr))
return false;

if (!OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr))
return false;

Expand Down
33 changes: 30 additions & 3 deletions clang/lib/AST/Interp/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ static bool retPrimValue(InterpState &S, CodePtr OpPC, APValue &Result,
case X: \
return Ret<X>(S, OpPC, Result);
switch (*T) {
RET_CASE(PT_Ptr);
RET_CASE(PT_FnPtr);
RET_CASE(PT_Float);
RET_CASE(PT_Bool);
RET_CASE(PT_Sint8);
Expand Down Expand Up @@ -613,15 +615,34 @@ static bool interp__builtin_ffs(InterpState &S, CodePtr OpPC,
return true;
}

static bool interp__builtin_addressof(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
PrimType PtrT =
S.getContext().classify(Call->getArg(0)->getType()).value_or(PT_Ptr);

if (PtrT == PT_FnPtr) {
const FunctionPointer &Arg = S.Stk.peek<FunctionPointer>();
S.Stk.push<FunctionPointer>(Arg);
} else if (PtrT == PT_Ptr) {
const Pointer &Arg = S.Stk.peek<Pointer>();
S.Stk.push<Pointer>(Arg);
} else {
assert(false && "Unsupported pointer type passed to __builtin_addressof()");
}
return true;
}

bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
const CallExpr *Call) {
InterpFrame *Frame = S.Current;
APValue Dummy;

QualType ReturnType = Call->getCallReturnType(S.getCtx());
std::optional<PrimType> ReturnT = S.getContext().classify(ReturnType);
std::optional<PrimType> ReturnT = S.getContext().classify(Call->getType());

// If classify failed, we assume void.
assert(ReturnT || ReturnType->isVoidType());
assert(ReturnT || Call->getType()->isVoidType());

switch (F->getBuiltinID()) {
case Builtin::BI__builtin_is_constant_evaluated:
Expand Down Expand Up @@ -820,6 +841,12 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
if (!interp__builtin_ffs(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BIaddressof:
case Builtin::BI__addressof:
case Builtin::BI__builtin_addressof:
if (!interp__builtin_addressof(S, OpPC, Frame, F, Call))
return false;
break;

default:
return false;
Expand Down
16 changes: 8 additions & 8 deletions clang/lib/AST/Interp/Program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,14 +315,14 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
bool IsConst, bool IsTemporary,
bool IsMutable, const Expr *Init) {
// Classes and structures.
if (auto *RT = Ty->getAs<RecordType>()) {
if (auto *Record = getOrCreateRecord(RT->getDecl()))
if (const auto *RT = Ty->getAs<RecordType>()) {
if (const auto *Record = getOrCreateRecord(RT->getDecl()))
return allocateDescriptor(D, Record, MDSize, IsConst, IsTemporary,
IsMutable);
}

// Arrays.
if (auto ArrayType = Ty->getAsArrayTypeUnsafe()) {
if (const auto ArrayType = Ty->getAsArrayTypeUnsafe()) {
QualType ElemTy = ArrayType->getElementType();
// Array of well-known bounds.
if (auto CAT = dyn_cast<ConstantArrayType>(ArrayType)) {
Expand All @@ -338,7 +338,7 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
} else {
// Arrays of composites. In this case, the array is a list of pointers,
// followed by the actual elements.
Descriptor *ElemDesc = createDescriptor(
const Descriptor *ElemDesc = createDescriptor(
D, ElemTy.getTypePtr(), std::nullopt, IsConst, IsTemporary);
if (!ElemDesc)
return nullptr;
Expand All @@ -358,8 +358,8 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
return allocateDescriptor(D, *T, IsTemporary,
Descriptor::UnknownSize{});
} else {
Descriptor *Desc = createDescriptor(D, ElemTy.getTypePtr(), MDSize,
IsConst, IsTemporary);
const Descriptor *Desc = createDescriptor(D, ElemTy.getTypePtr(),
MDSize, IsConst, IsTemporary);
if (!Desc)
return nullptr;
return allocateDescriptor(D, Desc, IsTemporary,
Expand All @@ -369,14 +369,14 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
}

// Atomic types.
if (auto *AT = Ty->getAs<AtomicType>()) {
if (const auto *AT = Ty->getAs<AtomicType>()) {
const Type *InnerTy = AT->getValueType().getTypePtr();
return createDescriptor(D, InnerTy, MDSize, IsConst, IsTemporary,
IsMutable);
}

// Complex types - represented as arrays of elements.
if (auto *CT = Ty->getAs<ComplexType>()) {
if (const auto *CT = Ty->getAs<ComplexType>()) {
PrimType ElemTy = *Ctx.classify(CT->getElementType());
return allocateDescriptor(D, ElemTy, MDSize, 2, IsConst, IsTemporary,
IsMutable);
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/AST/TextNodeDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1094,6 +1094,16 @@ void clang::TextNodeDumper::VisitReturnStmt(const ReturnStmt *Node) {
}
}

void clang::TextNodeDumper::VisitCoawaitExpr(const CoawaitExpr *Node) {
if (Node->isImplicit())
OS << " implicit";
}

void clang::TextNodeDumper::VisitCoreturnStmt(const CoreturnStmt *Node) {
if (Node->isImplicit())
OS << " implicit";
}

void TextNodeDumper::VisitConstantExpr(const ConstantExpr *Node) {
if (Node->hasAPValueResult())
AddChild("value",
Expand Down
41 changes: 32 additions & 9 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2604,19 +2604,22 @@ bool QualType::isTrivialType(const ASTContext &Context) const {
return false;
}

bool QualType::isTriviallyCopyableType(const ASTContext &Context) const {
if ((*this)->isArrayType())
return Context.getBaseElementType(*this).isTriviallyCopyableType(Context);
static bool isTriviallyCopyableTypeImpl(const QualType &type,
const ASTContext &Context,
bool IsCopyConstructible) {
if (type->isArrayType())
return isTriviallyCopyableTypeImpl(Context.getBaseElementType(type),
Context, IsCopyConstructible);

if (hasNonTrivialObjCLifetime())
if (type.hasNonTrivialObjCLifetime())
return false;

// C++11 [basic.types]p9 - See Core 2094
// Scalar types, trivially copyable class types, arrays of such types, and
// cv-qualified versions of these types are collectively
// called trivially copyable types.
// called trivially copy constructible types.

QualType CanonicalType = getCanonicalType();
QualType CanonicalType = type.getCanonicalType();
if (CanonicalType->isDependentType())
return false;

Expand All @@ -2634,16 +2637,29 @@ bool QualType::isTriviallyCopyableType(const ASTContext &Context) const {

if (const auto *RT = CanonicalType->getAs<RecordType>()) {
if (const auto *ClassDecl = dyn_cast<CXXRecordDecl>(RT->getDecl())) {
if (!ClassDecl->isTriviallyCopyable()) return false;
if (IsCopyConstructible) {
return ClassDecl->isTriviallyCopyConstructible();
} else {
return ClassDecl->isTriviallyCopyable();
}
}

return true;
}

// No other types can match.
return false;
}

bool QualType::isTriviallyCopyableType(const ASTContext &Context) const {
return isTriviallyCopyableTypeImpl(*this, Context,
/*IsCopyConstructible=*/false);
}

bool QualType::isTriviallyCopyConstructibleType(
const ASTContext &Context) const {
return isTriviallyCopyableTypeImpl(*this, Context,
/*IsCopyConstructible=*/true);
}

bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const {
QualType BaseElementType = Context.getBaseElementType(*this);

Expand Down Expand Up @@ -3414,6 +3430,13 @@ StringRef FunctionType::getNameForCallConv(CallingConv CC) {
llvm_unreachable("Invalid calling convention.");
}

void FunctionProtoType::ExceptionSpecInfo::instantiate() {
assert(Type == EST_Uninstantiated);
NoexceptExpr =
cast<FunctionProtoType>(SourceTemplate->getType())->getNoexceptExpr();
Type = EST_DependentNoexcept;
}

FunctionProtoType::FunctionProtoType(QualType result, ArrayRef<QualType> params,
QualType canonical,
const ExtProtoInfo &epi)
Expand Down
355 changes: 191 additions & 164 deletions clang/lib/Analysis/ExprMutationAnalyzer.cpp

Large diffs are not rendered by default.

7 changes: 1 addition & 6 deletions clang/lib/Analysis/PathDiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,7 @@
using namespace clang;
using namespace ento;

static StringRef StripTrailingDots(StringRef s) {
for (StringRef::size_type i = s.size(); i != 0; --i)
if (s[i - 1] != '.')
return s.substr(0, i);
return {};
}
static StringRef StripTrailingDots(StringRef s) { return s.rtrim('.'); }

PathDiagnosticPiece::PathDiagnosticPiece(StringRef s,
Kind k, DisplayHint hint)
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Basic/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ bool AArch64TargetInfo::validateBranchProtection(StringRef Spec, StringRef,

BPI.BranchTargetEnforcement = PBP.BranchTargetEnforcement;
BPI.BranchProtectionPAuthLR = PBP.BranchProtectionPAuthLR;
BPI.GuardedControlStack = PBP.GuardedControlStack;
return true;
}

Expand Down Expand Up @@ -532,6 +533,9 @@ void AArch64TargetInfo::getTargetDefines(const LangOptions &Opts,
if (Opts.BranchTargetEnforcement)
Builder.defineMacro("__ARM_FEATURE_BTI_DEFAULT", "1");

if (Opts.GuardedControlStack)
Builder.defineMacro("__ARM_FEATURE_GCS_DEFAULT", "1");

if (HasLS64)
Builder.defineMacro("__ARM_FEATURE_LS64", "1");

Expand All @@ -544,6 +548,9 @@ void AArch64TargetInfo::getTargetDefines(const LangOptions &Opts,
if (HasD128)
Builder.defineMacro("__ARM_FEATURE_SYSREG128", "1");

if (HasGCS)
Builder.defineMacro("__ARM_FEATURE_GCS", "1");

if (*ArchInfo == llvm::AArch64::ARMV8_1A)
getTargetDefinesARMV81A(Opts, Builder);
else if (*ArchInfo == llvm::AArch64::ARMV8_2A)
Expand Down
64 changes: 64 additions & 0 deletions clang/lib/Basic/Targets/ARM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/TargetParser/ARMTargetParser.h"

using namespace clang;
using namespace clang::targets;
Expand Down Expand Up @@ -837,6 +838,69 @@ void ARMTargetInfo::getTargetDefines(const LangOptions &Opts,
if (Opts.RWPI)
Builder.defineMacro("__ARM_RWPI", "1");

// Macros for enabling co-proc intrinsics
uint64_t FeatureCoprocBF = 0;
switch (ArchKind) {
default:
break;
case llvm::ARM::ArchKind::ARMV4:
case llvm::ARM::ArchKind::ARMV4T:
// Filter __arm_ldcl and __arm_stcl in acle.h
FeatureCoprocBF = isThumb() ? 0 : FEATURE_COPROC_B1;
break;
case llvm::ARM::ArchKind::ARMV5T:
FeatureCoprocBF = isThumb() ? 0 : FEATURE_COPROC_B1 | FEATURE_COPROC_B2;
break;
case llvm::ARM::ArchKind::ARMV5TE:
case llvm::ARM::ArchKind::ARMV5TEJ:
if (!isThumb())
FeatureCoprocBF =
FEATURE_COPROC_B1 | FEATURE_COPROC_B2 | FEATURE_COPROC_B3;
break;
case llvm::ARM::ArchKind::ARMV6:
case llvm::ARM::ArchKind::ARMV6K:
case llvm::ARM::ArchKind::ARMV6KZ:
case llvm::ARM::ArchKind::ARMV6T2:
if (!isThumb() || ArchKind == llvm::ARM::ArchKind::ARMV6T2)
FeatureCoprocBF = FEATURE_COPROC_B1 | FEATURE_COPROC_B2 |
FEATURE_COPROC_B3 | FEATURE_COPROC_B4;
break;
case llvm::ARM::ArchKind::ARMV7A:
case llvm::ARM::ArchKind::ARMV7R:
case llvm::ARM::ArchKind::ARMV7M:
case llvm::ARM::ArchKind::ARMV7S:
case llvm::ARM::ArchKind::ARMV7EM:
FeatureCoprocBF = FEATURE_COPROC_B1 | FEATURE_COPROC_B2 |
FEATURE_COPROC_B3 | FEATURE_COPROC_B4;
break;
case llvm::ARM::ArchKind::ARMV8A:
case llvm::ARM::ArchKind::ARMV8R:
case llvm::ARM::ArchKind::ARMV8_1A:
case llvm::ARM::ArchKind::ARMV8_2A:
case llvm::ARM::ArchKind::ARMV8_3A:
case llvm::ARM::ArchKind::ARMV8_4A:
case llvm::ARM::ArchKind::ARMV8_5A:
case llvm::ARM::ArchKind::ARMV8_6A:
case llvm::ARM::ArchKind::ARMV8_7A:
case llvm::ARM::ArchKind::ARMV8_8A:
case llvm::ARM::ArchKind::ARMV8_9A:
case llvm::ARM::ArchKind::ARMV9A:
case llvm::ARM::ArchKind::ARMV9_1A:
case llvm::ARM::ArchKind::ARMV9_2A:
case llvm::ARM::ArchKind::ARMV9_3A:
case llvm::ARM::ArchKind::ARMV9_4A:
// Filter __arm_cdp, __arm_ldcl, __arm_stcl in arm_acle.h
FeatureCoprocBF = FEATURE_COPROC_B1 | FEATURE_COPROC_B3;
break;
case llvm::ARM::ArchKind::ARMV8MMainline:
case llvm::ARM::ArchKind::ARMV8_1MMainline:
FeatureCoprocBF = FEATURE_COPROC_B1 | FEATURE_COPROC_B2 |
FEATURE_COPROC_B3 | FEATURE_COPROC_B4;
break;
}
Builder.defineMacro("__ARM_FEATURE_COPROC",
"0x" + Twine::utohexstr(FeatureCoprocBF));

if (ArchKind == llvm::ARM::ArchKind::XSCALE)
Builder.defineMacro("__XSCALE__");

Expand Down
13 changes: 13 additions & 0 deletions clang/lib/Basic/Targets/ARM.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,19 @@ class LLVM_LIBRARY_VISIBILITY ARMTargetInfo : public TargetInfo {
};
uint32_t HW_FP;

enum {
/// __arm_cdp __arm_ldc, __arm_ldcl, __arm_stc,
/// __arm_stcl, __arm_mcr and __arm_mrc
FEATURE_COPROC_B1 = (1 << 0),
/// __arm_cdp2, __arm_ldc2, __arm_stc2, __arm_ldc2l,
/// __arm_stc2l, __arm_mcr2 and __arm_mrc2
FEATURE_COPROC_B2 = (1 << 1),
/// __arm_mcrr, __arm_mrrc
FEATURE_COPROC_B3 = (1 << 2),
/// __arm_mcrr2, __arm_mrrc2
FEATURE_COPROC_B4 = (1 << 3),
};

void setABIAAPCS();
void setABIAPCS(bool IsAAPCS16);

Expand Down
28 changes: 10 additions & 18 deletions clang/lib/Basic/Targets/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,8 @@ void RISCVTargetInfo::getTargetDefines(const LangOptions &Opts,
auto ExtName = Extension.first;
auto ExtInfo = Extension.second;

Builder.defineMacro(
Twine("__riscv_", ExtName),
Twine(getVersionValue(ExtInfo.MajorVersion, ExtInfo.MinorVersion)));
Builder.defineMacro(Twine("__riscv_", ExtName),
Twine(getVersionValue(ExtInfo.Major, ExtInfo.Minor)));
}

if (ISAInfo->hasExtension("m") || ISAInfo->hasExtension("zmmul"))
Expand Down Expand Up @@ -237,22 +236,15 @@ ArrayRef<Builtin::Info> RISCVTargetInfo::getTargetBuiltins() const {

static std::vector<std::string>
collectNonISAExtFeature(ArrayRef<std::string> FeaturesNeedOverride, int XLen) {
auto ParseResult =
llvm::RISCVISAInfo::parseFeatures(XLen, FeaturesNeedOverride);

if (!ParseResult) {
consumeError(ParseResult.takeError());
return std::vector<std::string>();
}

std::vector<std::string> ImpliedFeatures = (*ParseResult)->toFeatureVector();

std::vector<std::string> NonISAExtFeatureVec;

auto IsNonISAExtFeature = [](const std::string &Feature) {
assert(Feature.size() > 1 && (Feature[0] == '+' || Feature[0] == '-'));
StringRef Ext = StringRef(Feature).drop_front(); // drop the +/-
return !llvm::RISCVISAInfo::isSupportedExtensionFeature(Ext);
};
llvm::copy_if(FeaturesNeedOverride, std::back_inserter(NonISAExtFeatureVec),
[&](const std::string &Feat) {
return !llvm::is_contained(ImpliedFeatures, Feat);
});
IsNonISAExtFeature);

return NonISAExtFeatureVec;
}
Expand Down Expand Up @@ -303,7 +295,7 @@ bool RISCVTargetInfo::initFeatureMap(
}

// RISCVISAInfo makes implications for ISA features
std::vector<std::string> ImpliedFeatures = (*ParseResult)->toFeatureVector();
std::vector<std::string> ImpliedFeatures = (*ParseResult)->toFeatures();

// parseFeatures normalizes the feature set by dropping any explicit
// negatives, and non-extension features. We need to preserve the later
Expand Down Expand Up @@ -420,7 +412,7 @@ static void handleFullArchString(StringRef FullArchStr,
// Forward the invalid FullArchStr.
Features.push_back("+" + FullArchStr.str());
} else {
std::vector<std::string> FeatStrings = (*RII)->toFeatureVector();
std::vector<std::string> FeatStrings = (*RII)->toFeatures();
Features.insert(Features.end(), FeatStrings.begin(), FeatStrings.end());
}
}
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Basic/Targets/X86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,11 +295,13 @@ bool X86TargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
HasAVX512BF16 = true;
} else if (Feature == "+avx512er") {
HasAVX512ER = true;
Diags.Report(diag::warn_knl_knm_isa_support_removed);
} else if (Feature == "+avx512fp16") {
HasAVX512FP16 = true;
HasLegalHalfType = true;
} else if (Feature == "+avx512pf") {
HasAVX512PF = true;
Diags.Report(diag::warn_knl_knm_isa_support_removed);
} else if (Feature == "+avx512dq") {
HasAVX512DQ = true;
} else if (Feature == "+avx512bitalg") {
Expand Down Expand Up @@ -358,6 +360,7 @@ bool X86TargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
HasPREFETCHI = true;
} else if (Feature == "+prefetchwt1") {
HasPREFETCHWT1 = true;
Diags.Report(diag::warn_knl_knm_isa_support_removed);
} else if (Feature == "+clzero") {
HasCLZERO = true;
} else if (Feature == "+cldemote") {
Expand Down
244 changes: 242 additions & 2 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/OSLog.h"
#include "clang/AST/OperationKinds.h"
#include "clang/Basic/TargetBuiltins.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
Expand Down Expand Up @@ -818,6 +819,238 @@ CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
}

const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberField(
ASTContext &Ctx, const RecordDecl *RD, StringRef Name, uint64_t &Offset) {
const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
getLangOpts().getStrictFlexArraysLevel();
unsigned FieldNo = 0;
bool IsUnion = RD->isUnion();

for (const Decl *D : RD->decls()) {
if (const auto *Field = dyn_cast<FieldDecl>(D);
Field && (Name.empty() || Field->getNameAsString() == Name) &&
Decl::isFlexibleArrayMemberLike(
Ctx, Field, Field->getType(), StrictFlexArraysLevel,
/*IgnoreTemplateOrMacroSubstitution=*/true)) {
const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
Offset += Layout.getFieldOffset(FieldNo);
return Field;
}

if (const auto *Record = dyn_cast<RecordDecl>(D))
if (const FieldDecl *Field =
FindFlexibleArrayMemberField(Ctx, Record, Name, Offset)) {
const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
Offset += Layout.getFieldOffset(FieldNo);
return Field;
}

if (!IsUnion && isa<FieldDecl>(D))
++FieldNo;
}

return nullptr;
}

static unsigned CountCountedByAttrs(const RecordDecl *RD) {
unsigned Num = 0;

for (const Decl *D : RD->decls()) {
if (const auto *FD = dyn_cast<FieldDecl>(D);
FD && FD->hasAttr<CountedByAttr>()) {
return ++Num;
}

if (const auto *Rec = dyn_cast<RecordDecl>(D))
Num += CountCountedByAttrs(Rec);
}

return Num;
}

llvm::Value *
CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
llvm::IntegerType *ResType) {
// The code generated here calculates the size of a struct with a flexible
// array member that uses the counted_by attribute. There are two instances
// we handle:
//
// struct s {
// unsigned long flags;
// int count;
// int array[] __attribute__((counted_by(count)));
// }
//
// 1) bdos of the flexible array itself:
//
// __builtin_dynamic_object_size(p->array, 1) ==
// p->count * sizeof(*p->array)
//
// 2) bdos of a pointer into the flexible array:
//
// __builtin_dynamic_object_size(&p->array[42], 1) ==
// (p->count - 42) * sizeof(*p->array)
//
// 2) bdos of the whole struct, including the flexible array:
//
// __builtin_dynamic_object_size(p, 1) ==
// max(sizeof(struct s),
// offsetof(struct s, array) + p->count * sizeof(*p->array))
//
ASTContext &Ctx = getContext();
const Expr *Base = E->IgnoreParenImpCasts();
const Expr *Idx = nullptr;

if (const auto *UO = dyn_cast<UnaryOperator>(Base);
UO && UO->getOpcode() == UO_AddrOf) {
Expr *SubExpr = UO->getSubExpr()->IgnoreParenImpCasts();
if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(SubExpr)) {
Base = ASE->getBase()->IgnoreParenImpCasts();
Idx = ASE->getIdx()->IgnoreParenImpCasts();

if (const auto *IL = dyn_cast<IntegerLiteral>(Idx)) {
int64_t Val = IL->getValue().getSExtValue();
if (Val < 0)
return getDefaultBuiltinObjectSizeResult(Type, ResType);

if (Val == 0)
// The index is 0, so we don't need to take it into account.
Idx = nullptr;
}
} else {
// Potential pointer to another element in the struct.
Base = SubExpr;
}
}

// Get the flexible array member Decl.
const RecordDecl *OuterRD = nullptr;
std::string FAMName;
if (const auto *ME = dyn_cast<MemberExpr>(Base)) {
// Check if \p Base is referencing the FAM itself.
const ValueDecl *VD = ME->getMemberDecl();
OuterRD = VD->getDeclContext()->getOuterLexicalRecordContext();
FAMName = VD->getNameAsString();
} else if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
// Check if we're pointing to the whole struct.
QualType Ty = DRE->getDecl()->getType();
if (Ty->isPointerType())
Ty = Ty->getPointeeType();
OuterRD = Ty->getAsRecordDecl();

// If we have a situation like this:
//
// struct union_of_fams {
// int flags;
// union {
// signed char normal_field;
// struct {
// int count1;
// int arr1[] __counted_by(count1);
// };
// struct {
// signed char count2;
// int arr2[] __counted_by(count2);
// };
// };
// };
//
// We don't konw which 'count' to use in this scenario:
//
// size_t get_size(struct union_of_fams *p) {
// return __builtin_dynamic_object_size(p, 1);
// }
//
// Instead of calculating a wrong number, we give up.
if (OuterRD && CountCountedByAttrs(OuterRD) > 1)
return nullptr;
}

if (!OuterRD)
return nullptr;

uint64_t Offset = 0;
const FieldDecl *FAMDecl =
FindFlexibleArrayMemberField(Ctx, OuterRD, FAMName, Offset);
Offset = Ctx.toCharUnitsFromBits(Offset).getQuantity();

if (!FAMDecl || !FAMDecl->hasAttr<CountedByAttr>())
// No flexible array member found or it doesn't have the "counted_by"
// attribute.
return nullptr;

const FieldDecl *CountedByFD = FindCountedByField(FAMDecl);
if (!CountedByFD)
// Can't find the field referenced by the "counted_by" attribute.
return nullptr;

// Build a load of the counted_by field.
bool IsSigned = CountedByFD->getType()->isSignedIntegerType();
Value *CountedByInst = EmitCountedByFieldExpr(Base, FAMDecl, CountedByFD);
if (!CountedByInst)
return getDefaultBuiltinObjectSizeResult(Type, ResType);

CountedByInst = Builder.CreateIntCast(CountedByInst, ResType, IsSigned);

// Build a load of the index and subtract it from the count.
Value *IdxInst = nullptr;
if (Idx) {
if (Idx->HasSideEffects(getContext()))
// We can't have side-effects.
return getDefaultBuiltinObjectSizeResult(Type, ResType);

bool IdxSigned = Idx->getType()->isSignedIntegerType();
IdxInst = EmitAnyExprToTemp(Idx).getScalarVal();
IdxInst = Builder.CreateIntCast(IdxInst, ResType, IdxSigned);

// We go ahead with the calculation here. If the index turns out to be
// negative, we'll catch it at the end.
CountedByInst =
Builder.CreateSub(CountedByInst, IdxInst, "", !IsSigned, IsSigned);
}

// Calculate how large the flexible array member is in bytes.
const ArrayType *ArrayTy = Ctx.getAsArrayType(FAMDecl->getType());
CharUnits Size = Ctx.getTypeSizeInChars(ArrayTy->getElementType());
llvm::Constant *ElemSize =
llvm::ConstantInt::get(ResType, Size.getQuantity(), IsSigned);
Value *FAMSize =
Builder.CreateMul(CountedByInst, ElemSize, "", !IsSigned, IsSigned);
FAMSize = Builder.CreateIntCast(FAMSize, ResType, IsSigned);
Value *Res = FAMSize;

if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
// The whole struct is specificed in the __bdos.
const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(OuterRD);

// Get the offset of the FAM.
llvm::Constant *FAMOffset = ConstantInt::get(ResType, Offset, IsSigned);
Value *OffsetAndFAMSize =
Builder.CreateAdd(FAMOffset, Res, "", !IsSigned, IsSigned);

// Get the full size of the struct.
llvm::Constant *SizeofStruct =
ConstantInt::get(ResType, Layout.getSize().getQuantity(), IsSigned);

// max(sizeof(struct s),
// offsetof(struct s, array) + p->count * sizeof(*p->array))
Res = IsSigned
? Builder.CreateBinaryIntrinsic(llvm::Intrinsic::smax,
OffsetAndFAMSize, SizeofStruct)
: Builder.CreateBinaryIntrinsic(llvm::Intrinsic::umax,
OffsetAndFAMSize, SizeofStruct);
}

// A negative \p IdxInst or \p CountedByInst means that the index lands
// outside of the flexible array member. If that's the case, we want to
// return 0.
Value *Cmp = Builder.CreateIsNotNeg(CountedByInst);
if (IdxInst)
Cmp = Builder.CreateAnd(Builder.CreateIsNotNeg(IdxInst), Cmp);

return Builder.CreateSelect(Cmp, Res, ConstantInt::get(ResType, 0, IsSigned));
}

/// Returns a Value corresponding to the size of the given expression.
/// This Value may be either of the following:
/// - A llvm::Argument (if E is a param with the pass_object_size attribute on
Expand Down Expand Up @@ -850,6 +1083,13 @@ CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type,
}
}

if (IsDynamic) {
// Emit special code for a flexible array member with the "counted_by"
// attribute.
if (Value *V = emitFlexibleArrayMemberSize(E, Type, ResType))
return V;
}

// LLVM can't handle Type=3 appropriately, and __builtin_object_size shouldn't
// evaluate E for side-effects. In either case, we shouldn't lower to
// @llvm.objectsize.
Expand Down Expand Up @@ -9681,8 +9921,8 @@ Value *CodeGenFunction::EmitSVEMaskedStore(const CallExpr *E,
bool IsQuadStore = false;

switch (IntrinsicID) {
case Intrinsic::aarch64_sve_st1uwq:
case Intrinsic::aarch64_sve_st1udq:
case Intrinsic::aarch64_sve_st1wq:
case Intrinsic::aarch64_sve_st1dq:
AddrMemoryTy = llvm::ScalableVectorType::get(MemEltTy, 1);
PredTy =
llvm::ScalableVectorType::get(IntegerType::get(getLLVMContext(), 1), 1);
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2612,6 +2612,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
if (IRFunctionArgs.hasSRetArg()) {
llvm::AttrBuilder SRETAttrs(getLLVMContext());
SRETAttrs.addStructRetAttr(getTypes().ConvertTypeForMem(RetTy));
SRETAttrs.addAttribute(llvm::Attribute::Writable);
SRETAttrs.addAttribute(llvm::Attribute::DeadOnUnwind);
hasUsedSRet = true;
if (RetAI.getInReg())
SRETAttrs.addAttribute(llvm::Attribute::InReg);
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/CodeGen/CGClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,7 @@ void CodeGenFunction::EmitConstructorBody(FunctionArgList &Args) {
EnterCXXTryStmt(*cast<CXXTryStmt>(Body), true);

incrementProfileCounter(Body);
maybeCreateMCDCCondBitmap();

RunCleanupsScope RunCleanups(*this);

Expand Down Expand Up @@ -1444,8 +1445,10 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
}

Stmt *Body = Dtor->getBody();
if (Body)
if (Body) {
incrementProfileCounter(Body);
maybeCreateMCDCCondBitmap();
}

// The call to operator delete in a deleting destructor happens
// outside of the function-try-block, which means it's always
Expand Down Expand Up @@ -1548,6 +1551,7 @@ void CodeGenFunction::emitImplicitAssignmentOperatorBody(FunctionArgList &Args)
LexicalScope Scope(*this, RootCS->getSourceRange());

incrementProfileCounter(RootCS);
maybeCreateMCDCCondBitmap();
AssignmentMemcpyizer AM(*this, AssignOp, Args);
for (auto *I : RootCS->body())
AM.emitAssignment(I);
Expand Down
7 changes: 5 additions & 2 deletions clang/lib/CodeGen/CGException.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,9 @@ static const EHPersonality &getObjCPersonality(const TargetInfo &Target,
case ObjCRuntime::WatchOS:
return EHPersonality::NeXT_ObjC;
case ObjCRuntime::GNUstep:
if (L.ObjCRuntime.getVersion() >= VersionTuple(1, 7))
if (T.isOSCygMing())
return EHPersonality::GNU_CPlusPlus_SEH;
else if (L.ObjCRuntime.getVersion() >= VersionTuple(1, 7))
return EHPersonality::GNUstep_ObjC;
[[fallthrough]];
case ObjCRuntime::GCC:
Expand Down Expand Up @@ -210,7 +212,8 @@ static const EHPersonality &getObjCXXPersonality(const TargetInfo &Target,
return getObjCPersonality(Target, L);

case ObjCRuntime::GNUstep:
return EHPersonality::GNU_ObjCXX;
return Target.getTriple().isOSCygMing() ? EHPersonality::GNU_CPlusPlus_SEH
: EHPersonality::GNU_ObjCXX;

// The GCC runtime's personality function inherently doesn't support
// mixed EH. Use the ObjC personality just to avoid returning null.
Expand Down
340 changes: 334 additions & 6 deletions clang/lib/CodeGen/CGExpr.cpp

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions clang/lib/CodeGen/CGExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4564,6 +4564,12 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
if (LHSCondVal) { // If we have 1 && X, just emit X.
CGF.incrementProfileCounter(E);

// If the top of the logical operator nest, reset the MCDC temp to 0.
if (CGF.MCDCLogOpStack.empty())
CGF.maybeResetMCDCCondBitmap(E);

CGF.MCDCLogOpStack.push_back(E);

Value *RHSCond = CGF.EvaluateExprAsBool(E->getRHS());

// If we're generating for profiling or coverage, generate a branch to a
Expand All @@ -4572,6 +4578,7 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
// "FalseBlock" after the increment is done.
if (InstrumentRegions &&
CodeGenFunction::isInstrumentedCondition(E->getRHS())) {
CGF.maybeUpdateMCDCCondBitmap(E->getRHS(), RHSCond);
llvm::BasicBlock *FBlock = CGF.createBasicBlock("land.end");
llvm::BasicBlock *RHSBlockCnt = CGF.createBasicBlock("land.rhscnt");
Builder.CreateCondBr(RHSCond, RHSBlockCnt, FBlock);
Expand All @@ -4581,6 +4588,11 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
CGF.EmitBlock(FBlock);
}

CGF.MCDCLogOpStack.pop_back();
// If the top of the logical operator nest, update the MCDC bitmap.
if (CGF.MCDCLogOpStack.empty())
CGF.maybeUpdateMCDCTestVectorBitmap(E);

// ZExt result to int or bool.
return Builder.CreateZExtOrBitCast(RHSCond, ResTy, "land.ext");
}
Expand All @@ -4590,6 +4602,12 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
return llvm::Constant::getNullValue(ResTy);
}

// If the top of the logical operator nest, reset the MCDC temp to 0.
if (CGF.MCDCLogOpStack.empty())
CGF.maybeResetMCDCCondBitmap(E);

CGF.MCDCLogOpStack.push_back(E);

llvm::BasicBlock *ContBlock = CGF.createBasicBlock("land.end");
llvm::BasicBlock *RHSBlock = CGF.createBasicBlock("land.rhs");

Expand Down Expand Up @@ -4622,6 +4640,7 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
// condition coverage.
if (InstrumentRegions &&
CodeGenFunction::isInstrumentedCondition(E->getRHS())) {
CGF.maybeUpdateMCDCCondBitmap(E->getRHS(), RHSCond);
llvm::BasicBlock *RHSBlockCnt = CGF.createBasicBlock("land.rhscnt");
Builder.CreateCondBr(RHSCond, RHSBlockCnt, ContBlock);
CGF.EmitBlock(RHSBlockCnt);
Expand All @@ -4639,6 +4658,11 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
// Insert an entry into the phi node for the edge with the value of RHSCond.
PN->addIncoming(RHSCond, RHSBlock);

CGF.MCDCLogOpStack.pop_back();
// If the top of the logical operator nest, update the MCDC bitmap.
if (CGF.MCDCLogOpStack.empty())
CGF.maybeUpdateMCDCTestVectorBitmap(E);

// Artificial location to preserve the scope information
{
auto NL = ApplyDebugLocation::CreateArtificial(CGF);
Expand Down Expand Up @@ -4680,6 +4704,12 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
if (!LHSCondVal) { // If we have 0 || X, just emit X.
CGF.incrementProfileCounter(E);

// If the top of the logical operator nest, reset the MCDC temp to 0.
if (CGF.MCDCLogOpStack.empty())
CGF.maybeResetMCDCCondBitmap(E);

CGF.MCDCLogOpStack.push_back(E);

Value *RHSCond = CGF.EvaluateExprAsBool(E->getRHS());

// If we're generating for profiling or coverage, generate a branch to a
Expand All @@ -4688,6 +4718,7 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
// "FalseBlock" after the increment is done.
if (InstrumentRegions &&
CodeGenFunction::isInstrumentedCondition(E->getRHS())) {
CGF.maybeUpdateMCDCCondBitmap(E->getRHS(), RHSCond);
llvm::BasicBlock *FBlock = CGF.createBasicBlock("lor.end");
llvm::BasicBlock *RHSBlockCnt = CGF.createBasicBlock("lor.rhscnt");
Builder.CreateCondBr(RHSCond, FBlock, RHSBlockCnt);
Expand All @@ -4697,6 +4728,11 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
CGF.EmitBlock(FBlock);
}

CGF.MCDCLogOpStack.pop_back();
// If the top of the logical operator nest, update the MCDC bitmap.
if (CGF.MCDCLogOpStack.empty())
CGF.maybeUpdateMCDCTestVectorBitmap(E);

// ZExt result to int or bool.
return Builder.CreateZExtOrBitCast(RHSCond, ResTy, "lor.ext");
}
Expand All @@ -4706,6 +4742,12 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
return llvm::ConstantInt::get(ResTy, 1);
}

// If the top of the logical operator nest, reset the MCDC temp to 0.
if (CGF.MCDCLogOpStack.empty())
CGF.maybeResetMCDCCondBitmap(E);

CGF.MCDCLogOpStack.push_back(E);

llvm::BasicBlock *ContBlock = CGF.createBasicBlock("lor.end");
llvm::BasicBlock *RHSBlock = CGF.createBasicBlock("lor.rhs");

Expand Down Expand Up @@ -4742,6 +4784,7 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
// condition coverage.
if (InstrumentRegions &&
CodeGenFunction::isInstrumentedCondition(E->getRHS())) {
CGF.maybeUpdateMCDCCondBitmap(E->getRHS(), RHSCond);
llvm::BasicBlock *RHSBlockCnt = CGF.createBasicBlock("lor.rhscnt");
Builder.CreateCondBr(RHSCond, ContBlock, RHSBlockCnt);
CGF.EmitBlock(RHSBlockCnt);
Expand All @@ -4755,6 +4798,11 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
CGF.EmitBlock(ContBlock);
PN->addIncoming(RHSCond, RHSBlock);

CGF.MCDCLogOpStack.pop_back();
// If the top of the logical operator nest, update the MCDC bitmap.
if (CGF.MCDCLogOpStack.empty())
CGF.maybeUpdateMCDCTestVectorBitmap(E);

// ZExt result to int.
return Builder.CreateZExtOrBitCast(PN, ResTy, "lor.ext");
}
Expand Down Expand Up @@ -4899,6 +4947,10 @@ VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
return Builder.CreateSelect(CondV, LHS, RHS, "cond");
}

// If the top of the logical operator nest, reset the MCDC temp to 0.
if (CGF.MCDCLogOpStack.empty())
CGF.maybeResetMCDCCondBitmap(condExpr);

llvm::BasicBlock *LHSBlock = CGF.createBasicBlock("cond.true");
llvm::BasicBlock *RHSBlock = CGF.createBasicBlock("cond.false");
llvm::BasicBlock *ContBlock = CGF.createBasicBlock("cond.end");
Expand Down Expand Up @@ -4934,6 +4986,11 @@ VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
llvm::PHINode *PN = Builder.CreatePHI(LHS->getType(), 2, "cond");
PN->addIncoming(LHS, LHSBlock);
PN->addIncoming(RHS, RHSBlock);

// If the top of the logical operator nest, update the MCDC bitmap.
if (CGF.MCDCLogOpStack.empty())
CGF.maybeUpdateMCDCTestVectorBitmap(condExpr);

return PN;
}

Expand Down
37 changes: 25 additions & 12 deletions clang/lib/CodeGen/CGObjCGNU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ class CGObjCGNU : public CGObjCRuntime {
/// Does the current target use SEH-based exceptions? False implies
/// Itanium-style DWARF unwinding.
bool usesSEHExceptions;
/// Does the current target uses C++-based exceptions?
bool usesCxxExceptions;

/// Helper to check if we are targeting a specific runtime version or later.
bool isRuntime(ObjCRuntime::Kind kind, unsigned major, unsigned minor=0) {
Expand Down Expand Up @@ -819,12 +821,18 @@ class CGObjCGNUstep : public CGObjCGNU {
SlotLookupSuperFn.init(&CGM, "objc_slot_lookup_super", SlotTy,
PtrToObjCSuperTy, SelectorTy);
// If we're in ObjC++ mode, then we want to make
if (usesSEHExceptions) {
llvm::Type *VoidTy = llvm::Type::getVoidTy(VMContext);
// void objc_exception_rethrow(void)
ExceptionReThrowFn.init(&CGM, "objc_exception_rethrow", VoidTy);
llvm::Type *VoidTy = llvm::Type::getVoidTy(VMContext);
if (usesCxxExceptions) {
// void *__cxa_begin_catch(void *e)
EnterCatchFn.init(&CGM, "__cxa_begin_catch", PtrTy, PtrTy);
// void __cxa_end_catch(void)
ExitCatchFn.init(&CGM, "__cxa_end_catch", VoidTy);
// void objc_exception_rethrow(void*)
ExceptionReThrowFn.init(&CGM, "__cxa_rethrow", PtrTy);
} else if (usesSEHExceptions) {
// void objc_exception_rethrow(void)
ExceptionReThrowFn.init(&CGM, "objc_exception_rethrow", VoidTy);
} else if (CGM.getLangOpts().CPlusPlus) {
llvm::Type *VoidTy = llvm::Type::getVoidTy(VMContext);
// void *__cxa_begin_catch(void *e)
EnterCatchFn.init(&CGM, "__cxa_begin_catch", PtrTy, PtrTy);
// void __cxa_end_catch(void)
Expand All @@ -833,15 +841,13 @@ class CGObjCGNUstep : public CGObjCGNU {
ExceptionReThrowFn.init(&CGM, "_Unwind_Resume_or_Rethrow", VoidTy,
PtrTy);
} else if (R.getVersion() >= VersionTuple(1, 7)) {
llvm::Type *VoidTy = llvm::Type::getVoidTy(VMContext);
// id objc_begin_catch(void *e)
EnterCatchFn.init(&CGM, "objc_begin_catch", IdTy, PtrTy);
// void objc_end_catch(void)
ExitCatchFn.init(&CGM, "objc_end_catch", VoidTy);
// void _Unwind_Resume_or_Rethrow(void*)
ExceptionReThrowFn.init(&CGM, "objc_exception_rethrow", VoidTy, PtrTy);
}
llvm::Type *VoidTy = llvm::Type::getVoidTy(VMContext);
SetPropertyAtomic.init(&CGM, "objc_setProperty_atomic", VoidTy, IdTy,
SelectorTy, IdTy, PtrDiffTy);
SetPropertyAtomicCopy.init(&CGM, "objc_setProperty_atomic_copy", VoidTy,
Expand Down Expand Up @@ -1851,6 +1857,8 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {
llvm::GlobalValue::HiddenVisibility :
llvm::GlobalValue::DefaultVisibility;
OffsetVar->setVisibility(ivarVisibility);
if (ivarVisibility != llvm::GlobalValue::HiddenVisibility)
CGM.setGVProperties(OffsetVar, OID->getClassInterface());
ivarBuilder.add(OffsetVar);
// Ivar size
ivarBuilder.addInt(Int32Ty,
Expand Down Expand Up @@ -2124,6 +2132,9 @@ CGObjCGNU::CGObjCGNU(CodeGenModule &cgm, unsigned runtimeABIVersion,
msgSendMDKind = VMContext.getMDKindID("GNUObjCMessageSend");
usesSEHExceptions =
cgm.getContext().getTargetInfo().getTriple().isWindowsMSVCEnvironment();
usesCxxExceptions =
cgm.getContext().getTargetInfo().getTriple().isOSCygMing() &&
isRuntime(ObjCRuntime::GNUstep, 2);

CodeGenTypes &Types = CGM.getTypes();
IntTy = cast<llvm::IntegerType>(
Expand Down Expand Up @@ -2210,7 +2221,10 @@ CGObjCGNU::CGObjCGNU(CodeGenModule &cgm, unsigned runtimeABIVersion,

// void objc_exception_throw(id);
ExceptionThrowFn.init(&CGM, "objc_exception_throw", VoidTy, IdTy);
ExceptionReThrowFn.init(&CGM, "objc_exception_throw", VoidTy, IdTy);
ExceptionReThrowFn.init(&CGM,
usesCxxExceptions ? "objc_exception_rethrow"
: "objc_exception_throw",
VoidTy, IdTy);
// int objc_sync_enter(id);
SyncEnterFn.init(&CGM, "objc_sync_enter", IntTy, IdTy);
// int objc_sync_exit(id);
Expand Down Expand Up @@ -2387,7 +2401,7 @@ llvm::Constant *CGObjCGNUstep::GetEHType(QualType T) {
if (usesSEHExceptions)
return CGM.getCXXABI().getAddrOfRTTIDescriptor(T);

if (!CGM.getLangOpts().CPlusPlus)
if (!CGM.getLangOpts().CPlusPlus && !usesCxxExceptions)
return CGObjCGNU::GetEHType(T);

// For Objective-C++, we want to provide the ability to catch both C++ and
Expand Down Expand Up @@ -3993,7 +4007,7 @@ void CGObjCGNU::EmitThrowStmt(CodeGenFunction &CGF,
ExceptionAsObject = CGF.ObjCEHValueStack.back();
isRethrow = true;
}
if (isRethrow && usesSEHExceptions) {
if (isRethrow && (usesSEHExceptions || usesCxxExceptions)) {
// For SEH, ExceptionAsObject may be undef, because the catch handler is
// not passed it for catchalls and so it is not visible to the catch
// funclet. The real thrown object will still be live on the stack at this
Expand All @@ -4003,8 +4017,7 @@ void CGObjCGNU::EmitThrowStmt(CodeGenFunction &CGF,
// argument.
llvm::CallBase *Throw = CGF.EmitRuntimeCallOrInvoke(ExceptionReThrowFn);
Throw->setDoesNotReturn();
}
else {
} else {
ExceptionAsObject = CGF.Builder.CreateBitCast(ExceptionAsObject, IdTy);
llvm::CallBase *Throw =
CGF.EmitRuntimeCallOrInvoke(ExceptionThrowFn, ExceptionAsObject);
Expand Down
14 changes: 13 additions & 1 deletion clang/lib/CodeGen/CGStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,19 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
if (!ThenCount && !getCurrentProfileCount() &&
CGM.getCodeGenOpts().OptimizationLevel)
LH = Stmt::getLikelihood(S.getThen(), S.getElse());
EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH);

// When measuring MC/DC, always fully evaluate the condition up front using
// EvaluateExprAsBool() so that the test vector bitmap can be updated prior to
// executing the body of the if.then or if.else. This is useful for when
// there is a 'return' within the body, but this is particularly beneficial
// when one if-stmt is nested within another if-stmt so that all of the MC/DC
// updates are kept linear and consistent.
if (!CGM.getCodeGenOpts().MCDCCoverage)
EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH);
else {
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
Builder.CreateCondBr(BoolCondVal, ThenBlock, ElseBlock);
}

// Emit the 'then' code.
EmitBlock(ThenBlock);
Expand Down
98 changes: 71 additions & 27 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1256,6 +1256,7 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,

void CodeGenFunction::EmitFunctionBody(const Stmt *Body) {
incrementProfileCounter(Body);
maybeCreateMCDCCondBitmap();
if (const CompoundStmt *S = dyn_cast<CompoundStmt>(Body))
EmitCompoundStmtWithoutScope(*S);
else
Expand Down Expand Up @@ -1601,6 +1602,13 @@ bool CodeGenFunction::mightAddDeclToScope(const Stmt *S) {
bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond,
bool &ResultBool,
bool AllowLabels) {
// If MC/DC is enabled, disable folding so that we can instrument all
// conditions to yield complete test vectors. We still keep track of
// folded conditions during region mapping and visualization.
if (!AllowLabels && CGM.getCodeGenOpts().hasProfileClangInstr() &&
CGM.getCodeGenOpts().MCDCCoverage)
return false;

llvm::APSInt ResultInt;
if (!ConstantFoldsToSimpleInteger(Cond, ResultInt, AllowLabels))
return false;
Expand Down Expand Up @@ -1629,16 +1637,20 @@ bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond,
return true;
}

/// Strip parentheses and simplistic logical-NOT operators.
const Expr *CodeGenFunction::stripCond(const Expr *C) {
while (const UnaryOperator *Op = dyn_cast<UnaryOperator>(C->IgnoreParens())) {
if (Op->getOpcode() != UO_LNot)
break;
C = Op->getSubExpr();
}
return C->IgnoreParens();
}

/// Determine whether the given condition is an instrumentable condition
/// (i.e. no "&&" or "||").
bool CodeGenFunction::isInstrumentedCondition(const Expr *C) {
// Bypass simplistic logical-NOT operator before determining whether the
// condition contains any other logical operator.
if (const UnaryOperator *UnOp = dyn_cast<UnaryOperator>(C->IgnoreParens()))
if (UnOp->getOpcode() == UO_LNot)
C = UnOp->getSubExpr();

const BinaryOperator *BOp = dyn_cast<BinaryOperator>(C->IgnoreParens());
const BinaryOperator *BOp = dyn_cast<BinaryOperator>(stripCond(C));
return (!BOp || !BOp->isLogicalOp());
}

Expand Down Expand Up @@ -1717,35 +1729,41 @@ void CodeGenFunction::EmitBranchToCounterBlock(
/// statement) to the specified blocks. Based on the condition, this might try
/// to simplify the codegen of the conditional based on the branch.
/// \param LH The value of the likelihood attribute on the True branch.
void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
llvm::BasicBlock *TrueBlock,
llvm::BasicBlock *FalseBlock,
uint64_t TrueCount,
Stmt::Likelihood LH) {
/// \param ConditionalOp Used by MC/DC code coverage to track the result of the
/// ConditionalOperator (ternary) through a recursive call for the operator's
/// LHS and RHS nodes.
void CodeGenFunction::EmitBranchOnBoolExpr(
const Expr *Cond, llvm::BasicBlock *TrueBlock, llvm::BasicBlock *FalseBlock,
uint64_t TrueCount, Stmt::Likelihood LH, const Expr *ConditionalOp) {
Cond = Cond->IgnoreParens();

if (const BinaryOperator *CondBOp = dyn_cast<BinaryOperator>(Cond)) {

// Handle X && Y in a condition.
if (CondBOp->getOpcode() == BO_LAnd) {
MCDCLogOpStack.push_back(CondBOp);

// If we have "1 && X", simplify the code. "0 && X" would have constant
// folded if the case was simple enough.
bool ConstantBool = false;
if (ConstantFoldsToSimpleInteger(CondBOp->getLHS(), ConstantBool) &&
ConstantBool) {
// br(1 && X) -> br(X).
incrementProfileCounter(CondBOp);
return EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LAnd, TrueBlock,
FalseBlock, TrueCount, LH);
EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LAnd, TrueBlock,
FalseBlock, TrueCount, LH);
MCDCLogOpStack.pop_back();
return;
}

// If we have "X && 1", simplify the code to use an uncond branch.
// "X && 0" would have been constant folded to 0.
if (ConstantFoldsToSimpleInteger(CondBOp->getRHS(), ConstantBool) &&
ConstantBool) {
// br(X && 1) -> br(X).
return EmitBranchToCounterBlock(CondBOp->getLHS(), BO_LAnd, TrueBlock,
FalseBlock, TrueCount, LH, CondBOp);
EmitBranchToCounterBlock(CondBOp->getLHS(), BO_LAnd, TrueBlock,
FalseBlock, TrueCount, LH, CondBOp);
MCDCLogOpStack.pop_back();
return;
}

// Emit the LHS as a conditional. If the LHS conditional is false, we
Expand Down Expand Up @@ -1774,31 +1792,36 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LAnd, TrueBlock,
FalseBlock, TrueCount, LH);
eval.end(*this);

MCDCLogOpStack.pop_back();
return;
}

if (CondBOp->getOpcode() == BO_LOr) {
MCDCLogOpStack.push_back(CondBOp);

// If we have "0 || X", simplify the code. "1 || X" would have constant
// folded if the case was simple enough.
bool ConstantBool = false;
if (ConstantFoldsToSimpleInteger(CondBOp->getLHS(), ConstantBool) &&
!ConstantBool) {
// br(0 || X) -> br(X).
incrementProfileCounter(CondBOp);
return EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LOr, TrueBlock,
FalseBlock, TrueCount, LH);
EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LOr, TrueBlock,
FalseBlock, TrueCount, LH);
MCDCLogOpStack.pop_back();
return;
}

// If we have "X || 0", simplify the code to use an uncond branch.
// "X || 1" would have been constant folded to 1.
if (ConstantFoldsToSimpleInteger(CondBOp->getRHS(), ConstantBool) &&
!ConstantBool) {
// br(X || 0) -> br(X).
return EmitBranchToCounterBlock(CondBOp->getLHS(), BO_LOr, TrueBlock,
FalseBlock, TrueCount, LH, CondBOp);
EmitBranchToCounterBlock(CondBOp->getLHS(), BO_LOr, TrueBlock,
FalseBlock, TrueCount, LH, CondBOp);
MCDCLogOpStack.pop_back();
return;
}

// Emit the LHS as a conditional. If the LHS conditional is true, we
// want to jump to the TrueBlock.
llvm::BasicBlock *LHSFalse = createBasicBlock("lor.lhs.false");
Expand Down Expand Up @@ -1829,14 +1852,20 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
RHSCount, LH);

eval.end(*this);

MCDCLogOpStack.pop_back();
return;
}
}

if (const UnaryOperator *CondUOp = dyn_cast<UnaryOperator>(Cond)) {
// br(!x, t, f) -> br(x, f, t)
if (CondUOp->getOpcode() == UO_LNot) {
// Avoid doing this optimization when instrumenting a condition for MC/DC.
// LNot is taken as part of the condition for simplicity, and changing its
// sense negatively impacts test vector tracking.
bool MCDCCondition = CGM.getCodeGenOpts().hasProfileClangInstr() &&
CGM.getCodeGenOpts().MCDCCoverage &&
isInstrumentedCondition(Cond);
if (CondUOp->getOpcode() == UO_LNot && !MCDCCondition) {
// Negate the count.
uint64_t FalseCount = getCurrentProfileCount() - TrueCount;
// The values of the enum are chosen to make this negation possible.
Expand Down Expand Up @@ -1876,14 +1905,14 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
{
ApplyDebugLocation DL(*this, Cond);
EmitBranchOnBoolExpr(CondOp->getLHS(), TrueBlock, FalseBlock,
LHSScaledTrueCount, LH);
LHSScaledTrueCount, LH, CondOp);
}
cond.end(*this);

cond.begin(*this);
EmitBlock(RHSBlock);
EmitBranchOnBoolExpr(CondOp->getRHS(), TrueBlock, FalseBlock,
TrueCount - LHSScaledTrueCount, LH);
TrueCount - LHSScaledTrueCount, LH, CondOp);
cond.end(*this);

return;
Expand All @@ -1906,6 +1935,21 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
CondV = EvaluateExprAsBool(Cond);
}

// If not at the top of the logical operator nest, update MCDC temp with the
// boolean result of the evaluated condition.
if (!MCDCLogOpStack.empty()) {
const Expr *MCDCBaseExpr = Cond;
// When a nested ConditionalOperator (ternary) is encountered in a boolean
// expression, MC/DC tracks the result of the ternary, and this is tied to
// the ConditionalOperator expression and not the ternary's LHS or RHS. If
// this is the case, the ConditionalOperator expression is passed through
// the ConditionalOp parameter and then used as the MCDC base expression.
if (ConditionalOp)
MCDCBaseExpr = ConditionalOp;

maybeUpdateMCDCCondBitmap(MCDCBaseExpr, CondV);
}

llvm::MDNode *Weights = nullptr;
llvm::MDNode *Unpredictable = nullptr;

Expand Down
80 changes: 79 additions & 1 deletion clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@ class CodeGenFunction : public CodeGenTypeCache {
/// nest would extend.
SmallVector<llvm::CanonicalLoopInfo *, 4> OMPLoopNestStack;

/// Stack to track the Logical Operator recursion nest for MC/DC.
SmallVector<const BinaryOperator *, 16> MCDCLogOpStack;

/// Number of nested loop to be consumed by the last surrounding
/// loop-associated directive.
int ExpectedOMPLoopDepth = 0;
Expand Down Expand Up @@ -1521,6 +1524,9 @@ class CodeGenFunction : public CodeGenTypeCache {

CodeGenPGO PGO;

/// Bitmap used by MC/DC to track condition outcomes of a boolean expression.
Address MCDCCondBitmapAddr = Address::invalid();

/// Calculate branch weights appropriate for PGO data
llvm::MDNode *createProfileWeights(uint64_t TrueCount,
uint64_t FalseCount) const;
Expand All @@ -1539,6 +1545,52 @@ class CodeGenFunction : public CodeGenTypeCache {
PGO.setCurrentStmt(S);
}

bool isMCDCCoverageEnabled() const {
return (CGM.getCodeGenOpts().hasProfileClangInstr() &&
CGM.getCodeGenOpts().MCDCCoverage &&
!CurFn->hasFnAttribute(llvm::Attribute::NoProfile));
}

/// Allocate a temp value on the stack that MCDC can use to track condition
/// results.
void maybeCreateMCDCCondBitmap() {
if (isMCDCCoverageEnabled()) {
PGO.emitMCDCParameters(Builder);
MCDCCondBitmapAddr =
CreateIRTemp(getContext().UnsignedIntTy, "mcdc.addr");
}
}

bool isBinaryLogicalOp(const Expr *E) const {
const BinaryOperator *BOp = dyn_cast<BinaryOperator>(E->IgnoreParens());
return (BOp && BOp->isLogicalOp());
}

/// Zero-init the MCDC temp value.
void maybeResetMCDCCondBitmap(const Expr *E) {
if (isMCDCCoverageEnabled() && isBinaryLogicalOp(E)) {
PGO.emitMCDCCondBitmapReset(Builder, E, MCDCCondBitmapAddr);
PGO.setCurrentStmt(E);
}
}

/// Increment the profiler's counter for the given expression by \p StepV.
/// If \p StepV is null, the default increment is 1.
void maybeUpdateMCDCTestVectorBitmap(const Expr *E) {
if (isMCDCCoverageEnabled() && isBinaryLogicalOp(E)) {
PGO.emitMCDCTestVectorBitmapUpdate(Builder, E, MCDCCondBitmapAddr);
PGO.setCurrentStmt(E);
}
}

/// Update the MCDC temp value with the condition's evaluated result.
void maybeUpdateMCDCCondBitmap(const Expr *E, llvm::Value *Val) {
if (isMCDCCoverageEnabled()) {
PGO.emitMCDCCondBitmapUpdate(Builder, E, MCDCCondBitmapAddr, Val);
PGO.setCurrentStmt(E);
}
}

/// Get the profiler's count for the given statement.
uint64_t getProfileCount(const Stmt *S) {
return PGO.getStmtCount(S).value_or(0);
Expand Down Expand Up @@ -3021,6 +3073,25 @@ class CodeGenFunction : public CodeGenTypeCache {
/// this expression is used as an lvalue, for instance in "&Arr[Idx]".
void EmitBoundsCheck(const Expr *E, const Expr *Base, llvm::Value *Index,
QualType IndexType, bool Accessed);
void EmitBoundsCheckImpl(const Expr *E, llvm::Value *Bound,
llvm::Value *Index, QualType IndexType,
QualType IndexedType, bool Accessed);

// Find a struct's flexible array member. It may be embedded inside multiple
// sub-structs, but must still be the last field.
const FieldDecl *FindFlexibleArrayMemberField(ASTContext &Ctx,
const RecordDecl *RD,
StringRef Name,
uint64_t &Offset);

/// Find the FieldDecl specified in a FAM's "counted_by" attribute. Returns
/// \p nullptr if either the attribute or the field doesn't exist.
const FieldDecl *FindCountedByField(const FieldDecl *FD);

/// Build an expression accessing the "counted_by" field.
llvm::Value *EmitCountedByFieldExpr(const Expr *Base,
const FieldDecl *FAMDecl,
const FieldDecl *CountDecl);

llvm::Value *EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
bool isInc, bool isPre);
Expand Down Expand Up @@ -4626,6 +4697,9 @@ class CodeGenFunction : public CodeGenTypeCache {
bool ConstantFoldsToSimpleInteger(const Expr *Cond, llvm::APSInt &Result,
bool AllowLabels = false);

/// Ignore parentheses and logical-NOT to track conditions consistently.
static const Expr *stripCond(const Expr *C);

/// isInstrumentedCondition - Determine whether the given condition is an
/// instrumentable condition (i.e. no "&&" or "||").
static bool isInstrumentedCondition(const Expr *C);
Expand All @@ -4648,7 +4722,8 @@ class CodeGenFunction : public CodeGenTypeCache {
/// evaluate to true based on PGO data.
void EmitBranchOnBoolExpr(const Expr *Cond, llvm::BasicBlock *TrueBlock,
llvm::BasicBlock *FalseBlock, uint64_t TrueCount,
Stmt::Likelihood LH = Stmt::LH_None);
Stmt::Likelihood LH = Stmt::LH_None,
const Expr *ConditionalOp = nullptr);

/// Given an assignment `*LHS = RHS`, emit a test that checks if \p RHS is
/// nonnull, if \p LHS is marked _Nonnull.
Expand Down Expand Up @@ -4817,6 +4892,9 @@ class CodeGenFunction : public CodeGenTypeCache {
llvm::Value *EmittedE,
bool IsDynamic);

llvm::Value *emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
llvm::IntegerType *ResType);

void emitZeroOrPatternForAutoVarInit(QualType type, const VarDecl &D,
Address Loc);

Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,8 @@ void CodeGenModule::Release() {
if (LangOpts.BranchProtectionPAuthLR)
getModule().addModuleFlag(llvm::Module::Min, "branch-protection-pauth-lr",
1);
if (LangOpts.GuardedControlStack)
getModule().addModuleFlag(llvm::Module::Min, "guarded-control-stack", 1);
if (LangOpts.hasSignReturnAddress())
getModule().addModuleFlag(llvm::Module::Min, "sign-return-address", 1);
if (LangOpts.isSignReturnAddressScopeAll())
Expand Down Expand Up @@ -4869,6 +4871,10 @@ CodeGenModule::GetOrCreateLLVMGlobal(StringRef MangledName, llvm::Type *Ty,
isExternallyVisible(D->getLinkageAndVisibility().getLinkage()))
GV->setSection(".cp.rodata");

// Handle code model attribute
if (const auto *CMA = D->getAttr<CodeModelAttr>())
GV->setCodeModel(CMA->getModel());

// Check if we a have a const declaration with an initializer, we may be
// able to emit it as available_externally to expose it's value to the
// optimizer.
Expand Down
263 changes: 252 additions & 11 deletions clang/lib/CodeGen/CodeGenPGO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,24 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
PGOHash Hash;
/// The map of statements to counters.
llvm::DenseMap<const Stmt *, unsigned> &CounterMap;
/// The next bitmap byte index to assign.
unsigned NextMCDCBitmapIdx;
/// The map of statements to MC/DC bitmap coverage objects.
llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap;
/// Maximum number of supported MC/DC conditions in a boolean expression.
unsigned MCDCMaxCond;
/// The profile version.
uint64_t ProfileVersion;
/// Diagnostics Engine used to report warnings.
DiagnosticsEngine &Diag;

MapRegionCounters(PGOHashVersion HashVersion, uint64_t ProfileVersion,
llvm::DenseMap<const Stmt *, unsigned> &CounterMap)
llvm::DenseMap<const Stmt *, unsigned> &CounterMap,
llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap,
unsigned MCDCMaxCond, DiagnosticsEngine &Diag)
: NextCounter(0), Hash(HashVersion), CounterMap(CounterMap),
ProfileVersion(ProfileVersion) {}
NextMCDCBitmapIdx(0), MCDCBitmapMap(MCDCBitmapMap),
MCDCMaxCond(MCDCMaxCond), ProfileVersion(ProfileVersion), Diag(Diag) {}

// Blocks and lambdas are handled as separate functions, so we need not
// traverse them in the parent context.
Expand Down Expand Up @@ -207,15 +218,126 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
return Type;
}

/// The following stacks are used with dataTraverseStmtPre() and
/// dataTraverseStmtPost() to track the depth of nested logical operators in a
/// boolean expression in a function. The ultimate purpose is to keep track
/// of the number of leaf-level conditions in the boolean expression so that a
/// profile bitmap can be allocated based on that number.
///
/// The stacks are also used to find error cases and notify the user. A
/// standard logical operator nest for a boolean expression could be in a form
/// similar to this: "x = a && b && c && (d || f)"
unsigned NumCond = 0;
bool SplitNestedLogicalOp = false;
SmallVector<const Stmt *, 16> NonLogOpStack;
SmallVector<const BinaryOperator *, 16> LogOpStack;

// Hook: dataTraverseStmtPre() is invoked prior to visiting an AST Stmt node.
bool dataTraverseStmtPre(Stmt *S) {
/// If MC/DC is not enabled, MCDCMaxCond will be set to 0. Do nothing.
if (MCDCMaxCond == 0)
return true;

/// At the top of the logical operator nest, reset the number of conditions.
if (LogOpStack.empty())
NumCond = 0;

if (const Expr *E = dyn_cast<Expr>(S)) {
const BinaryOperator *BinOp = dyn_cast<BinaryOperator>(E->IgnoreParens());
if (BinOp && BinOp->isLogicalOp()) {
/// Check for "split-nested" logical operators. This happens when a new
/// boolean expression logical-op nest is encountered within an existing
/// boolean expression, separated by a non-logical operator. For
/// example, in "x = (a && b && c && foo(d && f))", the "d && f" case
/// starts a new boolean expression that is separated from the other
/// conditions by the operator foo(). Split-nested cases are not
/// supported by MC/DC.
SplitNestedLogicalOp = SplitNestedLogicalOp || !NonLogOpStack.empty();

LogOpStack.push_back(BinOp);
return true;
}
}

/// Keep track of non-logical operators. These are OK as long as we don't
/// encounter a new logical operator after seeing one.
if (!LogOpStack.empty())
NonLogOpStack.push_back(S);

return true;
}

// Hook: dataTraverseStmtPost() is invoked by the AST visitor after visiting
// an AST Stmt node. MC/DC will use it to to signal when the top of a
// logical operation (boolean expression) nest is encountered.
bool dataTraverseStmtPost(Stmt *S) {
/// If MC/DC is not enabled, MCDCMaxCond will be set to 0. Do nothing.
if (MCDCMaxCond == 0)
return true;

if (const Expr *E = dyn_cast<Expr>(S)) {
const BinaryOperator *BinOp = dyn_cast<BinaryOperator>(E->IgnoreParens());
if (BinOp && BinOp->isLogicalOp()) {
assert(LogOpStack.back() == BinOp);
LogOpStack.pop_back();

/// At the top of logical operator nest:
if (LogOpStack.empty()) {
/// Was the "split-nested" logical operator case encountered?
if (SplitNestedLogicalOp) {
unsigned DiagID = Diag.getCustomDiagID(
DiagnosticsEngine::Warning,
"unsupported MC/DC boolean expression; "
"contains an operation with a nested boolean expression. "
"Expression will not be covered");
Diag.Report(S->getBeginLoc(), DiagID);
return false;
}

/// Was the maximum number of conditions encountered?
if (NumCond > MCDCMaxCond) {
unsigned DiagID = Diag.getCustomDiagID(
DiagnosticsEngine::Warning,
"unsupported MC/DC boolean expression; "
"number of conditions (%0) exceeds max (%1). "
"Expression will not be covered");
Diag.Report(S->getBeginLoc(), DiagID) << NumCond << MCDCMaxCond;
return false;
}

// Otherwise, allocate the number of bytes required for the bitmap
// based on the number of conditions. Must be at least 1-byte long.
MCDCBitmapMap[BinOp] = NextMCDCBitmapIdx;
unsigned SizeInBits = std::max<unsigned>(1L << NumCond, CHAR_BIT);
NextMCDCBitmapIdx += SizeInBits / CHAR_BIT;
}
return true;
}
}

if (!LogOpStack.empty())
NonLogOpStack.pop_back();

return true;
}

/// The RHS of all logical operators gets a fresh counter in order to count
/// how many times the RHS evaluates to true or false, depending on the
/// semantics of the operator. This is only valid for ">= v7" of the profile
/// version so that we facilitate backward compatibility.
/// version so that we facilitate backward compatibility. In addition, in
/// order to use MC/DC, count the number of total LHS and RHS conditions.
bool VisitBinaryOperator(BinaryOperator *S) {
if (ProfileVersion >= llvm::IndexedInstrProf::Version7)
if (S->isLogicalOp() &&
CodeGenFunction::isInstrumentedCondition(S->getRHS()))
CounterMap[S->getRHS()] = NextCounter++;
if (S->isLogicalOp()) {
if (CodeGenFunction::isInstrumentedCondition(S->getLHS()))
NumCond++;

if (CodeGenFunction::isInstrumentedCondition(S->getRHS())) {
if (ProfileVersion >= llvm::IndexedInstrProf::Version7)
CounterMap[S->getRHS()] = NextCounter++;

NumCond++;
}
}
return Base::VisitBinaryOperator(S);
}

Expand Down Expand Up @@ -851,8 +973,22 @@ void CodeGenPGO::mapRegionCounters(const Decl *D) {
ProfileVersion = PGOReader->getVersion();
}

// If MC/DC is enabled, set the MaxConditions to a preset value. Otherwise,
// set it to zero. This value impacts the number of conditions accepted in a
// given boolean expression, which impacts the size of the bitmap used to
// track test vector execution for that boolean expression. Because the
// bitmap scales exponentially (2^n) based on the number of conditions seen,
// the maximum value is hard-coded at 6 conditions, which is more than enough
// for most embedded applications. Setting a maximum value prevents the
// bitmap footprint from growing too large without the user's knowledge. In
// the future, this value could be adjusted with a command-line option.
unsigned MCDCMaxConditions = (CGM.getCodeGenOpts().MCDCCoverage) ? 6 : 0;

RegionCounterMap.reset(new llvm::DenseMap<const Stmt *, unsigned>);
MapRegionCounters Walker(HashVersion, ProfileVersion, *RegionCounterMap);
RegionMCDCBitmapMap.reset(new llvm::DenseMap<const Stmt *, unsigned>);
MapRegionCounters Walker(HashVersion, ProfileVersion, *RegionCounterMap,
*RegionMCDCBitmapMap, MCDCMaxConditions,
CGM.getDiags());
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D))
Walker.TraverseDecl(const_cast<FunctionDecl *>(FD));
else if (const ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(D))
Expand All @@ -863,6 +999,7 @@ void CodeGenPGO::mapRegionCounters(const Decl *D) {
Walker.TraverseDecl(const_cast<CapturedDecl *>(CD));
assert(Walker.NextCounter > 0 && "no entry counter mapped for decl");
NumRegionCounters = Walker.NextCounter;
MCDCBitmapBytes = Walker.NextMCDCBitmapIdx;
FunctionHash = Walker.Hash.finalize();
}

Expand Down Expand Up @@ -894,9 +1031,11 @@ void CodeGenPGO::emitCounterRegionMapping(const Decl *D) {

std::string CoverageMapping;
llvm::raw_string_ostream OS(CoverageMapping);
CoverageMappingGen MappingGen(*CGM.getCoverageMapping(),
CGM.getContext().getSourceManager(),
CGM.getLangOpts(), RegionCounterMap.get());
RegionCondIDMap.reset(new llvm::DenseMap<const Stmt *, unsigned>);
CoverageMappingGen MappingGen(
*CGM.getCoverageMapping(), CGM.getContext().getSourceManager(),
CGM.getLangOpts(), RegionCounterMap.get(), RegionMCDCBitmapMap.get(),
RegionCondIDMap.get());
MappingGen.emitCounterMapping(D, OS);
OS.flush();

Expand Down Expand Up @@ -972,6 +1111,108 @@ void CodeGenPGO::emitCounterIncrement(CGBuilderTy &Builder, const Stmt *S,
ArrayRef(Args));
}

bool CodeGenPGO::canEmitMCDCCoverage(const CGBuilderTy &Builder) {
return (CGM.getCodeGenOpts().hasProfileClangInstr() &&
CGM.getCodeGenOpts().MCDCCoverage && Builder.GetInsertBlock());
}

void CodeGenPGO::emitMCDCParameters(CGBuilderTy &Builder) {
if (!canEmitMCDCCoverage(Builder) || !RegionMCDCBitmapMap)
return;

auto *I8PtrTy = llvm::PointerType::getUnqual(CGM.getLLVMContext());

// Emit intrinsic representing MCDC bitmap parameters at function entry.
// This is used by the instrumentation pass, but it isn't actually lowered to
// anything.
llvm::Value *Args[3] = {llvm::ConstantExpr::getBitCast(FuncNameVar, I8PtrTy),
Builder.getInt64(FunctionHash),
Builder.getInt32(MCDCBitmapBytes)};
Builder.CreateCall(
CGM.getIntrinsic(llvm::Intrinsic::instrprof_mcdc_parameters), Args);
}

void CodeGenPGO::emitMCDCTestVectorBitmapUpdate(CGBuilderTy &Builder,
const Expr *S,
Address MCDCCondBitmapAddr) {
if (!canEmitMCDCCoverage(Builder) || !RegionMCDCBitmapMap)
return;

S = S->IgnoreParens();

auto ExprMCDCBitmapMapIterator = RegionMCDCBitmapMap->find(S);
if (ExprMCDCBitmapMapIterator == RegionMCDCBitmapMap->end())
return;

// Extract the ID of the global bitmap associated with this expression.
unsigned MCDCTestVectorBitmapID = ExprMCDCBitmapMapIterator->second;
auto *I8PtrTy = llvm::PointerType::getUnqual(CGM.getLLVMContext());

// Emit intrinsic responsible for updating the global bitmap corresponding to
// a boolean expression. The index being set is based on the value loaded
// from a pointer to a dedicated temporary value on the stack that is itself
// updated via emitMCDCCondBitmapReset() and emitMCDCCondBitmapUpdate(). The
// index represents an executed test vector.
llvm::Value *Args[5] = {llvm::ConstantExpr::getBitCast(FuncNameVar, I8PtrTy),
Builder.getInt64(FunctionHash),
Builder.getInt32(MCDCBitmapBytes),
Builder.getInt32(MCDCTestVectorBitmapID),
MCDCCondBitmapAddr.getPointer()};
Builder.CreateCall(
CGM.getIntrinsic(llvm::Intrinsic::instrprof_mcdc_tvbitmap_update), Args);
}

void CodeGenPGO::emitMCDCCondBitmapReset(CGBuilderTy &Builder, const Expr *S,
Address MCDCCondBitmapAddr) {
if (!canEmitMCDCCoverage(Builder) || !RegionMCDCBitmapMap)
return;

S = S->IgnoreParens();

if (RegionMCDCBitmapMap->find(S) == RegionMCDCBitmapMap->end())
return;

// Emit intrinsic that resets a dedicated temporary value on the stack to 0.
Builder.CreateStore(Builder.getInt32(0), MCDCCondBitmapAddr);
}

void CodeGenPGO::emitMCDCCondBitmapUpdate(CGBuilderTy &Builder, const Expr *S,
Address MCDCCondBitmapAddr,
llvm::Value *Val) {
if (!canEmitMCDCCoverage(Builder) || !RegionCondIDMap)
return;

// Even though, for simplicity, parentheses and unary logical-NOT operators
// are considered part of their underlying condition for both MC/DC and
// branch coverage, the condition IDs themselves are assigned and tracked
// using the underlying condition itself. This is done solely for
// consistency since parentheses and logical-NOTs are ignored when checking
// whether the condition is actually an instrumentable condition. This can
// also make debugging a bit easier.
S = CodeGenFunction::stripCond(S);

auto ExprMCDCConditionIDMapIterator = RegionCondIDMap->find(S);
if (ExprMCDCConditionIDMapIterator == RegionCondIDMap->end())
return;

// Extract the ID of the condition we are setting in the bitmap.
unsigned CondID = ExprMCDCConditionIDMapIterator->second;
assert(CondID > 0 && "Condition has no ID!");

auto *I8PtrTy = llvm::PointerType::getUnqual(CGM.getLLVMContext());

// Emit intrinsic that updates a dedicated temporary value on the stack after
// a condition is evaluated. After the set of conditions has been updated,
// the resulting value is used to update the boolean expression's bitmap.
llvm::Value *Args[5] = {llvm::ConstantExpr::getBitCast(FuncNameVar, I8PtrTy),
Builder.getInt64(FunctionHash),
Builder.getInt32(CondID - 1),
MCDCCondBitmapAddr.getPointer(), Val};
Builder.CreateCall(
CGM.getIntrinsic(llvm::Intrinsic::instrprof_mcdc_condbitmap_update),
Args);
}

void CodeGenPGO::setValueProfilingFlag(llvm::Module &M) {
if (CGM.getCodeGenOpts().hasProfileClangInstr())
M.addModuleFlag(llvm::Module::Warning, "EnableValueProfiling",
Expand Down
14 changes: 13 additions & 1 deletion clang/lib/CodeGen/CodeGenPGO.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ class CodeGenPGO {

std::array <unsigned, llvm::IPVK_Last + 1> NumValueSites;
unsigned NumRegionCounters;
unsigned MCDCBitmapBytes;
uint64_t FunctionHash;
std::unique_ptr<llvm::DenseMap<const Stmt *, unsigned>> RegionCounterMap;
std::unique_ptr<llvm::DenseMap<const Stmt *, unsigned>> RegionMCDCBitmapMap;
std::unique_ptr<llvm::DenseMap<const Stmt *, unsigned>> RegionCondIDMap;
std::unique_ptr<llvm::DenseMap<const Stmt *, uint64_t>> StmtCountMap;
std::unique_ptr<llvm::InstrProfRecord> ProfRecord;
std::vector<uint64_t> RegionCounts;
Expand All @@ -43,7 +46,8 @@ class CodeGenPGO {
public:
CodeGenPGO(CodeGenModule &CGModule)
: CGM(CGModule), FuncNameVar(nullptr), NumValueSites({{0}}),
NumRegionCounters(0), FunctionHash(0), CurrentRegionCount(0) {}
NumRegionCounters(0), MCDCBitmapBytes(0), FunctionHash(0),
CurrentRegionCount(0) {}

/// Whether or not we have PGO region data for the current function. This is
/// false both when we have no data at all and when our data has been
Expand Down Expand Up @@ -103,10 +107,18 @@ class CodeGenPGO {
bool IsInMainFile);
bool skipRegionMappingForDecl(const Decl *D);
void emitCounterRegionMapping(const Decl *D);
bool canEmitMCDCCoverage(const CGBuilderTy &Builder);

public:
void emitCounterIncrement(CGBuilderTy &Builder, const Stmt *S,
llvm::Value *StepV);
void emitMCDCTestVectorBitmapUpdate(CGBuilderTy &Builder, const Expr *S,
Address MCDCCondBitmapAddr);
void emitMCDCParameters(CGBuilderTy &Builder);
void emitMCDCCondBitmapReset(CGBuilderTy &Builder, const Expr *S,
Address MCDCCondBitmapAddr);
void emitMCDCCondBitmapUpdate(CGBuilderTy &Builder, const Expr *S,
Address MCDCCondBitmapAddr, llvm::Value *Val);

/// Return the region count for the counter at the given index.
uint64_t getRegionCount(const Stmt *S) {
Expand Down
Loading