Skip to content

Commit 3d14763

Browse files
committed
asdf
1 parent b836942 commit 3d14763

File tree

11 files changed

+265
-72
lines changed

11 files changed

+265
-72
lines changed

clang/lib/AST/ByteCode/Descriptor.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,6 @@ struct Descriptor final {
275275
void dump(llvm::raw_ostream &OS) const;
276276
void dumpFull(unsigned Offset = 0, unsigned Indent = 0) const;
277277
};
278-
279278
} // namespace interp
280279
} // namespace clang
281280

clang/lib/AST/ByteCode/InitMap.cpp

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@
1010
using namespace clang;
1111
using namespace clang::interp;
1212

13-
InitMap::InitMap(unsigned N)
14-
: UninitFields(N), Data(std::make_unique<T[]>(numFields(N))) {}
15-
1613
bool InitMap::initializeElement(unsigned I) {
1714
unsigned Bucket = I / PER_FIELD;
1815
T Mask = T(1) << (I % PER_FIELD);
@@ -29,3 +26,29 @@ bool InitMap::isElementInitialized(unsigned I) const {
2926
unsigned Bucket = I / PER_FIELD;
3027
return data()[Bucket] & (T(1) << (I % PER_FIELD));
3128
}
29+
30+
// Values in the second half of data() are inverted,
31+
// i.e. 0 means "lifetime started".
32+
void InitMap::startElementLifetime(unsigned I) {
33+
unsigned LifetimeIndex = NumElems + I;
34+
35+
unsigned Bucket = LifetimeIndex / PER_FIELD;
36+
T Mask = T(1) << (LifetimeIndex % PER_FIELD);
37+
if ((data()[Bucket] & Mask)) {
38+
data()[Bucket] &= ~Mask;
39+
--DeadFields;
40+
}
41+
}
42+
43+
// Values in the second half of data() are inverted,
44+
// i.e. 0 means "lifetime started".
45+
void InitMap::endElementLifetime(unsigned I) {
46+
unsigned LifetimeIndex = NumElems + I;
47+
48+
unsigned Bucket = LifetimeIndex / PER_FIELD;
49+
T Mask = T(1) << (LifetimeIndex % PER_FIELD);
50+
if (!(data()[Bucket] & Mask)) {
51+
data()[Bucket] |= Mask;
52+
++DeadFields;
53+
}
54+
}

clang/lib/AST/ByteCode/InitMap.h

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,27 +24,44 @@ struct InitMap final {
2424
using T = uint64_t;
2525
/// Bits stored in a single field.
2626
static constexpr uint64_t PER_FIELD = sizeof(T) * CHAR_BIT;
27+
/// Number of fields in the init map.
28+
unsigned NumElems;
2729
/// Number of fields not initialized.
2830
unsigned UninitFields;
31+
unsigned DeadFields = 0;
2932
std::unique_ptr<T[]> Data;
3033

3134
public:
3235
/// Initializes the map with no fields set.
33-
explicit InitMap(unsigned N);
34-
35-
private:
36-
friend class Pointer;
36+
explicit InitMap(unsigned N)
37+
: NumElems(N), UninitFields(N),
38+
Data(std::make_unique<T[]>(numFields(N))) {}
39+
explicit InitMap(unsigned N, bool AllInitialized)
40+
: NumElems(N), UninitFields(AllInitialized ? 0 : N),
41+
Data(std::make_unique<T[]>(numFields(N))) {}
42+
43+
void startElementLifetime(unsigned I);
44+
void endElementLifetime(unsigned I);
45+
46+
bool isElementAlive(unsigned I) const {
47+
unsigned LifetimeIndex = NumElems + I;
48+
unsigned Bucket = LifetimeIndex / PER_FIELD;
49+
return !(data()[Bucket] & (T(1) << (LifetimeIndex % PER_FIELD)));
50+
}
3751

38-
/// Returns a pointer to storage.
39-
T *data() { return Data.get(); }
40-
const T *data() const { return Data.get(); }
52+
bool allElementsAlive() const { return DeadFields == 0; }
4153

4254
/// Initializes an element. Returns true when object if fully initialized.
4355
bool initializeElement(unsigned I);
4456

4557
/// Checks if an element was initialized.
4658
bool isElementInitialized(unsigned I) const;
4759

60+
private:
61+
/// Returns a pointer to storage.
62+
T *data() { return Data.get(); }
63+
const T *data() const { return Data.get(); }
64+
4865
static constexpr size_t numFields(unsigned N) {
4966
return ((N + PER_FIELD - 1) / PER_FIELD) * 2;
5067
}
@@ -94,7 +111,6 @@ struct InitMapPtr final {
94111
};
95112
};
96113
static_assert(sizeof(InitMapPtr) == sizeof(void *));
97-
98114
} // namespace interp
99115
} // namespace clang
100116

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1907,10 +1907,6 @@ bool EndLifetime(InterpState &S, CodePtr OpPC) {
19071907
if (Ptr.isBlockPointer() && !CheckDummy(S, OpPC, Ptr.block(), AK_Destroy))
19081908
return false;
19091909

1910-
// FIXME: We need per-element lifetime information for primitive arrays.
1911-
if (Ptr.isArrayElement())
1912-
return true;
1913-
19141910
endLifetimeRecurse(Ptr.narrow());
19151911
return true;
19161912
}
@@ -1921,10 +1917,6 @@ bool EndLifetimePop(InterpState &S, CodePtr OpPC) {
19211917
if (Ptr.isBlockPointer() && !CheckDummy(S, OpPC, Ptr.block(), AK_Destroy))
19221918
return false;
19231919

1924-
// FIXME: We need per-element lifetime information for primitive arrays.
1925-
if (Ptr.isArrayElement())
1926-
return true;
1927-
19281920
endLifetimeRecurse(Ptr.narrow());
19291921
return true;
19301922
}
@@ -2324,6 +2316,34 @@ bool FinishInitGlobal(InterpState &S, CodePtr OpPC) {
23242316
return true;
23252317
}
23262318

