67 changes: 40 additions & 27 deletions clang/lib/AST/Interp/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1386,18 +1386,8 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,

if (R->isUnion()) {
if (Inits.size() == 0) {
// Zero-initialize the first union field.
if (R->getNumFields() == 0)
return this->emitFinishInit(E);
const Record::Field *FieldToInit = R->getField(0u);
QualType FieldType = FieldToInit->Desc->getType();
if (std::optional<PrimType> T = classify(FieldType)) {
if (!this->visitZeroInitializer(*T, FieldType, E))
return false;
if (!this->emitInitField(*T, FieldToInit->Offset, E))
return false;
}
// FIXME: Non-primitive case?
if (!this->visitZeroRecordInitializer(R, E))
return false;
} else {
const Expr *Init = Inits[0];
const FieldDecl *FToInit = nullptr;
Expand Down Expand Up @@ -3366,6 +3356,9 @@ bool Compiler<Emitter>::visitZeroRecordInitializer(const Record *R,
assert(R);
// Fields
for (const Record::Field &Field : R->fields()) {
if (Field.Decl->isUnnamedBitField())
continue;

const Descriptor *D = Field.Desc;
if (D->isPrimitive()) {
QualType QT = D->getType();
Expand All @@ -3374,6 +3367,8 @@ bool Compiler<Emitter>::visitZeroRecordInitializer(const Record *R,
return false;
if (!this->emitInitField(T, Field.Offset, E))
return false;
if (R->isUnion())
break;
continue;
}

Expand Down Expand Up @@ -3409,8 +3404,11 @@ bool Compiler<Emitter>::visitZeroRecordInitializer(const Record *R,
assert(false);
}

if (!this->emitPopPtr(E))
if (!this->emitFinishInitPop(E))
return false;

if (R->isUnion())
break;
}

for (const Record::Base &B : R->bases()) {
Expand Down Expand Up @@ -4762,7 +4760,7 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
if (!this->visitInitializer(InitExpr))
return false;

return this->emitPopPtr(InitExpr);
return this->emitFinishInitPop(InitExpr);
};

// Emit custom code if this is a lambda static invoker.
Expand All @@ -4777,6 +4775,22 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
if (!R)
return false;

if (R->isUnion() && Ctor->isCopyOrMoveConstructor()) {
// union copy and move ctors are special.
assert(cast<CompoundStmt>(Ctor->getBody())->body_empty());
if (!this->emitThis(Ctor))
return false;

auto PVD = Ctor->getParamDecl(0);
ParamOffset PO = this->Params[PVD]; // Must exist.

if (!this->emitGetParam(PT_Ptr, PO.Offset, Ctor))
return false;

return this->emitMemcpy(Ctor) && this->emitPopPtr(Ctor) &&
this->emitRetVoid(Ctor);
}

InitLinkScope<Emitter> InitScope(this, InitLink::This());
for (const auto *Init : Ctor->inits()) {
// Scope needed for the initializers.
Expand Down Expand Up @@ -5548,22 +5562,21 @@ bool Compiler<Emitter>::emitComplexComparison(const Expr *LHS, const Expr *RHS,
template <class Emitter>
bool Compiler<Emitter>::emitRecordDestruction(const Record *R) {
assert(R);
// First, destroy all fields.
for (const Record::Field &Field : llvm::reverse(R->fields())) {
const Descriptor *D = Field.Desc;
if (!D->isPrimitive() && !D->isPrimitiveArray()) {
if (!this->emitGetPtrField(Field.Offset, SourceInfo{}))
return false;
if (!this->emitDestruction(D))
return false;
if (!this->emitPopPtr(SourceInfo{}))
return false;
if (!R->isUnion()) {
// First, destroy all fields.
for (const Record::Field &Field : llvm::reverse(R->fields())) {
const Descriptor *D = Field.Desc;
if (!D->isPrimitive() && !D->isPrimitiveArray()) {
if (!this->emitGetPtrField(Field.Offset, SourceInfo{}))
return false;
if (!this->emitDestruction(D))
return false;
if (!this->emitPopPtr(SourceInfo{}))
return false;
}
}
}

// FIXME: Unions need to be handled differently here. We don't want to
// call the destructor of its members.

// Now emit the destructor and recurse into base classes.
if (const CXXDestructorDecl *Dtor = R->getDestructor();
Dtor && !Dtor->isTrivial()) {
Expand Down
54 changes: 33 additions & 21 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ using namespace clang;
using namespace clang::interp;

template <typename T>
static void ctorTy(Block *, std::byte *Ptr, bool, bool, bool,
static void ctorTy(Block *, std::byte *Ptr, bool, bool, bool, bool,
const Descriptor *) {
new (Ptr) T();
}
Expand All @@ -40,7 +40,7 @@ static void moveTy(Block *, const std::byte *Src, std::byte *Dst,
}

template <typename T>
static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool,
static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool, bool,
const Descriptor *D) {
new (Ptr) InitMapPtr(std::nullopt);

Expand Down Expand Up @@ -83,7 +83,8 @@ static void moveArrayTy(Block *, const std::byte *Src, std::byte *Dst,
}

static void ctorArrayDesc(Block *B, std::byte *Ptr, bool IsConst,
bool IsMutable, bool IsActive, const Descriptor *D) {
bool IsMutable, bool IsActive, bool InUnion,
const Descriptor *D) {
const unsigned NumElems = D->getNumElems();
const unsigned ElemSize =
D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
Expand All @@ -102,9 +103,11 @@ static void ctorArrayDesc(Block *B, std::byte *Ptr, bool IsConst,
Desc->IsActive = IsActive;
Desc->IsConst = IsConst || D->IsConst;
Desc->IsFieldMutable = IsMutable || D->IsMutable;
Desc->InUnion = InUnion;

if (auto Fn = D->ElemDesc->CtorFn)
Fn(B, ElemLoc, Desc->IsConst, Desc->IsFieldMutable, IsActive,
D->ElemDesc);
Desc->InUnion || SD->isUnion(), D->ElemDesc);
}
}

Expand Down Expand Up @@ -146,55 +149,62 @@ static void moveArrayDesc(Block *B, const std::byte *Src, std::byte *Dst,
}

static void initField(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
bool IsActive, bool IsUnion, const Descriptor *D,
unsigned FieldOffset) {
bool IsActive, bool IsUnionField, bool InUnion,
const Descriptor *D, unsigned FieldOffset) {
auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + FieldOffset) - 1;
Desc->Offset = FieldOffset;
Desc->Desc = D;
Desc->IsInitialized = D->IsArray;
Desc->IsBase = false;
Desc->IsActive = IsActive && !IsUnion;
Desc->IsActive = IsActive && !IsUnionField;
Desc->InUnion = InUnion;
Desc->IsConst = IsConst || D->IsConst;
Desc->IsFieldMutable = IsMutable || D->IsMutable;

if (auto Fn = D->CtorFn)
Fn(B, Ptr + FieldOffset, Desc->IsConst, Desc->IsFieldMutable,
Desc->IsActive, D);
Desc->IsActive, InUnion || D->isUnion(), D);
}

static void initBase(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
bool IsActive, const Descriptor *D, unsigned FieldOffset,
bool IsVirtualBase) {
bool IsActive, bool InUnion, const Descriptor *D,
unsigned FieldOffset, bool IsVirtualBase) {
assert(D);
assert(D->ElemRecord);
assert(!D->ElemRecord->isUnion()); // Unions cannot be base classes.

bool IsUnion = D->ElemRecord->isUnion();
auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + FieldOffset) - 1;
Desc->Offset = FieldOffset;
Desc->Desc = D;
Desc->IsInitialized = D->IsArray;
Desc->IsBase = true;
Desc->IsVirtualBase = IsVirtualBase;
Desc->IsActive = IsActive && !IsUnion;
Desc->IsActive = IsActive && !InUnion;
Desc->IsConst = IsConst || D->IsConst;
Desc->IsFieldMutable = IsMutable || D->IsMutable;
Desc->InUnion = InUnion;

for (const auto &V : D->ElemRecord->bases())
initBase(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, V.Desc,
V.Offset, false);
initBase(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, InUnion,
V.Desc, V.Offset, false);
for (const auto &F : D->ElemRecord->fields())
initField(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, IsUnion,
F.Desc, F.Offset);
initField(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, InUnion,
InUnion, F.Desc, F.Offset);
}

static void ctorRecord(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
bool IsActive, const Descriptor *D) {
bool IsActive, bool InUnion, const Descriptor *D) {
for (const auto &V : D->ElemRecord->bases())
initBase(B, Ptr, IsConst, IsMutable, IsActive, V.Desc, V.Offset, false);
for (const auto &F : D->ElemRecord->fields())
initField(B, Ptr, IsConst, IsMutable, IsActive, D->ElemRecord->isUnion(), F.Desc, F.Offset);
initBase(B, Ptr, IsConst, IsMutable, IsActive, InUnion, V.Desc, V.Offset,
false);
for (const auto &F : D->ElemRecord->fields()) {
bool IsUnionField = D->isUnion();
initField(B, Ptr, IsConst, IsMutable, IsActive, IsUnionField,
InUnion || IsUnionField, F.Desc, F.Offset);
}
for (const auto &V : D->ElemRecord->virtual_bases())
initBase(B, Ptr, IsConst, IsMutable, IsActive, V.Desc, V.Offset, true);
initBase(B, Ptr, IsConst, IsMutable, IsActive, InUnion, V.Desc, V.Offset,
true);
}

static void destroyField(Block *B, std::byte *Ptr, const Descriptor *D,
Expand Down Expand Up @@ -403,6 +413,8 @@ SourceLocation Descriptor::getLocation() const {
llvm_unreachable("Invalid descriptor type");
}

bool Descriptor::isUnion() const { return isRecord() && ElemRecord->isUnion(); }

InitMap::InitMap(unsigned N)
: UninitFields(N), Data(std::make_unique<T[]>(numFields(N))) {
std::fill_n(data(), numFields(N), 0);
Expand Down
8 changes: 7 additions & 1 deletion clang/lib/AST/Interp/Descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ using InitMapPtr = std::optional<std::pair<bool, std::shared_ptr<InitMap>>>;
/// inline descriptors of all fields and array elements. It also initializes
/// all the fields which contain non-trivial types.
using BlockCtorFn = void (*)(Block *Storage, std::byte *FieldPtr, bool IsConst,
bool IsMutable, bool IsActive,
bool IsMutable, bool IsActive, bool InUnion,
const Descriptor *FieldDesc);

/// Invoked when a block is destroyed. Invokes the destructors of all
Expand Down Expand Up @@ -83,11 +83,15 @@ struct InlineDescriptor {
/// Flag indicating if the field is an embedded base class.
LLVM_PREFERRED_TYPE(bool)
unsigned IsBase : 1;
/// Flag inidcating if the field is a virtual base class.
LLVM_PREFERRED_TYPE(bool)
unsigned IsVirtualBase : 1;
/// Flag indicating if the field is the active member of a union.
LLVM_PREFERRED_TYPE(bool)
unsigned IsActive : 1;
/// Flat indicating if this field is in a union (even if nested).
unsigned InUnion : 1;
LLVM_PREFERRED_TYPE(bool)
/// Flag indicating if the field is mutable (if in a record).
LLVM_PREFERRED_TYPE(bool)
unsigned IsFieldMutable : 1;
Expand Down Expand Up @@ -250,6 +254,8 @@ struct Descriptor final {
bool isArray() const { return IsArray; }
/// Checks if the descriptor is of a record.
bool isRecord() const { return !IsArray && ElemRecord; }
/// Checks if the descriptor is of a union.
bool isUnion() const;
/// Checks if this is a dummy descriptor.
bool isDummy() const { return IsDummy; }

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 @@ -226,6 +226,8 @@ LLVM_DUMP_METHOD void Descriptor::dump(llvm::raw_ostream &OS) const {
OS << " primitive-array";
else if (isCompositeArray())
OS << " composite-array";
else if (isUnion())
OS << " union";
else if (isRecord())
OS << " record";
else if (isPrimitive())
Expand All @@ -250,6 +252,7 @@ LLVM_DUMP_METHOD void InlineDescriptor::dump(llvm::raw_ostream &OS) const {
OS << "IsInitialized: " << IsInitialized << "\n";
OS << "IsBase: " << IsBase << "\n";
OS << "IsActive: " << IsActive << "\n";
OS << "InUnion: " << InUnion << "\n";
OS << "IsFieldMutable: " << IsFieldMutable << "\n";
OS << "Desc: ";
if (Desc)
Expand Down
27 changes: 22 additions & 5 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,18 +125,35 @@ static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
if (Ptr.isActive())
return true;

// Get the inactive field descriptor.
const FieldDecl *InactiveField = Ptr.getField();

// Walk up the pointer chain to find the union which is not active.
Pointer U = Ptr.getBase();
while (!U.isActive()) {
Pointer C = Ptr;
while (!U.isRoot() && U.inUnion() && !U.isActive()) {
C = U;
U = U.getBase();
}
// Get the inactive field descriptor.
const FieldDecl *InactiveField = C.getField();
assert(InactiveField);

// Consider:
// union U {
// struct {
// int x;
// int y;
// } a;
// }
//
// When activating x, we will also activate a. If we now try to read
// from y, we will get to CheckActive, because y is not active. In that
// case, our U will be a (not a union). We return here and let later code
// handle this.
if (!U.getFieldDesc()->isUnion())
return true;

// Find the active field of the union.
const Record *R = U.getRecord();
assert(R && R->isUnion() && "Not a union");

const FieldDecl *ActiveField = nullptr;
for (unsigned I = 0, N = R->getNumFields(); I < N; ++I) {
const Pointer &Field = U.atField(R->getField(I)->Offset);
Expand Down
41 changes: 11 additions & 30 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -1391,6 +1391,7 @@ bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
return false;
const Pointer &Field = This.atField(I);
Field.deref<T>() = S.Stk.pop<T>();
Field.activate();
Field.initialize();
return true;
}
Expand All @@ -1413,20 +1414,6 @@ bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F,
return true;
}

template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitThisFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) {
if (S.checkingPotentialConstantExpression())
return false;
const Pointer &This = S.Current->getThis();
if (!CheckThis(S, OpPC, This))
return false;
const Pointer &Field = This.atField(I);
Field.deref<T>() = S.Stk.pop<T>();
Field.activate();
Field.initialize();
return true;
}

/// 1) Pops the value from the stack
/// 2) Peeks a pointer from the stack
/// 3) Pushes the value to field I of the pointer on the stack
Expand All @@ -1451,17 +1438,6 @@ bool InitBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) {
return true;
}

template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) {
const T &Value = S.Stk.pop<T>();
const Pointer &Ptr = S.Stk.pop<Pointer>();
const Pointer &Field = Ptr.atField(I);
Field.deref<T>() = Value;
Field.activate();
Field.initialize();
return true;
}

//===----------------------------------------------------------------------===//
// GetPtr Local/Param/Global/Field/This
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -1719,8 +1695,10 @@ bool Store(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.peek<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
if (Ptr.canBeInitialized())
if (Ptr.canBeInitialized()) {
Ptr.initialize();
Ptr.activate();
}
Ptr.deref<T>() = Value;
return true;
}
Expand All @@ -1731,8 +1709,10 @@ bool StorePop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
if (Ptr.canBeInitialized())
if (Ptr.canBeInitialized()) {
Ptr.initialize();
Ptr.activate();
}
Ptr.deref<T>() = Value;
return true;
}
Expand Down Expand Up @@ -2653,9 +2633,10 @@ inline bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
ThisPtr.getFieldDesc()->getType()->getAsCXXRecordDecl();
if (Func->getParentDecl()->isDerivedFrom(ThisFieldDecl)) {
// If the function we call is further DOWN the hierarchy than the
// FieldDesc of our pointer, just get the DeclDesc instead, which
// is the furthest we might go up in the hierarchy.
ThisPtr = ThisPtr.getDeclPtr();
// FieldDesc of our pointer, just go up the hierarchy of this field
// the furthest we can go.
while (ThisPtr.isBaseClass())
ThisPtr = ThisPtr.getBase();
}
}

Expand Down
5 changes: 3 additions & 2 deletions clang/lib/AST/Interp/InterpBlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,10 @@ class Block final {
void invokeCtor() {
assert(!IsInitialized);
std::memset(rawData(), 0, Desc->getAllocSize());
if (Desc->CtorFn)
if (Desc->CtorFn) {
Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,
/*isActive=*/true, Desc);
/*isActive=*/true, /*InUnion=*/false, Desc);
}
IsInitialized = true;
}

Expand Down
26 changes: 21 additions & 5 deletions clang/lib/AST/Interp/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1658,18 +1658,34 @@ bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest) {
}

if (DestDesc->isRecord()) {
assert(SrcDesc->isRecord());
assert(SrcDesc->ElemRecord == DestDesc->ElemRecord);
const Record *R = DestDesc->ElemRecord;
for (const Record::Field &F : R->fields()) {
auto copyField = [&](const Record::Field &F, bool Activate) -> bool {
Pointer DestField = Dest.atField(F.Offset);
if (std::optional<PrimType> FT = S.Ctx.classify(F.Decl->getType())) {
TYPE_SWITCH(*FT, {
DestField.deref<T>() = Src.atField(F.Offset).deref<T>();
DestField.initialize();
if (Activate)
DestField.activate();
});
return true;
}
return Invalid(S, OpPC);
};

assert(SrcDesc->isRecord());
assert(SrcDesc->ElemRecord == DestDesc->ElemRecord);
const Record *R = DestDesc->ElemRecord;
for (const Record::Field &F : R->fields()) {
if (R->isUnion()) {
// For unions, only copy the active field.
const Pointer &SrcField = Src.atField(F.Offset);
if (SrcField.isActive()) {
if (!copyField(F, /*Activate=*/true))
return false;
}
} else {
return Invalid(S, OpPC);
if (!copyField(F, /*Activate=*/false))
return false;
}
}
return true;
Expand Down
4 changes: 0 additions & 4 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -440,8 +440,6 @@ def SetThisField : AccessOpcode;
// [Value] -> []
def InitThisField : AccessOpcode;
// [Value] -> []
def InitThisFieldActive : AccessOpcode;
// [Value] -> []
def InitThisBitField : Opcode {
let Types = [AluTypeClass];
let Args = [ArgRecordField, ArgUint32];
Expand All @@ -451,8 +449,6 @@ def InitThisBitField : Opcode {
def InitField : AccessOpcode;
// [Pointer, Value] -> []
def InitBitField : BitFieldOpcode;
// [Pointer, Value] -> []
def InitFieldActive : AccessOpcode;

//===----------------------------------------------------------------------===//
// Pointer access
Expand Down
27 changes: 26 additions & 1 deletion clang/lib/AST/Interp/Pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -388,12 +388,37 @@ void Pointer::initialize() const {
void Pointer::activate() const {
// Field has its bit in an inline descriptor.
assert(PointeeStorage.BS.Base != 0 &&
"Only composite fields can be initialised");
"Only composite fields can be activated");

if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor))
return;
if (!getInlineDesc()->InUnion)
return;

getInlineDesc()->IsActive = true;

// Get the union, iterate over its fields and DEactivate all others.
Pointer UnionPtr = getBase();
while (!UnionPtr.getFieldDesc()->isUnion())
UnionPtr = UnionPtr.getBase();

const Record *UnionRecord = UnionPtr.getRecord();
for (const Record::Field &F : UnionRecord->fields()) {
Pointer FieldPtr = UnionPtr.atField(F.Offset);
if (FieldPtr == *this) {
} else {
FieldPtr.getInlineDesc()->IsActive = false;
// FIXME: Recurse.
}
}

Pointer B = getBase();
while (!B.isRoot() && B.inUnion()) {
// FIXME: Need to de-activate other fields of parent records.
B.getInlineDesc()->IsActive = true;
assert(B.isActive());
B = B.getBase();
}
}

void Pointer::deactivate() const {
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Interp/Pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,12 @@ class Pointer {
return getFieldDesc()->IsArray;
return false;
}
bool inUnion() const {
if (isBlockPointer())
return getInlineDesc()->InUnion;
return false;
};

/// Checks if the structure is a primitive array.
bool inPrimitiveArray() const {
if (isBlockPointer())
Expand Down
26 changes: 23 additions & 3 deletions clang/lib/AST/OpenMPClause.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1773,6 +1773,24 @@ OMPNumTeamsClause *OMPNumTeamsClause::CreateEmpty(const ASTContext &C,
return new (Mem) OMPNumTeamsClause(N);
}

OMPThreadLimitClause *OMPThreadLimitClause::Create(
const ASTContext &C, OpenMPDirectiveKind CaptureRegion,
SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation EndLoc,
ArrayRef<Expr *> VL, Stmt *PreInit) {
void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(VL.size()));
OMPThreadLimitClause *Clause =
new (Mem) OMPThreadLimitClause(C, StartLoc, LParenLoc, EndLoc, VL.size());
Clause->setVarRefs(VL);
Clause->setPreInitStmt(PreInit, CaptureRegion);
return Clause;
}

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

//===----------------------------------------------------------------------===//
// OpenMP clauses printing methods
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -2081,9 +2099,11 @@ void OMPClausePrinter::VisitOMPNumTeamsClause(OMPNumTeamsClause *Node) {
}

void OMPClausePrinter::VisitOMPThreadLimitClause(OMPThreadLimitClause *Node) {
OS << "thread_limit(";
Node->getThreadLimit()->printPretty(OS, nullptr, Policy, 0);
OS << ")";
if (!Node->varlist_empty()) {
OS << "thread_limit";
VisitOMPClauseList(Node, '(');
OS << ")";
}
}

void OMPClausePrinter::VisitOMPPriorityClause(OMPPriorityClause *Node) {
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/AST/StmtProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -862,9 +862,8 @@ void OMPClauseProfiler::VisitOMPNumTeamsClause(const OMPNumTeamsClause *C) {
}
void OMPClauseProfiler::VisitOMPThreadLimitClause(
const OMPThreadLimitClause *C) {
VisitOMPClauseList(C);
VistOMPClauseWithPreInit(C);
if (C->getThreadLimit())
Profiler->VisitStmt(C->getThreadLimit());
}
void OMPClauseProfiler::VisitOMPPriorityClause(const OMPPriorityClause *C) {
VistOMPClauseWithPreInit(C);
Expand Down
13 changes: 9 additions & 4 deletions clang/lib/CodeGen/CGObjCGNU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2092,10 +2092,15 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {
auto *classStart =
llvm::StructType::get(PtrTy, PtrTy, PtrTy, LongTy, LongTy);
auto &astContext = CGM.getContext();
auto flags = Builder.CreateLoad(
Address{Builder.CreateStructGEP(classStart, selfValue, 4), LongTy,
CharUnits::fromQuantity(
astContext.getTypeAlign(astContext.UnsignedLongTy))});
// FIXME: The following few lines up to and including the call to
// `CreateLoad` were known to miscompile when MSVC 19.40.33813 is used
// to build Clang. When the bug is fixed in future MSVC releases, we
// should revert these lines to their previous state. See discussion in
// https://github.com/llvm/llvm-project/pull/102681
llvm::Value *Val = Builder.CreateStructGEP(classStart, selfValue, 4);
auto Align = CharUnits::fromQuantity(
astContext.getTypeAlign(astContext.UnsignedLongTy));
auto flags = Builder.CreateLoad(Address{Val, LongTy, Align});
auto isInitialized =
Builder.CreateAnd(flags, ClassFlags::ClassFlagInitialized);
llvm::BasicBlock *notInitializedBlock =
Expand Down
15 changes: 10 additions & 5 deletions clang/lib/CodeGen/CGOpenMPRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6332,7 +6332,8 @@ const Expr *CGOpenMPRuntime::getNumThreadsExprForTargetDirective(
CGOpenMPInnerExprInfo CGInfo(CGF, *CS);
CodeGenFunction::CGCapturedStmtRAII CapInfoRAII(CGF, &CGInfo);
CodeGenFunction::LexicalScope Scope(
CGF, ThreadLimitClause->getThreadLimit()->getSourceRange());
CGF,
ThreadLimitClause->getThreadLimit().front()->getSourceRange());
if (const auto *PreInit =
cast_or_null<DeclStmt>(ThreadLimitClause->getPreInitStmt())) {
for (const auto *I : PreInit->decls()) {
Expand All @@ -6349,7 +6350,8 @@ const Expr *CGOpenMPRuntime::getNumThreadsExprForTargetDirective(
}
}
if (ThreadLimitClause)
CheckForConstExpr(ThreadLimitClause->getThreadLimit(), ThreadLimitExpr);
CheckForConstExpr(ThreadLimitClause->getThreadLimit().front(),
ThreadLimitExpr);
if (const auto *Dir = dyn_cast_or_null<OMPExecutableDirective>(Child)) {
if (isOpenMPTeamsDirective(Dir->getDirectiveKind()) &&
!isOpenMPDistributeDirective(Dir->getDirectiveKind())) {
Expand All @@ -6370,7 +6372,8 @@ const Expr *CGOpenMPRuntime::getNumThreadsExprForTargetDirective(
if (D.hasClausesOfKind<OMPThreadLimitClause>()) {
CodeGenFunction::RunCleanupsScope ThreadLimitScope(CGF);
const auto *ThreadLimitClause = D.getSingleClause<OMPThreadLimitClause>();
CheckForConstExpr(ThreadLimitClause->getThreadLimit(), ThreadLimitExpr);
CheckForConstExpr(ThreadLimitClause->getThreadLimit().front(),
ThreadLimitExpr);
}
const CapturedStmt *CS = D.getInnermostCapturedStmt();
getNumThreads(CGF, CS, NTPtr, UpperBound, UpperBoundOnly, CondVal);
Expand All @@ -6388,7 +6391,8 @@ const Expr *CGOpenMPRuntime::getNumThreadsExprForTargetDirective(
if (D.hasClausesOfKind<OMPThreadLimitClause>()) {
CodeGenFunction::RunCleanupsScope ThreadLimitScope(CGF);
const auto *ThreadLimitClause = D.getSingleClause<OMPThreadLimitClause>();
CheckForConstExpr(ThreadLimitClause->getThreadLimit(), ThreadLimitExpr);
CheckForConstExpr(ThreadLimitClause->getThreadLimit().front(),
ThreadLimitExpr);
}
getNumThreads(CGF, D.getInnermostCapturedStmt(), NTPtr, UpperBound,
UpperBoundOnly, CondVal);
Expand Down Expand Up @@ -6424,7 +6428,8 @@ const Expr *CGOpenMPRuntime::getNumThreadsExprForTargetDirective(
if (D.hasClausesOfKind<OMPThreadLimitClause>()) {
CodeGenFunction::RunCleanupsScope ThreadLimitScope(CGF);
const auto *ThreadLimitClause = D.getSingleClause<OMPThreadLimitClause>();
CheckForConstExpr(ThreadLimitClause->getThreadLimit(), ThreadLimitExpr);
CheckForConstExpr(ThreadLimitClause->getThreadLimit().front(),
ThreadLimitExpr);
}
if (D.hasClausesOfKind<OMPNumThreadsClause>()) {
CodeGenFunction::RunCleanupsScope NumThreadsScope(CGF);
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/CodeGen/CGStmtOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5259,7 +5259,7 @@ void CodeGenFunction::EmitOMPTargetTaskBasedDirective(
// enclosing this target region. This will indirectly set the thread_limit
// for every applicable construct within target region.
CGF.CGM.getOpenMPRuntime().emitThreadLimitClause(
CGF, TL->getThreadLimit(), S.getBeginLoc());
CGF, TL->getThreadLimit().front(), S.getBeginLoc());
}
BodyGen(CGF);
};
Expand Down Expand Up @@ -6860,7 +6860,7 @@ static void emitCommonOMPTeamsDirective(CodeGenFunction &CGF,
const auto *TL = S.getSingleClause<OMPThreadLimitClause>();
if (NT || TL) {
const Expr *NumTeams = NT ? NT->getNumTeams().front() : nullptr;
const Expr *ThreadLimit = TL ? TL->getThreadLimit() : nullptr;
const Expr *ThreadLimit = TL ? TL->getThreadLimit().front() : nullptr;

CGF.CGM.getOpenMPRuntime().emitNumTeamsClause(CGF, NumTeams, ThreadLimit,
S.getBeginLoc());
Expand Down
34 changes: 33 additions & 1 deletion clang/lib/Format/ContinuationIndenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ static bool startsSegmentOfBuilderTypeCall(const FormatToken &Tok) {
// Returns \c true if \c Current starts a new parameter.
static bool startsNextParameter(const FormatToken &Current,
const FormatStyle &Style) {
const FormatToken &Previous = *Current.Previous;
assert(Current.Previous);
const auto &Previous = *Current.Previous;
if (Current.is(TT_CtorInitializerComma) &&
Style.BreakConstructorInitializers == FormatStyle::BCIS_BeforeComma) {
return true;
Expand All @@ -146,6 +147,31 @@ static bool startsNextParameter(const FormatToken &Current,
Style.BreakInheritanceList != FormatStyle::BILS_BeforeComma));
}

// Returns \c true if \c Token in an alignable binary operator
static bool isAlignableBinaryOperator(const FormatToken &Token) {
// No need to align binary operators that only have two operands.
bool HasTwoOperands = Token.OperatorIndex == 0 && !Token.NextOperator;
return Token.is(TT_BinaryOperator) && !HasTwoOperands &&
Token.getPrecedence() > prec::Conditional &&
Token.getPrecedence() < prec::PointerToMember;
}

// Returns \c true if \c Current starts the next operand in a binary operation.
static bool startsNextOperand(const FormatToken &Current) {
assert(Current.Previous);
const auto &Previous = *Current.Previous;
return isAlignableBinaryOperator(Previous) && !Current.isTrailingComment();
}

// Returns \c true if \c Current is a binary operation that must break.
static bool mustBreakBinaryOperation(const FormatToken &Current,
const FormatStyle &Style) {
return Style.BreakBinaryOperations != FormatStyle::BBO_Never &&
(Style.BreakBeforeBinaryOperators == FormatStyle::BOS_None
? startsNextOperand
: isAlignableBinaryOperator)(Current);
}

static bool opensProtoMessageField(const FormatToken &LessTok,
const FormatStyle &Style) {
if (LessTok.isNot(tok::less))
Expand Down Expand Up @@ -869,6 +895,9 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
}
if (CurrentState.AvoidBinPacking && startsNextParameter(Current, Style))
CurrentState.NoLineBreak = true;
if (mustBreakBinaryOperation(Current, Style))
CurrentState.NoLineBreak = true;

if (startsSegmentOfBuilderTypeCall(Current) &&
State.Column > getNewLineColumn(State)) {
CurrentState.ContainsUnwrappedBuilder = true;
Expand Down Expand Up @@ -1235,6 +1264,9 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
}
}

if (mustBreakBinaryOperation(Current, Style))
CurrentState.BreakBeforeParameter = true;

return Penalty;
}

Expand Down
12 changes: 12 additions & 0 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,16 @@ struct ScalarEnumerationTraits<FormatStyle::BreakBeforeInlineASMColonStyle> {
}
};

template <>
struct ScalarEnumerationTraits<FormatStyle::BreakBinaryOperationsStyle> {
static void enumeration(IO &IO,
FormatStyle::BreakBinaryOperationsStyle &Value) {
IO.enumCase(Value, "Never", FormatStyle::BBO_Never);
IO.enumCase(Value, "OnePerLine", FormatStyle::BBO_OnePerLine);
IO.enumCase(Value, "RespectPrecedence", FormatStyle::BBO_RespectPrecedence);
}
};

template <>
struct ScalarEnumerationTraits<FormatStyle::BreakConstructorInitializersStyle> {
static void
Expand Down Expand Up @@ -968,6 +978,7 @@ template <> struct MappingTraits<FormatStyle> {
Style.BreakBeforeInlineASMColon);
IO.mapOptional("BreakBeforeTernaryOperators",
Style.BreakBeforeTernaryOperators);
IO.mapOptional("BreakBinaryOperations", Style.BreakBinaryOperations);
IO.mapOptional("BreakConstructorInitializers",
Style.BreakConstructorInitializers);
IO.mapOptional("BreakFunctionDefinitionParameters",
Expand Down Expand Up @@ -1480,6 +1491,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.BreakBeforeConceptDeclarations = FormatStyle::BBCDS_Always;
LLVMStyle.BreakBeforeInlineASMColon = FormatStyle::BBIAS_OnlyMultiline;
LLVMStyle.BreakBeforeTernaryOperators = true;
LLVMStyle.BreakBinaryOperations = FormatStyle::BBO_Never;
LLVMStyle.BreakConstructorInitializers = FormatStyle::BCIS_BeforeColon;
LLVMStyle.BreakFunctionDefinitionParameters = false;
LLVMStyle.BreakInheritanceList = FormatStyle::BILS_BeforeColon;
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3175,6 +3175,15 @@ class ExpressionParser {
parse(Precedence + 1);

int CurrentPrecedence = getCurrentPrecedence();
if (Style.BreakBinaryOperations == FormatStyle::BBO_OnePerLine &&
CurrentPrecedence > prec::Conditional &&
CurrentPrecedence < prec::PointerToMember) {
// When BreakBinaryOperations is set to BreakAll,
// all operations will be on the same line or on individual lines.
// Override precedence to avoid adding fake parenthesis which could
// group operations of a different precedence level on the same line
CurrentPrecedence = prec::Additive;
}

if (Precedence == CurrentPrecedence && Current &&
Current->is(TT_SelectorName)) {
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Parse/ParseOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3175,7 +3175,6 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind,
case OMPC_simdlen:
case OMPC_collapse:
case OMPC_ordered:
case OMPC_thread_limit:
case OMPC_priority:
case OMPC_grainsize:
case OMPC_num_tasks:
Expand Down Expand Up @@ -3332,6 +3331,7 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind,
: ParseOpenMPClause(CKind, WrongDirective);
break;
case OMPC_num_teams:
case OMPC_thread_limit:
if (!FirstClause) {
Diag(Tok, diag::err_omp_more_one_clause)
<< getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind) << 0;
Expand Down
58 changes: 39 additions & 19 deletions clang/lib/Sema/SemaOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13061,6 +13061,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDirective(ArrayRef<OMPClause *> Clauses,
return StmtError();

if (!checkNumExprsInClause<OMPNumTeamsClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed) ||
!checkNumExprsInClause<OMPThreadLimitClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed))
return StmtError();

Expand Down Expand Up @@ -13843,7 +13845,9 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDirective(
? diag::err_ompx_more_than_three_expr_not_allowed
: diag::err_omp_multi_expr_not_allowed;
if (!checkNumExprsInClause<OMPNumTeamsClause>(*this, Clauses,
ClauseMaxNumExprs, DiagNo))
ClauseMaxNumExprs, DiagNo) ||
!checkNumExprsInClause<OMPThreadLimitClause>(*this, Clauses,
ClauseMaxNumExprs, DiagNo))
return StmtError();

return OMPTargetTeamsDirective::Create(getASTContext(), StartLoc, EndLoc,
Expand All @@ -13857,6 +13861,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeDirective(
return StmtError();

if (!checkNumExprsInClause<OMPNumTeamsClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed) ||
!checkNumExprsInClause<OMPThreadLimitClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed))
return StmtError();

Expand Down Expand Up @@ -13887,6 +13893,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeParallelForDirective(
return StmtError();

if (!checkNumExprsInClause<OMPNumTeamsClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed) ||
!checkNumExprsInClause<OMPThreadLimitClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed))
return StmtError();

Expand Down Expand Up @@ -13918,6 +13926,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeParallelForSimdDirective(
return StmtError();

if (!checkNumExprsInClause<OMPNumTeamsClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed) ||
!checkNumExprsInClause<OMPThreadLimitClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed))
return StmtError();

Expand Down Expand Up @@ -13952,6 +13962,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeSimdDirective(
return StmtError();

if (!checkNumExprsInClause<OMPNumTeamsClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed) ||
!checkNumExprsInClause<OMPThreadLimitClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed))
return StmtError();

Expand Down Expand Up @@ -15002,9 +15014,6 @@ OMPClause *SemaOpenMP::ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind,
case OMPC_ordered:
Res = ActOnOpenMPOrderedClause(StartLoc, EndLoc, LParenLoc, Expr);
break;
case OMPC_thread_limit:
Res = ActOnOpenMPThreadLimitClause(Expr, StartLoc, LParenLoc, EndLoc);
break;
case OMPC_priority:
Res = ActOnOpenMPPriorityClause(Expr, StartLoc, LParenLoc, EndLoc);
break;
Expand Down Expand Up @@ -15109,6 +15118,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind,
case OMPC_when:
case OMPC_bind:
case OMPC_num_teams:
case OMPC_thread_limit:
default:
llvm_unreachable("Clause is not allowed.");
}
Expand Down Expand Up @@ -16975,6 +16985,9 @@ OMPClause *SemaOpenMP::ActOnOpenMPVarListClause(OpenMPClauseKind Kind,
case OMPC_num_teams:
Res = ActOnOpenMPNumTeamsClause(VarList, StartLoc, LParenLoc, EndLoc);
break;
case OMPC_thread_limit:
Res = ActOnOpenMPThreadLimitClause(VarList, StartLoc, LParenLoc, EndLoc);
break;
case OMPC_if:
case OMPC_depobj:
case OMPC_final:
Expand Down Expand Up @@ -17005,7 +17018,6 @@ OMPClause *SemaOpenMP::ActOnOpenMPVarListClause(OpenMPClauseKind Kind,
case OMPC_device:
case OMPC_threads:
case OMPC_simd:
case OMPC_thread_limit:
case OMPC_priority:
case OMPC_grainsize:
case OMPC_nogroup:
Expand Down Expand Up @@ -21919,32 +21931,40 @@ OMPClause *SemaOpenMP::ActOnOpenMPNumTeamsClause(ArrayRef<Expr *> VarList,
LParenLoc, EndLoc, Vars, PreInit);
}

OMPClause *SemaOpenMP::ActOnOpenMPThreadLimitClause(Expr *ThreadLimit,
OMPClause *SemaOpenMP::ActOnOpenMPThreadLimitClause(ArrayRef<Expr *> VarList,
SourceLocation StartLoc,
SourceLocation LParenLoc,
SourceLocation EndLoc) {
Expr *ValExpr = ThreadLimit;
Stmt *HelperValStmt = nullptr;

// OpenMP [teams Constrcut, Restrictions]
// The thread_limit expression must evaluate to a positive integer value.
if (!isNonNegativeIntegerValue(ValExpr, SemaRef, OMPC_thread_limit,
/*StrictlyPositive=*/true))
if (VarList.empty())
return nullptr;

for (Expr *ValExpr : VarList) {
// OpenMP [teams Constrcut, Restrictions]
// The thread_limit expression must evaluate to a positive integer value.
if (!isNonNegativeIntegerValue(ValExpr, SemaRef, OMPC_thread_limit,
/*StrictlyPositive=*/true))
return nullptr;
}

OpenMPDirectiveKind DKind = DSAStack->getCurrentDirective();
OpenMPDirectiveKind CaptureRegion = getOpenMPCaptureRegionForClause(
DKind, OMPC_thread_limit, getLangOpts().OpenMP);
if (CaptureRegion != OMPD_unknown &&
!SemaRef.CurContext->isDependentContext()) {
if (CaptureRegion == OMPD_unknown || SemaRef.CurContext->isDependentContext())
return OMPThreadLimitClause::Create(getASTContext(), CaptureRegion,
StartLoc, LParenLoc, EndLoc, VarList,
/*PreInit=*/nullptr);

llvm::MapVector<const Expr *, DeclRefExpr *> Captures;
SmallVector<Expr *, 3> Vars;
for (Expr *ValExpr : VarList) {
ValExpr = SemaRef.MakeFullExpr(ValExpr).get();
llvm::MapVector<const Expr *, DeclRefExpr *> Captures;
ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures).get();
HelperValStmt = buildPreInits(getASTContext(), Captures);
Vars.push_back(ValExpr);
}

return new (getASTContext()) OMPThreadLimitClause(
ValExpr, HelperValStmt, CaptureRegion, StartLoc, LParenLoc, EndLoc);
Stmt *PreInit = buildPreInits(getASTContext(), Captures);
return OMPThreadLimitClause::Create(getASTContext(), CaptureRegion, StartLoc,
LParenLoc, EndLoc, Vars, PreInit);
}

OMPClause *SemaOpenMP::ActOnOpenMPPriorityClause(Expr *Priority,
Expand Down
19 changes: 12 additions & 7 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -2091,12 +2091,12 @@ class TreeTransform {
///
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
OMPClause *RebuildOMPThreadLimitClause(Expr *ThreadLimit,
OMPClause *RebuildOMPThreadLimitClause(ArrayRef<Expr *> VarList,
SourceLocation StartLoc,
SourceLocation LParenLoc,
SourceLocation EndLoc) {
return getSema().OpenMP().ActOnOpenMPThreadLimitClause(
ThreadLimit, StartLoc, LParenLoc, EndLoc);
return getSema().OpenMP().ActOnOpenMPThreadLimitClause(VarList, StartLoc,
LParenLoc, EndLoc);
}

/// Build a new OpenMP 'priority' clause.
Expand Down Expand Up @@ -11028,11 +11028,16 @@ TreeTransform<Derived>::TransformOMPNumTeamsClause(OMPNumTeamsClause *C) {
template <typename Derived>
OMPClause *
TreeTransform<Derived>::TransformOMPThreadLimitClause(OMPThreadLimitClause *C) {
ExprResult E = getDerived().TransformExpr(C->getThreadLimit());
if (E.isInvalid())
return nullptr;
llvm::SmallVector<Expr *, 3> Vars;
Vars.reserve(C->varlist_size());
for (auto *VE : C->varlist()) {
ExprResult EVar = getDerived().TransformExpr(cast<Expr>(VE));
if (EVar.isInvalid())
return nullptr;
Vars.push_back(EVar.get());
}
return getDerived().RebuildOMPThreadLimitClause(
E.get(), C->getBeginLoc(), C->getLParenLoc(), C->getEndLoc());
Vars, C->getBeginLoc(), C->getLParenLoc(), C->getEndLoc());
}

template <typename Derived>
Expand Down
13 changes: 8 additions & 5 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10645,7 +10645,7 @@ OMPClause *OMPClauseReader::readClause() {
C = OMPNumTeamsClause::CreateEmpty(Context, Record.readInt());
break;
case llvm::omp::OMPC_thread_limit:
C = new (Context) OMPThreadLimitClause();
C = OMPThreadLimitClause::CreateEmpty(Context, Record.readInt());
break;
case llvm::omp::OMPC_priority:
C = new (Context) OMPPriorityClause();
Expand Down Expand Up @@ -11468,17 +11468,20 @@ void OMPClauseReader::VisitOMPNumTeamsClause(OMPNumTeamsClause *C) {
unsigned NumVars = C->varlist_size();
SmallVector<Expr *, 16> Vars;
Vars.reserve(NumVars);
for ([[maybe_unused]] auto _ : llvm::seq<unsigned>(NumVars)) {
(void)_;
for (unsigned I = 0; I != NumVars; ++I)
Vars.push_back(Record.readSubExpr());
}
C->setVarRefs(Vars);
}

void OMPClauseReader::VisitOMPThreadLimitClause(OMPThreadLimitClause *C) {
VisitOMPClauseWithPreInit(C);
C->setThreadLimit(Record.readSubExpr());
C->setLParenLoc(Record.readSourceLocation());
unsigned NumVars = C->varlist_size();
SmallVector<Expr *, 16> Vars;
Vars.reserve(NumVars);
for (unsigned I = 0; I != NumVars; ++I)
Vars.push_back(Record.readSubExpr());
C->setVarRefs(Vars);
}

void OMPClauseReader::VisitOMPPriorityClause(OMPPriorityClause *C) {
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7589,9 +7589,11 @@ void OMPClauseWriter::VisitOMPNumTeamsClause(OMPNumTeamsClause *C) {
}

void OMPClauseWriter::VisitOMPThreadLimitClause(OMPThreadLimitClause *C) {
Record.push_back(C->varlist_size());
VisitOMPClauseWithPreInit(C);
Record.AddStmt(C->getThreadLimit());
Record.AddSourceLocation(C->getLParenLoc());
for (auto *VE : C->varlist())
Record.AddStmt(VE);
}

void OMPClauseWriter::VisitOMPPriorityClause(OMPPriorityClause *C) {
Expand Down
44 changes: 44 additions & 0 deletions clang/test/AST/Interp/cxx20.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -858,3 +858,47 @@ namespace DefinitionLoc {
constexpr NonConstexprCopy ncc2 = ncc1; // both-error {{constant expression}} \
// both-note {{non-constexpr constructor}}
}

/// FIXME: Call base dtors when explicitly calling dtor.
namespace VirtDtor {
class B {
public:
constexpr B(char *p) : p(p) {}
virtual constexpr ~B() {
*p = 'B';
++p;
}

char *p;
};

class C : public B {
public:
constexpr C(char *p) : B(p) {}
virtual constexpr ~C() override {
*p = 'C';
++p;
}
};

union U {
constexpr U(char *p) : c(p) {}
constexpr ~U() {}

C c;
};

constexpr int test(char a, char b) {
char buff[2] = {};
U u(buff);

/// U is a union, so it won't call the destructor of its fields.
/// We do this manually here. Explicitly calling ~C() here should
/// also call the destructor of the base classes however.
u.c.~C();

return buff[0] == a && buff[1] == b;
}

static_assert(test('C', 'B')); // expected-error {{failed}}
}
32 changes: 27 additions & 5 deletions clang/test/AST/Interp/records.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -964,8 +964,6 @@ namespace TemporaryObjectExpr {
static_assert(foo(F()) == 0, "");
}

/// FIXME: This needs support for unions on the new interpreter.
/// We diagnose an uninitialized object in c++14.
#if __cplusplus > 201402L
namespace Unions {
struct F {
Expand All @@ -978,10 +976,10 @@ namespace TemporaryObjectExpr {
};

constexpr int foo(F f) {
return f.i + f.U.f; // ref-note {{read of member 'f' of union with active member 'a'}}
return f.i + f.U.f; // both-note {{read of member 'f' of union with active member 'a'}}
}
static_assert(foo(F()) == 0, ""); // ref-error {{not an integral constant expression}} \
// ref-note {{in call to}}
static_assert(foo(F()) == 0, ""); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}
}
#endif

Expand Down Expand Up @@ -1574,3 +1572,27 @@ namespace ctorOverrider {
constexpr Covariant1 cb;
}
#endif

#if __cplusplus >= 202002L
namespace VirtDtor {
struct X { char *p; constexpr ~X() { *p++ = 'X'; } };
struct Y : X { int y; virtual constexpr ~Y() { *p++ = 'Y'; } };
struct Z : Y { int z; constexpr ~Z() override { *p++ = 'Z'; } };

union VU {
constexpr VU() : z() {}
constexpr ~VU() {}
Z z;
};

constexpr char virt_dtor(int mode, const char *expected) {
char buff[4] = {};
VU vu;
vu.z.p = buff;

((Y&)vu.z).~Y();
return true;
}
static_assert(virt_dtor(0, "ZYX"));
}
#endif
317 changes: 317 additions & 0 deletions clang/test/AST/Interp/unions.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=expected,both %s
// RUN: %clang_cc1 -std=c++20 -fexperimental-new-constant-interpreter -verify=expected,both %s
// RUN: %clang_cc1 -verify=ref,both %s
// RUN: %clang_cc1 -std=c++20 -verify=ref,both %s

union U {
int a;
Expand Down Expand Up @@ -65,3 +67,318 @@ namespace ZeroInit {
constexpr Z z{};
static_assert(z.f == 0.0, "");
}

namespace DefaultInit {
union U1 {
constexpr U1() {}
int a, b = 42;
};

constexpr U1 u1; /// OK.

constexpr int foo() {
U1 u;
return u.a; // both-note {{read of member 'a' of union with active member 'b'}}
}
static_assert(foo() == 42); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}
}

#if __cplusplus >= 202002L
namespace SimpleActivate {
constexpr int foo() { // ref-error {{never produces a constant expression}}
union {
int a;
int b;
} Z;

Z.a = 10;
Z.b = 20;
return Z.a; // both-note {{read of member 'a' of union with active member 'b'}} \
// ref-note {{read of member 'a' of union with active member 'b}}
}
static_assert(foo() == 20); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}

constexpr int foo2() {
union {
int a;
int b;
} Z;

Z.a = 10;
Z.b = 20;
return Z.b;
}
static_assert(foo2() == 20);


constexpr int foo3() {
union {
struct {
float x,y;
} a;
int b;
} Z;

Z.a.y = 10;

return Z.a.x; // both-note {{read of uninitialized object}}
}
static_assert(foo3() == 10); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}

constexpr int foo4() {
union {
struct {
float x,y;
} a;
int b;
} Z;

Z.a.x = 100;
Z.a.y = 10;

return Z.a.x;
}
static_assert(foo4() == 100);
}

namespace IndirectFieldDecl {
struct C {
union { int a, b = 2, c; };
union { int d, e = 5, f; };
constexpr C() : a(1) {}
};
static_assert(C().a == 1, "");
}

namespace UnionDtor {

union U {
int *I;
constexpr U(int *I) : I(I) {}
constexpr ~U() {
*I = 10;
}
};

constexpr int foo() {
int a = 100;
{
U u(&a);
}
return a;
}
static_assert(foo() == 10);
}

namespace UnionMemberDtor {
class UM {
public:
int &I;
constexpr UM(int &I) : I(I) {}
constexpr ~UM() { I = 200; }
};

union U {
UM um;
constexpr U(int &I) : um(I) {}
constexpr ~U() {
}
};

constexpr int foo() {
int a = 100;
{
U u(a);
}

return a;
}
static_assert(foo() == 100);
}

namespace Nested {
union U {
int a;
int b;
};

union U2 {
U u;
U u2;
int x;
int y;
};

constexpr int foo() { // ref-error {{constexpr function never produces a constant expression}}
U2 u;
u.u.a = 10;
int a = u.y; // both-note {{read of member 'y' of union with active member 'u' is not allowed in a constant expression}} \
// ref-note {{read of member 'y' of union with active member 'u' is not allowed in a constant expression}}

return 1;
}
static_assert(foo() == 1); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}

constexpr int foo2() {
U2 u;
u.u.a = 10;
return u.u.a;
}
static_assert(foo2() == 10);

constexpr int foo3() { // ref-error {{constexpr function never produces a constant expression}}
U2 u;
u.u.a = 10;
int a = u.u.b; // both-note {{read of member 'b' of union with active member 'a' is not allowed in a constant expression}} \
// ref-note {{read of member 'b' of union with active member 'a' is not allowed in a constant expression}}

return 1;
}
static_assert(foo3() == 1); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}

constexpr int foo4() { // ref-error {{constexpr function never produces a constant expression}}
U2 u;

u.x = 10;

return u.u.a;// both-note {{read of member 'u' of union with active member 'x' is not allowed in a constant expression}} \
// ref-note {{read of member 'u' of union with active member 'x' is not allowed in a constant expression}}
}
static_assert(foo4() == 1); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}

}


namespace Zeroing {
struct non_trivial_constructor {
constexpr non_trivial_constructor() : x(100) {}
int x;
};
union U2 {
int a{1000};
non_trivial_constructor b;
};

static_assert(U2().b.x == 100, ""); // both-error {{not an integral constant expression}} \
// both-note {{read of member 'b' of union with active member 'a'}}

union { int a; int b; } constexpr u1{};
static_assert(u1.a == 0, "");
static_assert(u1.b == 0, ""); // both-error {{not an integral constant expression}} \
// both-note {{read of member 'b' of union with active member 'a'}}

union U { int a; int b; } constexpr u2 = U();
static_assert(u2.a == 0, "");
static_assert(u2.b == 0, ""); // both-error {{not an integral constant expression}} \
// both-note {{read of member 'b' of union with active member 'a'}}


struct F {int x; int y; };
union { F a; int b; } constexpr u3{};
static_assert(u3.a.x == 0, "");

union U4 { F a; int b; } constexpr u4 = U4();
static_assert(u4.a.x == 0, "");

union { int a[5]; int b; } constexpr u5{};
static_assert(u5.a[0] == 0, "");
static_assert(u5.a[4] == 0, "");
static_assert(u5.b == 0, ""); // both-error {{not an integral constant expression}} \
// both-note {{read of member 'b' of union with active member 'a'}}

union U6 { int a[5]; int b; } constexpr u6 = U6();
static_assert(u6.a[0] == 0, "");
static_assert(u6.a[4] == 0, "");
static_assert(u6.b == 0, ""); // both-error {{not an integral constant expression}} \
// both-note {{read of member 'b' of union with active member 'a'}}

union UnionWithUnnamedBitfield {
int : 3;
int n;
};
static_assert(UnionWithUnnamedBitfield().n == 0, "");
static_assert(UnionWithUnnamedBitfield{}.n == 0, "");
static_assert(UnionWithUnnamedBitfield{1}.n == 1, "");
}

namespace IndirectField {
struct S {
struct {
union {
struct {
int a;
int b;
};
int c;
};
int d;
};
union {
int e;
int f;
};
constexpr S(int a, int b, int d, int e) : a(a), b(b), d(d), e(e) {}
constexpr S(int c, int d, int f) : c(c), d(d), f(f) {}
};

constexpr S s1(1,2,3,4);
constexpr S s2(5, 6, 7);

static_assert(s1.a == 1, "");
static_assert(s1.b == 2, "");

static_assert(s1.c == 0, ""); // both-error {{constant expression}} both-note {{union with active member}}
static_assert(s1.d == 3, "");
static_assert(s1.e == 4, "");
static_assert(s1.f == 0, ""); // both-error {{constant expression}} both-note {{union with active member}}

static_assert(s2.a == 0, ""); // both-error {{constant expression}} both-note {{union with active member}}
static_assert(s2.b == 0, ""); // both-error {{constant expression}} both-note {{union with active member}}
static_assert(s2.c == 5, "");
static_assert(s2.d == 6, "");
static_assert(s2.e == 0, ""); // both-error {{constant expression}} both-note {{union with active member}}
static_assert(s2.f == 7, "");
}

namespace CopyCtor {
union U {
int a;
int b;
};

constexpr U x = {42};
constexpr U y = x;
static_assert(y.a == 42, "");
static_assert(y.b == 42, ""); // both-error {{constant expression}} \
// both-note {{'b' of union with active member 'a'}}
}

namespace UnionInBase {
struct Base {
int y;
};
struct A : Base {
int x;
int arr[3];
union { int p, q; };
};
union B {
A a;
int b;
};
constexpr int read_wrong_member_indirect() { // both-error {{never produces a constant}}
B b = {.b = 1};
int *p = &b.a.y;
return *p; // both-note 2{{read of member 'a' of union with active member 'b'}}

}
static_assert(read_wrong_member_indirect() == 1); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}
}
#endif
60 changes: 60 additions & 0 deletions clang/test/Format/list-ignored.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// RUN: rm -rf %t.dir
// RUN: mkdir -p %t.dir/level1/level2

// RUN: cd %t.dir
// RUN: echo "*" > .clang-format-ignore
// RUN: echo "level*/*.c*" >> .clang-format-ignore
// RUN: echo "*/*2/foo.*" >> .clang-format-ignore

// RUN: touch foo.cc
// RUN: clang-format -list-ignored .clang-format-ignore foo.cc \
// RUN: | FileCheck %s
// CHECK: .clang-format-ignore
// CHECK-NEXT: foo.cc

// RUN: cd level1
// RUN: touch bar.cc baz.c
// RUN: clang-format -list-ignored bar.cc baz.c \
// RUN: | FileCheck %s -check-prefix=CHECK2
// CHECK2: bar.cc
// CHECK2-NEXT: baz.c

// RUN: cd level2
// RUN: touch foo.c foo.js
// RUN: clang-format -list-ignored foo.c foo.js \
// RUN: | FileCheck %s -check-prefix=CHECK3
// CHECK3: foo.c
// CHECK3-NEXT: foo.js

// RUN: touch .clang-format-ignore
// RUN: clang-format -list-ignored foo.c foo.js \
// RUN: | FileCheck %s -allow-empty -check-prefix=CHECK4
// CHECK4-NOT: foo.c
// CHECK4-NOT: foo.js

// RUN: echo "*.js" > .clang-format-ignore
// RUN: clang-format -list-ignored foo.c foo.js \
// RUN: | FileCheck %s -check-prefix=CHECK5
// CHECK5-NOT: foo.c
// CHECK5: foo.js

// RUN: cd ../..
// RUN: clang-format -list-ignored *.cc level1/*.c* level1/level2/foo.* \
// RUN: | FileCheck %s -check-prefix=CHECK6
// CHECK6: foo.cc
// CHECK6-NEXT: bar.cc
// CHECK6-NEXT: baz.c
// CHECK6-NOT: foo.c
// CHECK6-NEXT: foo.js

// RUN: rm .clang-format-ignore
// RUN: clang-format -list-ignored *.cc level1/*.c* level1/level2/foo.* \
// RUN: | FileCheck %s -check-prefix=CHECK7
// CHECK7-NOT: foo.cc
// CHECK7-NOT: bar.cc
// CHECK7-NOT: baz.c
// CHECK7-NOT: foo.c
// CHECK7: foo.js

// RUN: cd ..
// RUN: rm -r %t.dir
4 changes: 2 additions & 2 deletions clang/test/OpenMP/target_teams_ast_print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ int main (int argc, char **argv) {
// CHECK-NEXT: #pragma omp target teams ompx_bare num_teams(1) thread_limit(32)
a=3;
// CHECK-NEXT: a = 3;
#pragma omp target teams ompx_bare num_teams(1, 2, 3) thread_limit(32)
// CHECK-NEXT: #pragma omp target teams ompx_bare num_teams(1,2,3) thread_limit(32)
#pragma omp target teams ompx_bare num_teams(1, 2, 3) thread_limit(2, 4, 6)
// CHECK-NEXT: #pragma omp target teams ompx_bare num_teams(1,2,3) thread_limit(2,4,6)
a=4;
// CHECK-NEXT: a = 4;
#pragma omp target teams default(none), private(argc,b) num_teams(f) firstprivate(argv) reduction(| : c, d) reduction(* : e) thread_limit(f+g)
Expand Down
12 changes: 12 additions & 0 deletions clang/test/OpenMP/target_teams_distribute_num_teams_messages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,15 @@ T tmain(T argc) {
#pragma omp target teams distribute num_teams(1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}}
for (int i=0; i<100; i++) foo();

#pragma omp target teams distribute thread_limit(1, 2, 3) // expected-error {{only one expression allowed in 'thread_limit' clause}}
for (int i=0; i<100; i++) foo();

#pragma omp target teams ompx_bare num_teams(1, 2, 3, 4) thread_limit(1) // expected-error {{at most three expressions are allowed in 'num_teams' clause in 'target teams ompx_bare' construct}}
for (int i=0; i<100; i++) foo();

#pragma omp target teams ompx_bare num_teams(1) thread_limit(1, 2, 3, 4) // expected-error {{at most three expressions are allowed in 'thread_limit' clause in 'target teams ompx_bare' construct}}
for (int i=0; i<100; i++) foo();

return 0;
}

Expand Down Expand Up @@ -94,8 +100,14 @@ int main(int argc, char **argv) {
#pragma omp target teams distribute num_teams(1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}}
for (int i=0; i<100; i++) foo();

#pragma omp target teams distribute thread_limit(1, 2, 3) // expected-error {{only one expression allowed in 'thread_limit' clause}}
for (int i=0; i<100; i++) foo();

#pragma omp target teams ompx_bare num_teams(1, 2, 3, 4) thread_limit(1) // expected-error {{at most three expressions are allowed in 'num_teams' clause in 'target teams ompx_bare' construct}}
for (int i=0; i<100; i++) foo();

#pragma omp target teams ompx_bare num_teams(1) thread_limit(1, 2, 3, 4) // expected-error {{at most three expressions are allowed in 'thread_limit' clause in 'target teams ompx_bare' construct}}
for (int i=0; i<100; i++) foo();

return tmain<int, 10>(argc); // expected-note {{in instantiation of function template specialization 'tmain<int, 10>' requested here}}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ T tmain(T argc) {
for (int i=0; i<100; i++) foo();
#pragma omp target teams distribute parallel for num_teams(1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}}
for (int i=0; i<100; i++) foo();
#pragma omp target teams distribute parallel for thread_limit(1, 2, 3) // expected-error {{only one expression allowed in 'thread_limit' clause}}
for (int i=0; i<100; i++) foo();

return 0;
}
Expand Down Expand Up @@ -90,5 +92,8 @@ int main(int argc, char **argv) {
#pragma omp target teams distribute parallel for num_teams(1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}}
for (int i=0; i<100; i++) foo();

#pragma omp target teams distribute parallel for thread_limit(1, 2, 3) // expected-error {{only one expression allowed in 'thread_limit' clause}}
for (int i=0; i<100; i++) foo();

return tmain<int, 10>(argc); // expected-note {{in instantiation of function template specialization 'tmain<int, 10>' requested here}}
}
7 changes: 7 additions & 0 deletions clang/test/OpenMP/teams_num_teams_messages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ T tmain(T argc) {
#pragma omp target
#pragma omp teams num_teams (1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}}
foo();
#pragma omp target
#pragma omp teams thread_limit(1, 2, 3) // expected-error {{only one expression allowed in 'thread_limit' clause}}
foo();

return 0;
}
Expand Down Expand Up @@ -118,5 +121,9 @@ int main(int argc, char **argv) {
#pragma omp teams num_teams (1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}}
foo();

#pragma omp target
#pragma omp teams thread_limit(1, 2, 3) // expected-error {{only one expression allowed in 'thread_limit' clause}}
foo();

return tmain<int, 10>(argc); // expected-note {{in instantiation of function template specialization 'tmain<int, 10>' requested here}}
}
12 changes: 11 additions & 1 deletion clang/tools/clang-format/ClangFormat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ static cl::opt<bool> FailOnIncompleteFormat(
cl::desc("If set, fail with exit code 1 on incomplete format."),
cl::init(false), cl::cat(ClangFormatCategory));

static cl::opt<bool> ListIgnored("list-ignored",
cl::desc("List ignored files."),
cl::cat(ClangFormatCategory), cl::Hidden);

namespace clang {
namespace format {

Expand Down Expand Up @@ -715,7 +719,13 @@ int main(int argc, const char **argv) {
unsigned FileNo = 1;
bool Error = false;
for (const auto &FileName : FileNames) {
if (isIgnored(FileName))
const bool Ignored = isIgnored(FileName);
if (ListIgnored) {
if (Ignored)
outs() << FileName << '\n';
continue;
}
if (Ignored)
continue;
if (Verbose) {
errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "
Expand Down
15 changes: 13 additions & 2 deletions clang/tools/clang-format/git-clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,12 @@ def main():
# those files.
cd_to_toplevel()
filter_symlinks(changed_lines)
filter_ignored_files(changed_lines, binary=opts.binary)
if opts.verbose >= 1:
ignored_files.difference_update(changed_lines)
if ignored_files:
print(
'Ignoring changes in the following files (wrong extension or symlink):')
print('Ignoring the following files (wrong extension, symlink, or '
'ignored by clang-format):')
for filename in ignored_files:
print(' %s' % filename)
if changed_lines:
Expand Down Expand Up @@ -399,6 +400,16 @@ def filter_symlinks(dictionary):
del dictionary[filename]


def filter_ignored_files(dictionary, binary):
"""Delete every key in `dictionary` that is ignored by clang-format."""
ignored_files = run(binary, '-list-ignored', *dictionary.keys())
if not ignored_files:
return
ignored_files = ignored_files.split('\n')
for filename in ignored_files:
del dictionary[filename]


def cd_to_toplevel():
"""Change to the top level of the git repository."""
toplevel = run('git', 'rev-parse', '--show-toplevel')
Expand Down
2 changes: 1 addition & 1 deletion clang/tools/libclang/CIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2522,8 +2522,8 @@ void OMPClauseEnqueue::VisitOMPNumTeamsClause(const OMPNumTeamsClause *C) {

void OMPClauseEnqueue::VisitOMPThreadLimitClause(
const OMPThreadLimitClause *C) {
VisitOMPClauseList(C);
VisitOMPClauseWithPreInit(C);
Visitor->AddStmt(C->getThreadLimit());
}

void OMPClauseEnqueue::VisitOMPPriorityClause(const OMPPriorityClause *C) {
Expand Down
8 changes: 8 additions & 0 deletions clang/unittests/Format/ConfigParseTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,14 @@ TEST(ConfigParseTest, ParsesConfiguration) {
CHECK_PARSE("BreakBeforeBinaryOperators: true", BreakBeforeBinaryOperators,
FormatStyle::BOS_All);

Style.BreakBinaryOperations = FormatStyle::BBO_Never;
CHECK_PARSE("BreakBinaryOperations: OnePerLine", BreakBinaryOperations,
FormatStyle::BBO_OnePerLine);
CHECK_PARSE("BreakBinaryOperations: RespectPrecedence", BreakBinaryOperations,
FormatStyle::BBO_RespectPrecedence);
CHECK_PARSE("BreakBinaryOperations: Never", BreakBinaryOperations,
FormatStyle::BBO_Never);

Style.BreakConstructorInitializers = FormatStyle::BCIS_BeforeColon;
CHECK_PARSE("BreakConstructorInitializers: BeforeComma",
BreakConstructorInitializers, FormatStyle::BCIS_BeforeComma);
Expand Down
239 changes: 239 additions & 0 deletions clang/unittests/Format/FormatTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27659,6 +27659,245 @@ TEST_F(FormatTest, SpaceBetweenKeywordAndLiteral) {
verifyFormat("return sizeof \"5\";");
}

TEST_F(FormatTest, BreakBinaryOperations) {
auto Style = getLLVMStyleWithColumns(60);
EXPECT_EQ(Style.BreakBinaryOperations, FormatStyle::BBO_Never);

// Logical operations
verifyFormat("if (condition1 && condition2) {\n"
"}",
Style);

verifyFormat("if (condition1 && condition2 &&\n"
" (condition3 || condition4) && condition5 &&\n"
" condition6) {\n"
"}",
Style);

verifyFormat("if (loooooooooooooooooooooongcondition1 &&\n"
" loooooooooooooooooooooongcondition2) {\n"
"}",
Style);

// Arithmetic
verifyFormat("const int result = lhs + rhs;", Style);

verifyFormat("const int result = loooooooongop1 + looooooooongop2 +\n"
" loooooooooooooooooooooongop3;",
Style);

verifyFormat("result = longOperand1 + longOperand2 -\n"
" (longOperand3 + longOperand4) -\n"
" longOperand5 * longOperand6;",
Style);

verifyFormat("const int result =\n"
" operand1 + operand2 - (operand3 + operand4);",
Style);

Style.BreakBinaryOperations = FormatStyle::BBO_OnePerLine;

// Logical operations
verifyFormat("if (condition1 && condition2) {\n"
"}",
Style);

verifyFormat("if (condition1 && // comment\n"
" condition2 &&\n"
" (condition3 || condition4) && // comment\n"
" condition5 &&\n"
" condition6) {\n"
"}",
Style);

verifyFormat("if (loooooooooooooooooooooongcondition1 &&\n"
" loooooooooooooooooooooongcondition2) {\n"
"}",
Style);

// Arithmetic
verifyFormat("const int result = lhs + rhs;", Style);

verifyFormat("result = loooooooooooooooooooooongop1 +\n"
" loooooooooooooooooooooongop2 +\n"
" loooooooooooooooooooooongop3;",
Style);

verifyFormat("const int result =\n"
" operand1 + operand2 - (operand3 + operand4);",
Style);

verifyFormat("result = longOperand1 +\n"
" longOperand2 -\n"
" (longOperand3 + longOperand4) -\n"
" longOperand5 +\n"
" longOperand6;",
Style);

verifyFormat("result = operand1 +\n"
" operand2 -\n"
" operand3 +\n"
" operand4 -\n"
" operand5 +\n"
" operand6;",
Style);

// Ensure mixed precedence operations are handled properly
verifyFormat("result = op1 + op2 * op3 - op4;", Style);

verifyFormat("result = operand1 +\n"
" operand2 /\n"
" operand3 +\n"
" operand4 /\n"
" operand5 *\n"
" operand6;",
Style);

verifyFormat("result = operand1 *\n"
" operand2 -\n"
" operand3 *\n"
" operand4 -\n"
" operand5 +\n"
" operand6;",
Style);

verifyFormat("result = operand1 *\n"
" (operand2 - operand3 * operand4) -\n"
" operand5 +\n"
" operand6;",
Style);

verifyFormat("result = operand1.member *\n"
" (operand2.member() - operand3->mem * operand4) -\n"
" operand5.member() +\n"
" operand6->member;",
Style);

Style.BreakBinaryOperations = FormatStyle::BBO_RespectPrecedence;
verifyFormat("result = op1 + op2 * op3 - op4;", Style);

verifyFormat("result = operand1 +\n"
" operand2 / operand3 +\n"
" operand4 / operand5 * operand6;",
Style);

verifyFormat("result = operand1 * operand2 -\n"
" operand3 * operand4 -\n"
" operand5 +\n"
" operand6;",
Style);

verifyFormat("result = operand1 * (operand2 - operand3 * operand4) -\n"
" operand5 +\n"
" operand6;",
Style);

verifyFormat("std::uint32_t a = byte_buffer[0] |\n"
" byte_buffer[1] << 8 |\n"
" byte_buffer[2] << 16 |\n"
" byte_buffer[3] << 24;",
Style);

Style.BreakBinaryOperations = FormatStyle::BBO_OnePerLine;
Style.BreakBeforeBinaryOperators = FormatStyle::BOS_NonAssignment;

// Logical operations
verifyFormat("if (condition1 && condition2) {\n"
"}",
Style);

verifyFormat("if (loooooooooooooooooooooongcondition1\n"
" && loooooooooooooooooooooongcondition2) {\n"
"}",
Style);

// Arithmetic
verifyFormat("const int result = lhs + rhs;", Style);

verifyFormat("result = loooooooooooooooooooooongop1\n"
" + loooooooooooooooooooooongop2\n"
" + loooooooooooooooooooooongop3;",
Style);

verifyFormat("const int result =\n"
" operand1 + operand2 - (operand3 + operand4);",
Style);

verifyFormat("result = longOperand1\n"
" + longOperand2\n"
" - (longOperand3 + longOperand4)\n"
" - longOperand5\n"
" + longOperand6;",
Style);

verifyFormat("result = operand1\n"
" + operand2\n"
" - operand3\n"
" + operand4\n"
" - operand5\n"
" + operand6;",
Style);

// Ensure mixed precedence operations are handled properly
verifyFormat("result = op1 + op2 * op3 - op4;", Style);

verifyFormat("result = operand1\n"
" + operand2\n"
" / operand3\n"
" + operand4\n"
" / operand5\n"
" * operand6;",
Style);

verifyFormat("result = operand1\n"
" * operand2\n"
" - operand3\n"
" * operand4\n"
" - operand5\n"
" + operand6;",
Style);

verifyFormat("result = operand1\n"
" * (operand2 - operand3 * operand4)\n"
" - operand5\n"
" + operand6;",
Style);

verifyFormat("std::uint32_t a = byte_buffer[0]\n"
" | byte_buffer[1]\n"
" << 8\n"
" | byte_buffer[2]\n"
" << 16\n"
" | byte_buffer[3]\n"
" << 24;",
Style);

Style.BreakBinaryOperations = FormatStyle::BBO_RespectPrecedence;
verifyFormat("result = op1 + op2 * op3 - op4;", Style);

verifyFormat("result = operand1\n"
" + operand2 / operand3\n"
" + operand4 / operand5 * operand6;",
Style);

verifyFormat("result = operand1 * operand2\n"
" - operand3 * operand4\n"
" - operand5\n"
" + operand6;",
Style);

verifyFormat("result = operand1 * (operand2 - operand3 * operand4)\n"
" - operand5\n"
" + operand6;",
Style);

verifyFormat("std::uint32_t a = byte_buffer[0]\n"
" | byte_buffer[1] << 8\n"
" | byte_buffer[2] << 16\n"
" | byte_buffer[3] << 24;",
Style);
}

} // namespace
} // namespace test
} // namespace format
Expand Down
77 changes: 33 additions & 44 deletions compiler-rt/lib/dfsan/dfsan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -473,12 +473,10 @@ __dfsan_mem_shadow_origin_conditional_exchange(u8 condition, void *target,
}
}

namespace __dfsan {

bool dfsan_inited = false;
bool dfsan_init_is_running = false;
bool __dfsan::dfsan_inited;
bool __dfsan::dfsan_init_is_running;

void dfsan_copy_memory(void *dst, const void *src, uptr size) {
void __dfsan::dfsan_copy_memory(void *dst, const void *src, uptr size) {
internal_memcpy(dst, src, size);
dfsan_mem_shadow_transfer(dst, src, size);
if (dfsan_get_track_origins())
Expand Down Expand Up @@ -545,7 +543,8 @@ static void ReleaseOrClearShadows(void *addr, uptr size) {
}
}

void SetShadow(dfsan_label label, void *addr, uptr size, dfsan_origin origin) {
static void SetShadow(dfsan_label label, void *addr, uptr size,
dfsan_origin origin) {
if (0 != label) {
const uptr beg_shadow_addr = (uptr)__dfsan::shadow_for(addr);
internal_memset((void *)beg_shadow_addr, label, size);
Expand All @@ -560,8 +559,6 @@ void SetShadow(dfsan_label label, void *addr, uptr size, dfsan_origin origin) {
ReleaseOrClearShadows(addr, size);
}

} // namespace __dfsan

// If the label s is tainted, set the size bytes from the address p to be a new
// origin chain with the previous ID o and the current stack trace. This is
// used by instrumentation to reduce code size when too much code is inserted.
Expand All @@ -576,7 +573,7 @@ extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __dfsan_maybe_store_origin(

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __dfsan_set_label(
dfsan_label label, dfsan_origin origin, void *addr, uptr size) {
__dfsan::SetShadow(label, addr, size, origin);
SetShadow(label, addr, size, origin);
}

SANITIZER_INTERFACE_ATTRIBUTE
Expand All @@ -587,7 +584,7 @@ void dfsan_set_label(dfsan_label label, void *addr, uptr size) {
GET_STORE_STACK_TRACE_PC_BP(pc, bp);
init_origin = ChainOrigin(0, &stack, true);
}
__dfsan::SetShadow(label, addr, size, init_origin);
SetShadow(label, addr, size, init_origin);
}

SANITIZER_INTERFACE_ATTRIBUTE
Expand Down Expand Up @@ -665,9 +662,10 @@ dfsan_has_label(dfsan_label label, dfsan_label elem) {
}

namespace __dfsan {

typedef void (*dfsan_conditional_callback_t)(dfsan_label label,
dfsan_origin origin);

} // namespace __dfsan
static dfsan_conditional_callback_t conditional_callback = nullptr;
static dfsan_label labels_in_signal_conditional = 0;

Expand Down Expand Up @@ -696,35 +694,34 @@ static void ConditionalCallback(dfsan_label label, dfsan_origin origin) {
conditional_callback(label, origin);
}

} // namespace __dfsan

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__dfsan_conditional_callback_origin(dfsan_label label, dfsan_origin origin) {
__dfsan::ConditionalCallback(label, origin);
ConditionalCallback(label, origin);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __dfsan_conditional_callback(
dfsan_label label) {
__dfsan::ConditionalCallback(label, 0);
ConditionalCallback(label, 0);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void dfsan_set_conditional_callback(
__dfsan::dfsan_conditional_callback_t callback) {
__dfsan::conditional_callback = callback;
conditional_callback = callback;
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE dfsan_label
dfsan_get_labels_in_signal_conditional() {
return __dfsan::labels_in_signal_conditional;
return labels_in_signal_conditional;
}

namespace __dfsan {

typedef void (*dfsan_reaches_function_callback_t)(dfsan_label label,
dfsan_origin origin,
const char *file,
unsigned int line,
const char *function);

} // namespace __dfsan
static dfsan_reaches_function_callback_t reaches_function_callback = nullptr;
static dfsan_label labels_in_signal_reaches_function = 0;

Expand Down Expand Up @@ -753,71 +750,69 @@ static void ReachesFunctionCallback(dfsan_label label, dfsan_origin origin,
reaches_function_callback(label, origin, file, line, function);
}

} // namespace __dfsan

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__dfsan_reaches_function_callback_origin(dfsan_label label, dfsan_origin origin,
const char *file, unsigned int line,
const char *function) {
__dfsan::ReachesFunctionCallback(label, origin, file, line, function);
ReachesFunctionCallback(label, origin, file, line, function);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__dfsan_reaches_function_callback(dfsan_label label, const char *file,
unsigned int line, const char *function) {
__dfsan::ReachesFunctionCallback(label, 0, file, line, function);
ReachesFunctionCallback(label, 0, file, line, function);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
dfsan_set_reaches_function_callback(
__dfsan::dfsan_reaches_function_callback_t callback) {
__dfsan::reaches_function_callback = callback;
reaches_function_callback = callback;
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE dfsan_label
dfsan_get_labels_in_signal_reaches_function() {
return __dfsan::labels_in_signal_reaches_function;
return labels_in_signal_reaches_function;
}

namespace {
class Decorator : public __sanitizer::SanitizerCommonDecorator {
public:
Decorator() : SanitizerCommonDecorator() {}
const char *Origin() const { return Magenta(); }
};
} // namespace

namespace {

void PrintNoOriginTrackingWarning() {
static void PrintNoOriginTrackingWarning() {
Decorator d;
Printf(
" %sDFSan: origin tracking is not enabled. Did you specify the "
"-dfsan-track-origins=1 option?%s\n",
d.Warning(), d.Default());
}

void PrintNoTaintWarning(const void *address) {
static void PrintNoTaintWarning(const void *address) {
Decorator d;
Printf(" %sDFSan: no tainted value at %x%s\n", d.Warning(), address,
d.Default());
}

void PrintInvalidOriginWarning(dfsan_label label, const void *address) {
static void PrintInvalidOriginWarning(dfsan_label label, const void *address) {
Decorator d;
Printf(
" %sTaint value 0x%x (at %p) has invalid origin tracking. This can "
"be a DFSan bug.%s\n",
d.Warning(), label, address, d.Default());
}

void PrintInvalidOriginIdWarning(dfsan_origin origin) {
static void PrintInvalidOriginIdWarning(dfsan_origin origin) {
Decorator d;
Printf(
" %sOrigin Id %d has invalid origin tracking. This can "
"be a DFSan bug.%s\n",
d.Warning(), origin, d.Default());
}

bool PrintOriginTraceFramesToStr(Origin o, InternalScopedString *out) {
static bool PrintOriginTraceFramesToStr(Origin o, InternalScopedString *out) {
Decorator d;
bool found = false;

Expand All @@ -841,8 +836,8 @@ bool PrintOriginTraceFramesToStr(Origin o, InternalScopedString *out) {
return found;
}

bool PrintOriginTraceToStr(const void *addr, const char *description,
InternalScopedString *out) {
static bool PrintOriginTraceToStr(const void *addr, const char *description,
InternalScopedString *out) {
CHECK(out);
CHECK(dfsan_get_track_origins());
Decorator d;
Expand All @@ -860,8 +855,6 @@ bool PrintOriginTraceToStr(const void *addr, const char *description,
return PrintOriginTraceFramesToStr(o, out);
}

} // namespace

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void dfsan_print_origin_trace(
const void *addr, const char *description) {
if (!dfsan_get_track_origins()) {
Expand Down Expand Up @@ -1091,8 +1084,8 @@ extern "C" void dfsan_flush() {
Die();
}
}
__dfsan::labels_in_signal_conditional = 0;
__dfsan::labels_in_signal_reaches_function = 0;
labels_in_signal_conditional = 0;
labels_in_signal_reaches_function = 0;
}

// TODO: CheckMemoryLayoutSanity is based on msan.
Expand Down Expand Up @@ -1167,7 +1160,7 @@ static bool ProtectMemoryRange(uptr beg, uptr size, const char *name) {

// TODO: InitShadow is based on msan.
// Consider refactoring these into a shared implementation.
bool InitShadow(bool init_origins, bool dry_run) {
static bool InitShadow(bool init_origins, bool dry_run) {
// Let user know mapping parameters first.
VPrintf(1, "dfsan_init %p\n", (void *)&__dfsan::dfsan_init);
for (unsigned i = 0; i < kMemoryLayoutSize; ++i)
Expand Down Expand Up @@ -1227,7 +1220,7 @@ bool InitShadow(bool init_origins, bool dry_run) {
return true;
}

bool InitShadowWithReExec(bool init_origins) {
static bool InitShadowWithReExec(bool init_origins) {
// Start with dry run: check layout is ok, but don't print warnings because
// warning messages will cause tests to fail (even if we successfully re-exec
// after the warning).
Expand Down Expand Up @@ -1290,11 +1283,7 @@ static void DFsanInit(int argc, char **argv, char **envp) {
dfsan_inited = true;
}

namespace __dfsan {

void dfsan_init() { DFsanInit(0, nullptr, nullptr); }

} // namespace __dfsan
void __dfsan::dfsan_init() { DFsanInit(0, nullptr, nullptr); }

#if SANITIZER_CAN_USE_PREINIT_ARRAY
__attribute__((section(".preinit_array"),
Expand Down
39 changes: 20 additions & 19 deletions compiler-rt/lib/dfsan/dfsan_allocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
#include "sanitizer_common/sanitizer_allocator_report.h"
#include "sanitizer_common/sanitizer_errno.h"

namespace __dfsan {
using namespace __dfsan;

namespace {

struct Metadata {
uptr requested_size;
Expand Down Expand Up @@ -67,8 +69,9 @@ static AllocatorCache fallback_allocator_cache;
static StaticSpinMutex fallback_mutex;

static uptr max_malloc_size;
} // namespace

void dfsan_allocator_init() {
void __dfsan::dfsan_allocator_init() {
SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
allocator.Init(common_flags()->allocator_release_to_os_interval_ms);
if (common_flags()->max_allocation_size_mb)
Expand All @@ -78,7 +81,7 @@ void dfsan_allocator_init() {
max_malloc_size = kMaxAllowedMallocSize;
}

AllocatorCache *GetAllocatorCache(DFsanThreadLocalMallocStorage *ms) {
static AllocatorCache *GetAllocatorCache(DFsanThreadLocalMallocStorage *ms) {
CHECK(ms);
CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator_cache));
return reinterpret_cast<AllocatorCache *>(ms->allocator_cache);
Expand Down Expand Up @@ -133,7 +136,7 @@ static void *DFsanAllocate(uptr size, uptr alignment, bool zeroise) {
return allocated;
}

void dfsan_deallocate(void *p) {
void __dfsan::dfsan_deallocate(void *p) {
CHECK(p);
Metadata *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(p));
uptr size = meta->requested_size;
Expand All @@ -151,7 +154,7 @@ void dfsan_deallocate(void *p) {
}
}

void *DFsanReallocate(void *old_p, uptr new_size, uptr alignment) {
static void *DFsanReallocate(void *old_p, uptr new_size, uptr alignment) {
Metadata *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(old_p));
uptr old_size = meta->requested_size;
uptr actually_allocated_size = allocator.GetActuallyAllocatedSize(old_p);
Expand All @@ -171,7 +174,7 @@ void *DFsanReallocate(void *old_p, uptr new_size, uptr alignment) {
return new_p;
}

void *DFsanCalloc(uptr nmemb, uptr size) {
static void *DFsanCalloc(uptr nmemb, uptr size) {
if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
if (AllocatorMayReturnNull())
return nullptr;
Expand Down Expand Up @@ -209,15 +212,15 @@ static uptr AllocationSizeFast(const void *p) {
return reinterpret_cast<Metadata *>(allocator.GetMetaData(p))->requested_size;
}

void *dfsan_malloc(uptr size) {
void *__dfsan::dfsan_malloc(uptr size) {
return SetErrnoOnNull(DFsanAllocate(size, sizeof(u64), false /*zeroise*/));
}

void *dfsan_calloc(uptr nmemb, uptr size) {
void *__dfsan::dfsan_calloc(uptr nmemb, uptr size) {
return SetErrnoOnNull(DFsanCalloc(nmemb, size));
}

void *dfsan_realloc(void *ptr, uptr size) {
void *__dfsan::dfsan_realloc(void *ptr, uptr size) {
if (!ptr)
return SetErrnoOnNull(DFsanAllocate(size, sizeof(u64), false /*zeroise*/));
if (size == 0) {
Expand All @@ -227,7 +230,7 @@ void *dfsan_realloc(void *ptr, uptr size) {
return SetErrnoOnNull(DFsanReallocate(ptr, size, sizeof(u64)));
}

void *dfsan_reallocarray(void *ptr, uptr nmemb, uptr size) {
void *__dfsan::dfsan_reallocarray(void *ptr, uptr nmemb, uptr size) {
if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
errno = errno_ENOMEM;
if (AllocatorMayReturnNull())
Expand All @@ -238,12 +241,12 @@ void *dfsan_reallocarray(void *ptr, uptr nmemb, uptr size) {
return dfsan_realloc(ptr, nmemb * size);
}

void *dfsan_valloc(uptr size) {
void *__dfsan::dfsan_valloc(uptr size) {
return SetErrnoOnNull(
DFsanAllocate(size, GetPageSizeCached(), false /*zeroise*/));
}

void *dfsan_pvalloc(uptr size) {
void *__dfsan::dfsan_pvalloc(uptr size) {
uptr PageSize = GetPageSizeCached();
if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {
errno = errno_ENOMEM;
Expand All @@ -257,7 +260,7 @@ void *dfsan_pvalloc(uptr size) {
return SetErrnoOnNull(DFsanAllocate(size, PageSize, false /*zeroise*/));
}

void *dfsan_aligned_alloc(uptr alignment, uptr size) {
void *__dfsan::dfsan_aligned_alloc(uptr alignment, uptr size) {
if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {
errno = errno_EINVAL;
if (AllocatorMayReturnNull())
Expand All @@ -268,7 +271,7 @@ void *dfsan_aligned_alloc(uptr alignment, uptr size) {
return SetErrnoOnNull(DFsanAllocate(size, alignment, false /*zeroise*/));
}

void *dfsan_memalign(uptr alignment, uptr size) {
void *__dfsan::dfsan_memalign(uptr alignment, uptr size) {
if (UNLIKELY(!IsPowerOfTwo(alignment))) {
errno = errno_EINVAL;
if (AllocatorMayReturnNull())
Expand All @@ -279,7 +282,7 @@ void *dfsan_memalign(uptr alignment, uptr size) {
return SetErrnoOnNull(DFsanAllocate(size, alignment, false /*zeroise*/));
}

int dfsan_posix_memalign(void **memptr, uptr alignment, uptr size) {
int __dfsan::dfsan_posix_memalign(void **memptr, uptr alignment, uptr size) {
if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
if (AllocatorMayReturnNull())
return errno_EINVAL;
Expand All @@ -295,10 +298,7 @@ int dfsan_posix_memalign(void **memptr, uptr alignment, uptr size) {
return 0;
}

} // namespace __dfsan

using namespace __dfsan;

extern "C" {
uptr __sanitizer_get_current_allocated_bytes() {
uptr stats[AllocatorStatCount];
allocator.GetStats(stats);
Expand Down Expand Up @@ -331,3 +331,4 @@ uptr __sanitizer_get_allocated_size_fast(const void *p) {
DCHECK_EQ(ret, __sanitizer_get_allocated_size(p));
return ret;
}
}
14 changes: 8 additions & 6 deletions compiler-rt/lib/dfsan/dfsan_chained_origin_depot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,18 @@

#include "dfsan_chained_origin_depot.h"

namespace __dfsan {
using namespace __dfsan;

static ChainedOriginDepot chainedOriginDepot;

ChainedOriginDepot* GetChainedOriginDepot() { return &chainedOriginDepot; }
ChainedOriginDepot* __dfsan::GetChainedOriginDepot() {
return &chainedOriginDepot;
}

void ChainedOriginDepotLockBeforeFork() { chainedOriginDepot.LockBeforeFork(); }
void __dfsan::ChainedOriginDepotLockBeforeFork() {
chainedOriginDepot.LockBeforeFork();
}

void ChainedOriginDepotUnlockAfterFork(bool fork_child) {
void __dfsan::ChainedOriginDepotUnlockAfterFork(bool fork_child) {
chainedOriginDepot.UnlockAfterFork(fork_child);
}

} // namespace __dfsan
5 changes: 4 additions & 1 deletion compiler-rt/lib/dfsan/dfsan_custom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void f(__VA_ARGS__);
SANITIZER_INTERFACE_ATTRIBUTE void __dfso_##fun() ALIAS(__dfso_##real);

// Async-safe, non-reentrant spin lock.
namespace {
class SignalSpinLocker {
public:
SignalSpinLocker() {
Expand All @@ -80,6 +81,7 @@ class SignalSpinLocker {
SignalSpinLocker(const SignalSpinLocker &) = delete;
SignalSpinLocker &operator=(const SignalSpinLocker &) = delete;
};
} // namespace

StaticSpinMutex SignalSpinLocker::sigactions_mu;

Expand Down Expand Up @@ -937,7 +939,8 @@ struct dl_iterate_phdr_info {
void *data;
};

int dl_iterate_phdr_cb(struct dl_phdr_info *info, size_t size, void *data) {
static int dl_iterate_phdr_cb(struct dl_phdr_info *info, size_t size,
void *data) {
dl_iterate_phdr_info *dipi = (dl_iterate_phdr_info *)data;
dfsan_set_label(0, *info);
dfsan_set_label(0, const_cast<char *>(info->dlpi_name),
Expand Down
65 changes: 31 additions & 34 deletions compiler-rt/lib/dfsan/dfsan_interceptors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,80 +25,79 @@
#include "sanitizer_common/sanitizer_posix.h"
#include "sanitizer_common/sanitizer_tls_get_addr.h"

using namespace __dfsan;
using namespace __sanitizer;

static bool interceptors_initialized;

struct DlsymAlloc : public DlSymAllocator<DlsymAlloc> {
static bool UseImpl() { return !__dfsan::dfsan_inited; }
static bool UseImpl() { return !dfsan_inited; }
};

INTERCEPTOR(void *, reallocarray, void *ptr, SIZE_T nmemb, SIZE_T size) {
return __dfsan::dfsan_reallocarray(ptr, nmemb, size);
return dfsan_reallocarray(ptr, nmemb, size);
}

INTERCEPTOR(void *, __libc_memalign, SIZE_T alignment, SIZE_T size) {
void *ptr = __dfsan::dfsan_memalign(alignment, size);
void *ptr = dfsan_memalign(alignment, size);
if (ptr)
DTLS_on_libc_memalign(ptr, size);
return ptr;
}

INTERCEPTOR(void *, aligned_alloc, SIZE_T alignment, SIZE_T size) {
return __dfsan::dfsan_aligned_alloc(alignment, size);
return dfsan_aligned_alloc(alignment, size);
}

INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) {
if (DlsymAlloc::Use())
return DlsymAlloc::Callocate(nmemb, size);
return __dfsan::dfsan_calloc(nmemb, size);
return dfsan_calloc(nmemb, size);
}

INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) {
if (DlsymAlloc::Use() || DlsymAlloc::PointerIsMine(ptr))
return DlsymAlloc::Realloc(ptr, size);
return __dfsan::dfsan_realloc(ptr, size);
return dfsan_realloc(ptr, size);
}

INTERCEPTOR(void *, malloc, SIZE_T size) {
if (DlsymAlloc::Use())
return DlsymAlloc::Allocate(size);
return __dfsan::dfsan_malloc(size);
return dfsan_malloc(size);
}

INTERCEPTOR(void, free, void *ptr) {
if (!ptr)
return;
if (DlsymAlloc::PointerIsMine(ptr))
return DlsymAlloc::Free(ptr);
return __dfsan::dfsan_deallocate(ptr);
return dfsan_deallocate(ptr);
}

INTERCEPTOR(void, cfree, void *ptr) {
if (!ptr)
return;
if (DlsymAlloc::PointerIsMine(ptr))
return DlsymAlloc::Free(ptr);
return __dfsan::dfsan_deallocate(ptr);
return dfsan_deallocate(ptr);
}

INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) {
CHECK_NE(memptr, 0);
int res = __dfsan::dfsan_posix_memalign(memptr, alignment, size);
int res = dfsan_posix_memalign(memptr, alignment, size);
if (!res)
dfsan_set_label(0, memptr, sizeof(*memptr));
return res;
}

INTERCEPTOR(void *, memalign, SIZE_T alignment, SIZE_T size) {
return __dfsan::dfsan_memalign(alignment, size);
return dfsan_memalign(alignment, size);
}

INTERCEPTOR(void *, valloc, SIZE_T size) { return __dfsan::dfsan_valloc(size); }
INTERCEPTOR(void *, valloc, SIZE_T size) { return dfsan_valloc(size); }

INTERCEPTOR(void *, pvalloc, SIZE_T size) {
return __dfsan::dfsan_pvalloc(size);
}
INTERCEPTOR(void *, pvalloc, SIZE_T size) { return dfsan_pvalloc(size); }

INTERCEPTOR(void, mallinfo, __sanitizer_struct_mallinfo *sret) {
internal_memset(sret, 0, sizeof(*sret));
Expand All @@ -115,16 +114,16 @@ INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
return __sanitizer_get_allocated_size(ptr);
}

#define ENSURE_DFSAN_INITED() \
do { \
CHECK(!__dfsan::dfsan_init_is_running); \
if (!__dfsan::dfsan_inited) { \
__dfsan::dfsan_init(); \
} \
#define ENSURE_DFSAN_INITED() \
do { \
CHECK(!dfsan_init_is_running); \
if (!dfsan_inited) { \
dfsan_init(); \
} \
} while (0)

#define COMMON_INTERCEPTOR_ENTER(func, ...) \
if (__dfsan::dfsan_init_is_running) \
if (dfsan_init_is_running) \
return REAL(func)(__VA_ARGS__); \
ENSURE_DFSAN_INITED(); \
dfsan_set_label(0, __errno_location(), sizeof(int));
Expand All @@ -133,7 +132,7 @@ INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags,
int fd, OFF_T offset) {
if (common_flags()->detect_write_exec)
ReportMmapWriteExec(prot, flags);
if (!__dfsan::dfsan_inited)
if (!dfsan_inited)
return (void *)internal_mmap(addr, length, prot, flags, fd, offset);
COMMON_INTERCEPTOR_ENTER(mmap, addr, length, prot, flags, fd, offset);
void *res = REAL(mmap)(addr, length, prot, flags, fd, offset);
Expand All @@ -147,7 +146,7 @@ INTERCEPTOR(void *, mmap64, void *addr, SIZE_T length, int prot, int flags,
int fd, OFF64_T offset) {
if (common_flags()->detect_write_exec)
ReportMmapWriteExec(prot, flags);
if (!__dfsan::dfsan_inited)
if (!dfsan_inited)
return (void *)internal_mmap(addr, length, prot, flags, fd, offset);
COMMON_INTERCEPTOR_ENTER(mmap64, addr, length, prot, flags, fd, offset);
void *res = REAL(mmap64)(addr, length, prot, flags, fd, offset);
Expand All @@ -158,7 +157,7 @@ INTERCEPTOR(void *, mmap64, void *addr, SIZE_T length, int prot, int flags,
}

INTERCEPTOR(int, munmap, void *addr, SIZE_T length) {
if (!__dfsan::dfsan_inited)
if (!dfsan_inited)
return internal_munmap(addr, length);
COMMON_INTERCEPTOR_ENTER(munmap, addr, length);
int res = REAL(munmap)(addr, length);
Expand All @@ -167,12 +166,12 @@ INTERCEPTOR(int, munmap, void *addr, SIZE_T length) {
return res;
}

#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
if (__dfsan::DFsanThread *t = __dfsan::GetCurrentThread()) { \
*begin = t->tls_begin(); \
*end = t->tls_end(); \
} else { \
*begin = *end = 0; \
#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
if (DFsanThread *t = GetCurrentThread()) { \
*begin = t->tls_begin(); \
*end = t->tls_end(); \
} else { \
*begin = *end = 0; \
}
#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ptr, size) \
dfsan_set_label(0, ptr, size)
Expand All @@ -190,8 +189,7 @@ INTERCEPTOR(void *, __tls_get_addr, void *arg) {
return res;
}

namespace __dfsan {
void initialize_interceptors() {
void __dfsan::initialize_interceptors() {
CHECK(!interceptors_initialized);

INTERCEPT_FUNCTION(aligned_alloc);
Expand All @@ -217,4 +215,3 @@ void initialize_interceptors() {

interceptors_initialized = true;
}
} // namespace __dfsan
12 changes: 5 additions & 7 deletions compiler-rt/lib/dfsan/dfsan_thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include "dfsan.h"
#include "sanitizer_common/sanitizer_tls_get_addr.h"

namespace __dfsan {
using namespace __dfsan;

DFsanThread *DFsanThread::Create(thread_callback_t start_routine, void *arg,
bool track_origins) {
Expand Down Expand Up @@ -99,17 +99,17 @@ bool DFsanThread::AddrIsInStack(uptr addr) {
static pthread_key_t tsd_key;
static bool tsd_key_inited = false;

void DFsanTSDInit(void (*destructor)(void *tsd)) {
void __dfsan::DFsanTSDInit(void (*destructor)(void *tsd)) {
CHECK(!tsd_key_inited);
tsd_key_inited = true;
CHECK_EQ(0, pthread_key_create(&tsd_key, destructor));
}

static THREADLOCAL DFsanThread *dfsan_current_thread;

DFsanThread *GetCurrentThread() { return dfsan_current_thread; }
DFsanThread *__dfsan::GetCurrentThread() { return dfsan_current_thread; }

void SetCurrentThread(DFsanThread *t) {
void __dfsan::SetCurrentThread(DFsanThread *t) {
// Make sure we do not reset the current DFsanThread.
CHECK_EQ(0, dfsan_current_thread);
dfsan_current_thread = t;
Expand All @@ -118,7 +118,7 @@ void SetCurrentThread(DFsanThread *t) {
pthread_setspecific(tsd_key, t);
}

void DFsanTSDDtor(void *tsd) {
void __dfsan::DFsanTSDDtor(void *tsd) {
DFsanThread *t = (DFsanThread *)tsd;
if (t->destructor_iterations_ > 1) {
t->destructor_iterations_--;
Expand All @@ -130,5 +130,3 @@ void DFsanTSDDtor(void *tsd) {
atomic_signal_fence(memory_order_seq_cst);
DFsanThread::TSDDtor(tsd);
}

} // namespace __dfsan
4 changes: 2 additions & 2 deletions compiler-rt/test/asan/TestCases/Linux/aligned_delete_test.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// RUN: %clangxx_asan -std=c++1z -faligned-allocation -fsanitize-recover=address -O0 %s -o %t
// RUN: %clangxx_asan -fno-sized-deallocation -fsanitize-recover=address -O0 %s -o %t
// RUN: %env_asan_opts=new_delete_type_mismatch=1:halt_on_error=false:detect_leaks=false %run %t 2>&1 | FileCheck %s
// RUN: %env_asan_opts=new_delete_type_mismatch=0 %run %t

// RUN: %clangxx_asan -std=c++1z -faligned-allocation -fsized-deallocation -fsanitize-recover=address -O0 %s -o %t
// RUN: %clangxx_asan -fsized-deallocation -fsanitize-recover=address -O0 %s -o %t
// RUN: %env_asan_opts=new_delete_type_mismatch=1:halt_on_error=false:detect_leaks=false %run %t 2>&1 | FileCheck %s
// RUN: %env_asan_opts=new_delete_type_mismatch=0 %run %t

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Check that we report new[] vs delete as alloc-dealloc-mismatch and not as
// new-delete-type-mismatch when -fsized-deallocation is enabled.

// RUN: %clangxx_asan -g %s -o %t && %env_asan_opts=alloc_dealloc_mismatch=1 not %run %t 2>&1 | FileCheck %s
// RUN: %clangxx_asan -fno-sized-deallocation -g %s -o %t && %env_asan_opts=alloc_dealloc_mismatch=1 not %run %t 2>&1 | FileCheck %s
// RUN: %clangxx_asan -fsized-deallocation -g %s -o %t && %env_asan_opts=alloc_dealloc_mismatch=1 not %run %t 2>&1 | FileCheck %s

#include <stdlib.h>
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/test/hwasan/TestCases/new-test.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Test basic new functionality.
// RUN: %clangxx_hwasan -std=c++17 %s -o %t -fsized-deallocation
// RUN: %clangxx_hwasan -std=c++17 %s -o %t
// RUN: %run %t

#include <cassert>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %clangxx -std=c++1z -faligned-allocation -O0 %s -o %t && %run %t
// RUN: %clangxx -std=c++1z -faligned-allocation -fsized-deallocation -O0 %s -o %t && %run %t
// RUN: %clangxx -fno-sized-deallocation -O0 %s -o %t && %run %t
// RUN: %clangxx -fsized-deallocation -O0 %s -o %t && %run %t

// ubsan does not intercept new/delete.
// UNSUPPORTED: ubsan
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/test/scudo/aligned-new.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %clangxx_scudo -std=c++1z -faligned-allocation %s -o %t
// RUN: %clangxx_scudo %s -o %t
// RUN: %run %t valid 2>&1
// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t invalid 2>&1
// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t invalid 2>&1 | FileCheck %s
Expand Down
4 changes: 2 additions & 2 deletions libc/newhdrgen/yaml/math.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2094,9 +2094,9 @@ functions:
- name: scalblnf16
standards:
- stdc
return_type: float16
return_type: _Float16
arguments:
- type: float16
- type: _Float16
- type: long
guard: LIBC_TYPES_HAS_FLOAT16
- name: scalblnl
Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/__atomic/atomic_ref.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ struct __atomic_ref_base {
_LIBCPP_HIDE_FROM_ABI void notify_all() const noexcept { std::__atomic_notify_all(*this); }

protected:
typedef _Tp _Aligned_Tp __attribute__((aligned(required_alignment)));
using _Aligned_Tp [[__gnu__::__aligned__(required_alignment)]] = _Tp;
_Aligned_Tp* __ptr_;

_LIBCPP_HIDE_FROM_ABI __atomic_ref_base(_Tp& __obj) : __ptr_(std::addressof(__obj)) {}
Expand Down
16 changes: 8 additions & 8 deletions libcxx/include/__bit/rotate.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,31 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp __rotl(_Tp __x, int __s) _NOEXCEPT {
static_assert(__libcpp_is_unsigned_integer<_Tp>::value, "__rotl requires an unsigned integer type");
const int __N = numeric_limits<_Tp>::digits;
int __r = __s % __N;
const int __n = numeric_limits<_Tp>::digits;
int __r = __s % __n;

if (__r == 0)
return __x;

if (__r > 0)
return (__x << __r) | (__x >> (__N - __r));
return (__x << __r) | (__x >> (__n - __r));

return (__x >> -__r) | (__x << (__N + __r));
return (__x >> -__r) | (__x << (__n + __r));
}

template <class _Tp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp __rotr(_Tp __x, int __s) _NOEXCEPT {
static_assert(__libcpp_is_unsigned_integer<_Tp>::value, "__rotr requires an unsigned integer type");
const int __N = numeric_limits<_Tp>::digits;
int __r = __s % __N;
const int __n = numeric_limits<_Tp>::digits;
int __r = __s % __n;

if (__r == 0)
return __x;

if (__r > 0)
return (__x >> __r) | (__x << (__N - __r));
return (__x >> __r) | (__x << (__n - __r));

return (__x << -__r) | (__x >> (__N + __r));
return (__x << -__r) | (__x >> (__n + __r));
}

#if _LIBCPP_STD_VER >= 20
Expand Down
4 changes: 2 additions & 2 deletions libcxx/include/__chrono/zoned_time.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@ template <class _TimeZonePtrOrName, class _Duration>
zoned_time(_TimeZonePtrOrName&&, local_time<_Duration>, choose = choose::earliest)
-> zoned_time<common_type_t<_Duration, seconds>, __time_zone_representation<_TimeZonePtrOrName>>;

template <class _Duration, class _TimeZonePtrOrName, class TimeZonePtr2>
zoned_time(_TimeZonePtrOrName&&, zoned_time<_Duration, TimeZonePtr2>, choose = choose::earliest)
template <class _Duration, class _TimeZonePtrOrName, class _TimeZonePtr2>
zoned_time(_TimeZonePtrOrName&&, zoned_time<_Duration, _TimeZonePtr2>, choose = choose::earliest)
-> zoned_time<common_type_t<_Duration, seconds>, __time_zone_representation<_TimeZonePtrOrName>>;

using zoned_seconds = zoned_time<seconds>;
Expand Down
Loading