Skip to content

Commit c49aa73

Browse files
SC llvm teamSC llvm team
authored andcommitted
Merged main:2633d94f289b into amd-gfx:38011e802b7f
Local branch amd-gfx 38011e8 Merged main:e9c4dc18bc9d into amd-gfx:162dd7710cc4 Remote branch main 2633d94 [mlir][emitc] Add a structured for operation (llvm#68206)
2 parents 38011e8 + 2633d94 commit c49aa73

File tree

44 files changed

+1420
-483
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1420
-483
lines changed

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2732,19 +2732,28 @@ bool ByteCodeExprGen<Emitter>::emitRecordDestruction(const Descriptor *Desc) {
27322732
// Arrays.
27332733
if (Desc->isArray()) {
27342734
const Descriptor *ElemDesc = Desc->ElemDesc;
2735-
const Record *ElemRecord = ElemDesc->ElemRecord;
2736-
assert(ElemRecord); // This is not a primitive array.
2735+
assert(ElemDesc);
2736+
2737+
// Don't need to do anything for these.
2738+
if (ElemDesc->isPrimitiveArray())
2739+
return this->emitPopPtr(SourceInfo{});
2740+
2741+
// If this is an array of record types, check if we need
2742+
// to call the element destructors at all. If not, try
2743+
// to save the work.
2744+
if (const Record *ElemRecord = ElemDesc->ElemRecord) {
2745+
if (const CXXDestructorDecl *Dtor = ElemRecord->getDestructor();
2746+
!Dtor || Dtor->isTrivial())
2747+
return this->emitPopPtr(SourceInfo{});
2748+
}
27372749

2738-
if (const CXXDestructorDecl *Dtor = ElemRecord->getDestructor();
2739-
Dtor && !Dtor->isTrivial()) {
2740-
for (ssize_t I = Desc->getNumElems() - 1; I >= 0; --I) {
2741-
if (!this->emitConstUint64(I, SourceInfo{}))
2742-
return false;
2743-
if (!this->emitArrayElemPtrUint64(SourceInfo{}))
2744-
return false;
2745-
if (!this->emitRecordDestruction(Desc->ElemDesc))
2746-
return false;
2747-
}
2750+
for (ssize_t I = Desc->getNumElems() - 1; I >= 0; --I) {
2751+
if (!this->emitConstUint64(I, SourceInfo{}))
2752+
return false;
2753+
if (!this->emitArrayElemPtrUint64(SourceInfo{}))
2754+
return false;
2755+
if (!this->emitRecordDestruction(ElemDesc))
2756+
return false;
27482757
}
27492758
return this->emitPopPtr(SourceInfo{});
27502759
}

clang/lib/AST/Interp/Descriptor.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,13 @@ Descriptor::Descriptor(const DeclTy &D, Record *R, MetadataSize MD,
296296
assert(Source && "Missing source");
297297
}
298298

299+
Descriptor::Descriptor(const DeclTy &D, MetadataSize MD)
300+
: Source(D), ElemSize(1), Size(ElemSize), MDSize(MD.value_or(0)),
301+
AllocSize(Size + MDSize), ElemRecord(nullptr), IsConst(true),
302+
IsMutable(false), IsTemporary(false), IsDummy(true) {
303+
assert(Source && "Missing source");
304+
}
305+
299306
QualType Descriptor::getType() const {
300307
if (auto *E = asExpr())
301308
return E->getType();

clang/lib/AST/Interp/Descriptor.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ struct Descriptor final {
8484
const unsigned ElemSize;
8585
/// Size of the storage, in host bytes.
8686
const unsigned Size;
87-
// Size of the metadata.
87+
/// Size of the metadata.
8888
const unsigned MDSize;
8989
/// Size of the allocation (storage + metadata), in host bytes.
9090
const unsigned AllocSize;
@@ -111,6 +111,8 @@ struct Descriptor final {
111111
const bool IsTemporary = false;
112112
/// Flag indicating if the block is an array.
113113
const bool IsArray = false;
114+
/// Flag indicating if this is a dummy descriptor.
115+
const bool IsDummy = false;
114116

115117
/// Storage management methods.
116118
const BlockCtorFn CtorFn = nullptr;
@@ -139,6 +141,8 @@ struct Descriptor final {
139141
Descriptor(const DeclTy &D, Record *R, MetadataSize MD, bool IsConst,
140142
bool IsTemporary, bool IsMutable);
141143

144+
Descriptor(const DeclTy &D, MetadataSize MD);
145+
142146
QualType getType() const;
143147
QualType getElemQualType() const;
144148
SourceLocation getLocation() const;
@@ -192,6 +196,8 @@ struct Descriptor final {
192196
bool isArray() const { return IsArray; }
193197
/// Checks if the descriptor is of a record.
194198
bool isRecord() const { return !IsArray && ElemRecord; }
199+
/// Checks if this is a dummy descriptor.
200+
bool isDummy() const { return IsDummy; }
195201
};
196202

197203
/// Bitfield tracking the initialisation status of elements of primitive arrays.

clang/lib/AST/Interp/Interp.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
215215
return true;
216216
}
217217

218+
bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
219+
return !Ptr.isDummy();
220+
}
221+
218222
bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
219223
CheckSubobjectKind CSK) {
220224
if (!Ptr.isZero())
@@ -297,6 +301,8 @@ bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
297301
}
298302

299303
bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
304+
if (!CheckDummy(S, OpPC, Ptr))
305+
return false;
300306
if (!CheckLive(S, OpPC, Ptr, AK_Read))
301307
return false;
302308
if (!CheckExtern(S, OpPC, Ptr))

clang/lib/AST/Interp/Interp.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
5555
/// Checks if a pointer is live and accessible.
5656
bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
5757
AccessKinds AK);
58+
59+
/// Checks if a pointer is a dummy pointer.
60+
bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
61+
5862
/// Checks if a pointer is null.
5963
bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
6064
CheckSubobjectKind CSK);
@@ -1415,8 +1419,9 @@ bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset,
14151419
// Compute the largest index into the array.
14161420
T MaxIndex = T::from(Ptr.getNumElems(), Offset.bitWidth());
14171421

1422+
bool Invalid = false;
14181423
// Helper to report an invalid offset, computed as APSInt.
1419-
auto InvalidOffset = [&]() {
1424+
auto DiagInvalidOffset = [&]() -> void {
14201425
const unsigned Bits = Offset.bitWidth();
14211426
APSInt APOffset(Offset.toAPSInt().extend(Bits + 2), false);
14221427
APSInt APIndex(Index.toAPSInt().extend(Bits + 2), false);
@@ -1426,28 +1431,31 @@ bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset,
14261431
<< NewIndex
14271432
<< /*array*/ static_cast<int>(!Ptr.inArray())
14281433
<< static_cast<unsigned>(MaxIndex);
1429-
return false;
1434+
Invalid = true;
14301435
};
14311436

14321437
T MaxOffset = T::from(MaxIndex - Index, Offset.bitWidth());
14331438
if constexpr (Op == ArithOp::Add) {
14341439
// If the new offset would be negative, bail out.
14351440
if (Offset.isNegative() && (Offset.isMin() || -Offset > Index))
1436-
return InvalidOffset();
1441+
DiagInvalidOffset();
14371442

14381443
// If the new offset would be out of bounds, bail out.
14391444
if (Offset.isPositive() && Offset > MaxOffset)
1440-
return InvalidOffset();
1445+
DiagInvalidOffset();
14411446
} else {
14421447
// If the new offset would be negative, bail out.
14431448
if (Offset.isPositive() && Index < Offset)
1444-
return InvalidOffset();
1449+
DiagInvalidOffset();
14451450

14461451
// If the new offset would be out of bounds, bail out.
14471452
if (Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset))
1448-
return InvalidOffset();
1453+
DiagInvalidOffset();
14491454
}
14501455

1456+
if (Invalid && !Ptr.isDummy())
1457+
return false;
1458+
14511459
// Offset is valid - compute it on unsigned.
14521460
int64_t WideIndex = static_cast<int64_t>(Index);
14531461
int64_t WideOffset = static_cast<int64_t>(Offset);

clang/lib/AST/Interp/InterpBuiltin.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ static bool interp__builtin_strlen(InterpState &S, CodePtr OpPC,
152152
if (!CheckLive(S, OpPC, StrPtr, AK_Read))
153153
return false;
154154

155+
if (!CheckDummy(S, OpPC, StrPtr))
156+
return false;
157+
155158
assert(StrPtr.getFieldDesc()->isPrimitiveArray());
156159

157160
size_t Len = 0;

clang/lib/AST/Interp/Pointer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ class Pointer {
314314
bool isActive() const { return Base == 0 || getInlineDesc()->IsActive; }
315315
/// Checks if a structure is a base class.
316316
bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; }
317+
/// Checks if the pointer pointers to a dummy value.
318+
bool isDummy() const { return getDeclDesc()->isDummy(); }
317319

318320
/// Checks if an object or a subfield is mutable.
319321
bool isConst() const {

clang/lib/AST/Interp/Program.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,16 +144,18 @@ std::optional<unsigned> Program::getOrCreateDummy(const ValueDecl *PD) {
144144
It != DummyParams.end())
145145
return It->second;
146146

147-
// Create a pointer to an incomplete array of the specified elements.
148-
QualType ElemTy = PD->getType();
149-
QualType Ty =
150-
Ctx.getASTContext().getIncompleteArrayType(ElemTy, ArrayType::Normal, 0);
147+
// Create dummy descriptor.
148+
Descriptor *Desc = allocateDescriptor(PD, std::nullopt);
149+
// Allocate a block for storage.
150+
unsigned I = Globals.size();
151151

152-
if (auto Idx = createGlobal(PD, Ty, /*isStatic=*/true, /*isExtern=*/true)) {
153-
DummyParams[PD] = *Idx;
154-
return Idx;
155-
}
156-
return std::nullopt;
152+
auto *G = new (Allocator, Desc->getAllocSize())
153+
Global(getCurrentDecl(), Desc, /*IsStatic=*/true, /*IsExtern=*/false);
154+
G->block()->invokeCtor();
155+
156+
Globals.push_back(G);
157+
DummyParams[PD] = I;
158+
return I;
157159
}
158160

159161
std::optional<unsigned> Program::createGlobal(const ValueDecl *VD,

clang/lib/StaticAnalyzer/Core/ExprEngine.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2509,7 +2509,7 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
25092509
if (BlockCount == AMgr.options.maxBlockVisitOnPath - 1 &&
25102510
AMgr.options.ShouldWidenLoops) {
25112511
const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminatorStmt();
2512-
if (!isa_and_nonnull<ForStmt, WhileStmt, DoStmt>(Term))
2512+
if (!isa_and_nonnull<ForStmt, WhileStmt, DoStmt, CXXForRangeStmt>(Term))
25132513
return;
25142514
// Widen.
25152515
const LocationContext *LCtx = Pred->getLocationContext();

clang/lib/StaticAnalyzer/Core/LoopWidening.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ static const Expr *getLoopCondition(const Stmt *LoopStmt) {
3535
return cast<WhileStmt>(LoopStmt)->getCond();
3636
case Stmt::DoStmtClass:
3737
return cast<DoStmt>(LoopStmt)->getCond();
38+
case Stmt::CXXForRangeStmtClass:
39+
return cast<CXXForRangeStmt>(LoopStmt)->getCond();
3840
}
3941
}
4042

@@ -45,7 +47,7 @@ ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState,
4547
const LocationContext *LCtx,
4648
unsigned BlockCount, const Stmt *LoopStmt) {
4749

48-
assert((isa<ForStmt, WhileStmt, DoStmt>(LoopStmt)));
50+
assert((isa<ForStmt, WhileStmt, DoStmt, CXXForRangeStmt>(LoopStmt)));
4951

5052
// Invalidate values in the current state.
5153
// TODO Make this more conservative by only invalidating values that might

clang/test/AST/Interp/arrays.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s
2+
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++20 -verify %s
23
// RUN: %clang_cc1 -verify=ref %s
4+
// RUN: %clang_cc1 -verify=ref -std=c++20 %s
35

46
constexpr int m = 3;
57
constexpr const int *foo[][5] = {
@@ -497,3 +499,58 @@ namespace Incomplete {
497499
// expected-error {{must be initialized by a constant expression}} \
498500
// expected-note {{read of non-constexpr variable 'arr'}}
499501
}
502+
503+
namespace GH69115 {
504+
/// This used to crash because we were trying to emit destructors for the
505+
/// array.
506+
constexpr int foo() {
507+
int arr[2][2] = {1, 2, 3, 4};
508+
return 0;
509+
}
510+
static_assert(foo() == 0, "");
511+
512+
/// Test that we still emit the destructors for multi-dimensional
513+
/// composite arrays.
514+
#if __cplusplus >= 202002L
515+
constexpr void assert(bool C) {
516+
if (C)
517+
return;
518+
// Invalid in constexpr.
519+
(void)(1 / 0); // expected-warning {{undefined}} \
520+
// ref-warning {{undefined}}
521+
}
522+
523+
class F {
524+
public:
525+
int a;
526+
int *dtor;
527+
int &idx;
528+
constexpr F(int a, int *dtor, int &idx) : a(a), dtor(dtor), idx(idx) {}
529+
constexpr ~F() noexcept(false){
530+
dtor[idx] = a;
531+
++idx;
532+
}
533+
};
534+
constexpr int foo2() {
535+
int dtorIndices[] = {0, 0, 0, 0};
536+
int idx = 0;
537+
538+
{
539+
F arr[2][2] = {F(1, dtorIndices, idx),
540+
F(2, dtorIndices, idx),
541+
F(3, dtorIndices, idx),
542+
F(4, dtorIndices, idx)};
543+
}
544+
545+
/// Reverse-reverse order.
546+
assert(idx == 4);
547+
assert(dtorIndices[0] == 4);
548+
assert(dtorIndices[1] == 3);
549+
assert(dtorIndices[2] == 2);
550+
assert(dtorIndices[3] == 1);
551+
552+
return 0;
553+
}
554+
static_assert(foo2() == 0, "");
555+
#endif
556+
}

clang/test/AST/Interp/c.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,23 @@ _Static_assert(&a != 0, ""); // ref-warning {{always true}} \
4747
// expected-warning {{always true}} \
4848
// pedantic-expected-warning {{always true}} \
4949
// pedantic-expected-warning {{is a GNU extension}}
50+
_Static_assert((&c + 1) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
51+
// pedantic-expected-warning {{is a GNU extension}}
52+
_Static_assert((&a + 100) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
53+
// pedantic-ref-note {{100 of non-array}} \
54+
// pedantic-expected-note {{100 of non-array}} \
55+
// pedantic-expected-warning {{is a GNU extension}}
56+
_Static_assert((&a - 100) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
57+
// pedantic-expected-warning {{is a GNU extension}} \
58+
// pedantic-ref-note {{-100 of non-array}} \
59+
// pedantic-expected-note {{-100 of non-array}}
60+
/// extern variable of a composite type.
61+
/// FIXME: The 'cast from void*' note is missing in the new interpreter.
62+
extern struct Test50S Test50;
63+
_Static_assert(&Test50 != (void*)0, ""); // ref-warning {{always true}} \
64+
// pedantic-ref-warning {{always true}} \
65+
// pedantic-ref-warning {{is a GNU extension}} \
66+
// pedantic-ref-note {{cast from 'void *' is not allowed}} \
67+
// expected-warning {{always true}} \
68+
// pedantic-expected-warning {{always true}} \
69+
// pedantic-expected-warning {{is a GNU extension}}

clang/test/Analysis/loop-widening-notes.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,15 @@ int test_for_loop() {
7070
return flag_d / num; // no-crash expected-warning {{Division by zero}}
7171
// expected-note@-1 {{Division by zero}}
7272
}
73+
74+
int test_for_range_loop() {
75+
int arr[10] = {0};
76+
for(auto x : arr) { // expected-note {{Assigning value}}
77+
++x;
78+
}
79+
if (arr[0] == 0) // expected-note {{Assuming the condition is true}}
80+
// expected-note@-1 {{Taking true branch}}
81+
return 1/arr[0]; // expected-warning {{Division by zero}}
82+
// expected-note@-1 {{Division by zero}}
83+
return 0;
84+
}

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,6 +1343,12 @@ static bool isIntrinsicModuleProcedure(llvm::StringRef name) {
13431343
name.startswith("ieee_") || name.startswith("__ppc_");
13441344
}
13451345

1346+
static bool isCoarrayIntrinsic(llvm::StringRef name) {
1347+
return name.startswith("atomic_") || name.startswith("co_") ||
1348+
name.contains("image") || name.endswith("cobound") ||
1349+
name.equals("team_number");
1350+
}
1351+
13461352
/// Return the generic name of an intrinsic module procedure specific name.
13471353
/// Remove any "__builtin_" prefix, and any specific suffix of the form
13481354
/// {_[ail]?[0-9]+}*, such as _1 or _a4.
@@ -1363,6 +1369,8 @@ llvm::StringRef genericName(llvm::StringRef specificName) {
13631369
void crashOnMissingIntrinsic(mlir::Location loc, llvm::StringRef name) {
13641370
if (isIntrinsicModuleProcedure(name))
13651371
TODO(loc, "intrinsic module procedure: " + llvm::Twine(name));
1372+
else if (isCoarrayIntrinsic(name))
1373+
TODO(loc, "coarray: intrinsic " + llvm::Twine(name));
13661374
else
13671375
TODO(loc, "intrinsic: " + llvm::Twine(name));
13681376
}

flang/runtime/io-api.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,3 +1517,17 @@ enum Iostat IONAME(CheckUnitNumberInRange128)(common::int128_t unit,
15171517
#endif
15181518

15191519
} // namespace Fortran::runtime::io
1520+
1521+
#if defined(_LIBCPP_VERBOSE_ABORT)
1522+
// Provide own definition for `std::__libcpp_verbose_abort` to avoid dependency
1523+
// on the version provided by libc++.
1524+
1525+
void std::__libcpp_verbose_abort(char const *format, ...) {
1526+
va_list list;
1527+
va_start(list, format);
1528+
std::vfprintf(stderr, format, list);
1529+
va_end(list);
1530+
1531+
std::abort();
1532+
}
1533+
#endif

0 commit comments

Comments
 (0)