2319+
bool Destroy(InterpState &S, CodePtr OpPC, uint32_t I) {
2320+
assert(S.Current->getFunction());
2321+
// FIXME: We iterate the scope once here and then again in the destroy() call
2322+
// below.
2323+
for (auto &Local : S.Current->getFunction()->getScope(I).locals_reverse()) {
2324+
if (!S.Current->getLocalBlock(Local.Offset)->isInitialized())
2325+
continue;
2326+
const Pointer &Ptr = S.Current->getLocalPointer(Local.Offset);
2327+
if (Ptr.getLifetime() == Lifetime::Ended) {
2328+
// Try to use the declaration for better diagnostics
2329+
if (const Decl *D = Ptr.getDeclDesc()->asDecl()) {
2330+
auto *ND = cast<NamedDecl>(D);
2331+
S.FFDiag(ND->getLocation(),
2332+
diag::note_constexpr_destroy_out_of_lifetime)
2333+
<< ND->getNameAsString();
2334+
} else {
2335+
S.FFDiag(Ptr.getDeclDesc()->getLocation(),
2336+
diag::note_constexpr_destroy_out_of_lifetime)
2337+
<< Ptr.toDiagnosticString(S.getASTContext());
2338+
}
2339+
return false;
2340+
}
2341+
}
2342+
2343+
S.Current->destroy(I);
2344+
return true;
2345+
}
2346+
23272347
// https://github.com/llvm/llvm-project/issues/102513
23282348
#if defined(_MSC_VER) && !defined(__clang__) && !defined(NDEBUG)
23292349
#pragma optimize("", off)

clang/lib/AST/ByteCode/Interp.h

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ bool CheckFunctionDecl(InterpState &S, CodePtr OpPC, const FunctionDecl *FD);
122122
bool handleFixedPointOverflow(InterpState &S, CodePtr OpPC,
123123
const FixedPoint &FP);
124124

125+
bool Destroy(InterpState &S, CodePtr OpPC, uint32_t I);
125126
bool isConstexprUnknown(const Pointer &P);
126127

127128
enum class ShiftDir { Left, Right };
@@ -2436,38 +2437,6 @@ inline bool SubPtr(InterpState &S, CodePtr OpPC, bool ElemSizeIsZero) {
24362437
return true;
24372438
}
24382439

2439-
//===----------------------------------------------------------------------===//
2440-
// Destroy
2441-
//===----------------------------------------------------------------------===//
2442-
2443-
inline bool Destroy(InterpState &S, CodePtr OpPC, uint32_t I) {
2444-
assert(S.Current->getFunction());
2445-
2446-
// FIXME: We iterate the scope once here and then again in the destroy() call
2447-
// below.
2448-
for (auto &Local : S.Current->getFunction()->getScope(I).locals_reverse()) {
2449-
const Pointer &Ptr = S.Current->getLocalPointer(Local.Offset);
2450-
2451-
if (Ptr.getLifetime() == Lifetime::Ended) {
2452-
// Try to use the declaration for better diagnostics
2453-
if (const Decl *D = Ptr.getDeclDesc()->asDecl()) {
2454-
auto *ND = cast<NamedDecl>(D);
2455-
S.FFDiag(ND->getLocation(),
2456-
diag::note_constexpr_destroy_out_of_lifetime)
2457-
<< ND->getNameAsString();
2458-
} else {
2459-
S.FFDiag(Ptr.getDeclDesc()->getLocation(),
2460-
diag::note_constexpr_destroy_out_of_lifetime)
2461-
<< Ptr.toDiagnosticString(S.getASTContext());
2462-
}
2463-
return false;
2464-
}
2465-
}
2466-
2467-
S.Current->destroy(I);
2468-
return true;
2469-
}
2470-
24712440
inline bool InitScope(InterpState &S, CodePtr OpPC, uint32_t I) {
24722441
S.Current->initScope(I);
24732442
return true;

clang/lib/AST/ByteCode/Pointer.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,19 @@ bool Pointer::isElementInitialized(unsigned Index) const {
490490
return isInitialized();
491491
}
492492

493+
bool Pointer::isElementAlive(unsigned Index) const {
494+
assert(getFieldDesc()->isPrimitiveArray());
495+
496+
InitMapPtr &IM = getInitMap();
497+
if (!IM.hasInitMap())
498+
return true;
499+
500+
if (IM.allInitialized())
501+
return true;
502+
503+
return IM->isElementAlive(Index);
504+
}
505+
493506
void Pointer::initialize() const {
494507
if (!isBlockPointer())
495508
return;
@@ -524,7 +537,6 @@ void Pointer::initializeElement(unsigned Index) const {
524537
assert(Index < getFieldDesc()->getNumElems());
525538

526539
InitMapPtr &IM = getInitMap();
527-
528540
if (IM.allInitialized())
529541
return;
530542

@@ -562,6 +574,23 @@ bool Pointer::allElementsInitialized() const {
562574
return IM.allInitialized();
563575
}
564576

577+
bool Pointer::allElementsAlive() const {
578+
assert(getFieldDesc()->isPrimitiveArray());
579+
assert(isArrayRoot());
580+
581+
if (isStatic() && BS.Base == 0)
582+
return true;
583+
584+
if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&
585+
Offset == BS.Base) {
586+
const auto &GD = block()->getBlockDesc<GlobalInlineDescriptor>();
587+
return GD.InitState == GlobalInitState::Initialized;
588+
}
589+
590+
InitMapPtr &IM = getInitMap();
591+
return IM.allInitialized() || (IM.hasInitMap() && IM->allElementsAlive());
592+
}
593+
565594
void Pointer::activate() const {
566595
// Field has its bit in an inline descriptor.
567596
assert(BS.Base != 0 && "Only composite fields can be activated");

clang/lib/AST/ByteCode/Pointer.h

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include "Descriptor.h"
1717
#include "FunctionPointer.h"
18+
#include "InitMap.h"
1819
#include "InterpBlock.h"
1920
#include "clang/AST/ComparisonCategories.h"
2021
#include "clang/AST/Decl.h"
@@ -721,6 +722,9 @@ class Pointer {
721722
/// Like isInitialized(), but for primitive arrays.
722723
bool isElementInitialized(unsigned Index) const;
723724
bool allElementsInitialized() const;
725+
bool allElementsAlive() const;
726+
bool isElementAlive(unsigned Index) const;
727+
724728
/// Activats a field.
725729
void activate() const;
726730
/// Deactivates an entire strurcutre.
@@ -731,6 +735,20 @@ class Pointer {
731735
return Lifetime::Started;
732736
if (BS.Base < sizeof(InlineDescriptor))
733737
return Lifetime::Started;
738+
739+
if (inArray() && !isArrayRoot()) {
740+
InitMapPtr &IM = getInitMap();
741+
742+
if (!IM.hasInitMap()) {
743+
if (IM.allInitialized())
744+
return Lifetime::Started;
745+
return getArray().getLifetime();
746+
}
747+
748+
return IM->isElementAlive(getIndex()) ? Lifetime::Started
749+
: Lifetime::Ended;
750+
}
751+
734752
return getInlineDesc()->LifeState;
735753
}
736754

@@ -739,6 +757,17 @@ class Pointer {
739757
return;
740758
if (BS.Base < sizeof(InlineDescriptor))
741759
return;
760+
761+
if (inArray()) {
762+
const Descriptor *Desc = getFieldDesc();
763+
InitMapPtr &IM = getInitMap();
764+
if (!IM.hasInitMap())
765+
IM.setInitMap(new InitMap(Desc->getNumElems(), IM.allInitialized()));
766+
767+
IM->endElementLifetime(getIndex());
768+
return;
769+
}
770+
742771
getInlineDesc()->LifeState = Lifetime::Ended;
743772
}
744773

@@ -747,6 +776,18 @@ class Pointer {
747776
return;
748777
if (BS.Base < sizeof(InlineDescriptor))
749778
return;
779+
780+
if (inArray()) {
781+
InitMapPtr &IM = getInitMap();
782+
if (!IM.hasInitMap()) {
783+
const Descriptor *Desc = getFieldDesc();
784+
IM.setInitMap(new InitMap(Desc->getNumElems(), IM.allInitialized()));
785+
}
786+
787+
IM->startElementLifetime(getIndex());
788+
return;
789+
}
790+
750791
getInlineDesc()->LifeState = Lifetime::Started;
751792
}
752793

@@ -791,7 +832,6 @@ class Pointer {
791832
friend class DeadBlock;
792833
friend class MemberPointer;
793834
friend class InterpState;
794-
friend struct InitMap;
795835
friend class DynamicAllocator;
796836
friend class Program;
797837

@@ -838,6 +878,8 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) {
838878
OS << ' ';
839879
if (const Descriptor *D = P.getFieldDesc())
840880
D->dump(OS);
881+
if (P.isArrayElement())
882+
OS << " index " << P.getIndex();
841883
return OS;
842884
}
843885

clang/lib/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ add_clang_library(clangAST
7373
ByteCode/Compiler.cpp
7474
ByteCode/Context.cpp
7575
ByteCode/Descriptor.cpp
76+
ByteCode/InitMap.cpp
7677
ByteCode/Disasm.cpp
7778
ByteCode/EvalEmitter.cpp
7879
ByteCode/Function.cpp

clang/test/AST/ByteCode/builtin-functions.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1848,11 +1848,9 @@ namespace WithinLifetime {
18481848
} xstd; // both-error {{is not a constant expression}} \
18491849
// both-note {{in call to}}
18501850

1851-
/// FIXME: We do not have per-element lifetime information for primitive arrays.
1852-
/// See https://github.com/llvm/llvm-project/issues/147528
18531851
consteval bool test_dynamic(bool read_after_deallocate) {
18541852
std::allocator<int> a;
1855-
int* p = a.allocate(1); // expected-note 2{{allocation performed here was not deallocated}}
1853+
int* p = a.allocate(1);
18561854
// a.allocate starts the lifetime of an array,
18571855
// the complete object of *p has started its lifetime
18581856
if (__builtin_is_within_lifetime(p))
@@ -1865,12 +1863,12 @@ namespace WithinLifetime {
18651863
return false;
18661864
a.deallocate(p, 1);
18671865
if (read_after_deallocate)
1868-
__builtin_is_within_lifetime(p); // ref-note {{read of heap allocated object that has been deleted}}
1866+
__builtin_is_within_lifetime(p); // both-note {{read of heap allocated object that has been deleted}}
18691867
return true;
18701868
}
1871-
static_assert(test_dynamic(false)); // expected-error {{not an integral constant expression}}
1869+
static_assert(test_dynamic(false));
18721870
static_assert(test_dynamic(true)); // both-error {{not an integral constant expression}} \
1873-
// ref-note {{in call to}}
1871+
// both-note {{in call to}}
18741872
}
18751873

18761874
#ifdef __SIZEOF_INT128__

0 commit comments

Comments
 (0)