Skip to content

Commit 610b853

Browse files
authored
[clang][bytecode] Implement __builtin_operator{new,delete} (llvm#107672)
1 parent 83fea8b commit 610b853

File tree

6 files changed

+334
-31
lines changed

6 files changed

+334
-31
lines changed

clang/lib/AST/ByteCode/DynamicAllocator.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,27 +40,30 @@ void DynamicAllocator::cleanup() {
4040
}
4141

4242
Block *DynamicAllocator::allocate(const Expr *Source, PrimType T,
43-
size_t NumElements, unsigned EvalID) {
43+
size_t NumElements, unsigned EvalID,
44+
Form AllocForm) {
4445
// Create a new descriptor for an array of the specified size and
4546
// element type.
4647
const Descriptor *D = allocateDescriptor(
4748
Source, T, Descriptor::InlineDescMD, NumElements, /*IsConst=*/false,
4849
/*IsTemporary=*/false, /*IsMutable=*/false);
4950

50-
return allocate(D, EvalID);
51+
return allocate(D, EvalID, AllocForm);
5152
}
5253

5354
Block *DynamicAllocator::allocate(const Descriptor *ElementDesc,
54-
size_t NumElements, unsigned EvalID) {
55+
size_t NumElements, unsigned EvalID,
56+
Form AllocForm) {
5557
// Create a new descriptor for an array of the specified size and
5658
// element type.
5759
const Descriptor *D = allocateDescriptor(
5860
ElementDesc->asExpr(), ElementDesc, Descriptor::InlineDescMD, NumElements,
5961
/*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false);
60-
return allocate(D, EvalID);
62+
return allocate(D, EvalID, AllocForm);
6163
}
6264

63-
Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID) {
65+
Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID,
66+
Form AllocForm) {
6467
assert(D);
6568
assert(D->asExpr());
6669

@@ -84,7 +87,7 @@ Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID) {
8487
It->second.Allocations.emplace_back(std::move(Memory));
8588
else
8689
AllocationSites.insert(
87-
{D->asExpr(), AllocationSite(std::move(Memory), D->isArray())});
90+
{D->asExpr(), AllocationSite(std::move(Memory), AllocForm)});
8891
return B;
8992
}
9093

clang/lib/AST/ByteCode/DynamicAllocator.h

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ class InterpState;
3131
/// For all array allocations, we need to allocate new Descriptor instances,
3232
/// so the DynamicAllocator has a llvm::BumpPtrAllocator similar to Program.
3333
class DynamicAllocator final {
34+
public:
35+
enum class Form : uint8_t {
36+
NonArray,
37+
Array,
38+
Operator,
39+
};
40+
41+
private:
3442
struct Allocation {
3543
std::unique_ptr<std::byte[]> Memory;
3644
Allocation(std::unique_ptr<std::byte[]> Memory)
@@ -39,10 +47,10 @@ class DynamicAllocator final {
3947

4048
struct AllocationSite {
4149
llvm::SmallVector<Allocation> Allocations;
42-
bool IsArrayAllocation = false;
50+
Form AllocForm;
4351

44-
AllocationSite(std::unique_ptr<std::byte[]> Memory, bool Array)
45-
: IsArrayAllocation(Array) {
52+
AllocationSite(std::unique_ptr<std::byte[]> Memory, Form AllocForm)
53+
: AllocForm(AllocForm) {
4654
Allocations.push_back({std::move(Memory)});
4755
}
4856

@@ -58,12 +66,13 @@ class DynamicAllocator final {
5866
unsigned getNumAllocations() const { return AllocationSites.size(); }
5967

6068
/// Allocate ONE element of the given descriptor.
61-
Block *allocate(const Descriptor *D, unsigned EvalID);
69+
Block *allocate(const Descriptor *D, unsigned EvalID, Form AllocForm);
6270
/// Allocate \p NumElements primitive elements of the given type.
6371
Block *allocate(const Expr *Source, PrimType T, size_t NumElements,
64-
unsigned EvalID);
72+
unsigned EvalID, Form AllocForm);
6573
/// Allocate \p NumElements elements of the given descriptor.
66-
Block *allocate(const Descriptor *D, size_t NumElements, unsigned EvalID);
74+
Block *allocate(const Descriptor *D, size_t NumElements, unsigned EvalID,
75+
Form AllocForm);
6776

6877
/// Deallocate the given source+block combination.
6978
/// Returns \c true if anything has been deallocatd, \c false otherwise.
@@ -72,10 +81,10 @@ class DynamicAllocator final {
7281

7382
/// Checks whether the allocation done at the given source is an array
7483
/// allocation.
75-
bool isArrayAllocation(const Expr *Source) const {
84+
std::optional<Form> getAllocationForm(const Expr *Source) const {
7685
if (auto It = AllocationSites.find(Source); It != AllocationSites.end())
77-
return It->second.IsArrayAllocation;
78-
return false;
86+
return It->second.AllocForm;
87+
return std::nullopt;
7988
}
8089

8190
/// Allocation site iterator.

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -812,10 +812,11 @@ bool CheckDynamicMemoryAllocation(InterpState &S, CodePtr OpPC) {
812812
return true;
813813
}
814814

815-
bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC, bool NewWasArray,
816-
bool DeleteIsArray, const Descriptor *D,
815+
bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC,
816+
DynamicAllocator::Form AllocForm,
817+
DynamicAllocator::Form DeleteForm, const Descriptor *D,
817818
const Expr *NewExpr) {
818-
if (NewWasArray == DeleteIsArray)
819+
if (AllocForm == DeleteForm)
819820
return true;
820821

821822
QualType TypeToDiagnose;
@@ -832,15 +833,21 @@ bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC, bool NewWasArray,
832833

833834
const SourceInfo &E = S.Current->getSource(OpPC);
834835
S.FFDiag(E, diag::note_constexpr_new_delete_mismatch)
835-
<< DeleteIsArray << 0 << TypeToDiagnose;
836+
<< static_cast<int>(DeleteForm) << static_cast<int>(AllocForm)
837+
<< TypeToDiagnose;
836838
S.Note(NewExpr->getExprLoc(), diag::note_constexpr_dynamic_alloc_here)
837839
<< NewExpr->getSourceRange();
838840
return false;
839841
}
840842

841843
bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Expr *Source,
842844
const Pointer &Ptr) {
843-
if (Source && isa<CXXNewExpr>(Source))
845+
// The two sources we currently allow are new expressions and
846+
// __builtin_operator_new calls.
847+
if (isa_and_nonnull<CXXNewExpr>(Source))
848+
return true;
849+
if (const CallExpr *CE = dyn_cast_if_present<CallExpr>(Source);
850+
CE && CE->getBuiltinCallee() == Builtin::BI__builtin_operator_new)
844851
return true;
845852

846853
// Whatever this is, we didn't heap allocate it.
@@ -1172,6 +1179,8 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
11721179

11731180
bool CallBI(InterpState &S, CodePtr &PC, const Function *Func,
11741181
const CallExpr *CE) {
1182+
if (S.checkingPotentialConstantExpression())
1183+
return false;
11751184
auto NewFrame = std::make_unique<InterpFrame>(S, Func, PC);
11761185

11771186
InterpFrame *FrameBefore = S.Current;

clang/lib/AST/ByteCode/Interp.h

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,9 @@ bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F,
130130
bool CheckDynamicMemoryAllocation(InterpState &S, CodePtr OpPC);
131131

132132
/// Diagnose mismatched new[]/delete or new/delete[] pairs.
133-
bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC, bool NewWasArray,
134-
bool DeleteIsArray, const Descriptor *D,
133+
bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC,
134+
DynamicAllocator::Form AllocForm,
135+
DynamicAllocator::Form DeleteForm, const Descriptor *D,
135136
const Expr *NewExpr);
136137

137138
/// Check the source of the pointer passed to delete/delete[] has actually
@@ -2797,10 +2798,11 @@ inline bool Alloc(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
27972798
return false;
27982799

27992800
DynamicAllocator &Allocator = S.getAllocator();
2800-
Block *B = Allocator.allocate(Desc, S.Ctx.getEvalID());
2801+
Block *B = Allocator.allocate(Desc, S.Ctx.getEvalID(),
2802+
DynamicAllocator::Form::NonArray);
28012803
assert(B);
28022804

2803-
S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
2805+
S.Stk.push<Pointer>(B);
28042806

28052807
return true;
28062808
}
@@ -2822,8 +2824,9 @@ inline bool AllocN(InterpState &S, CodePtr OpPC, PrimType T, const Expr *Source,
28222824
}
28232825

28242826
DynamicAllocator &Allocator = S.getAllocator();
2825-
Block *B = Allocator.allocate(Source, T, static_cast<size_t>(NumElements),
2826-
S.Ctx.getEvalID());
2827+
Block *B =
2828+
Allocator.allocate(Source, T, static_cast<size_t>(NumElements),
2829+
S.Ctx.getEvalID(), DynamicAllocator::Form::Array);
28272830
assert(B);
28282831
S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
28292832

@@ -2848,8 +2851,9 @@ inline bool AllocCN(InterpState &S, CodePtr OpPC, const Descriptor *ElementDesc,
28482851
}
28492852

28502853
DynamicAllocator &Allocator = S.getAllocator();
2851-
Block *B = Allocator.allocate(ElementDesc, static_cast<size_t>(NumElements),
2852-
S.Ctx.getEvalID());
2854+
Block *B =
2855+
Allocator.allocate(ElementDesc, static_cast<size_t>(NumElements),
2856+
S.Ctx.getEvalID(), DynamicAllocator::Form::Array);
28532857
assert(B);
28542858

28552859
S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
@@ -2894,17 +2898,23 @@ static inline bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm) {
28942898
return false;
28952899

28962900
DynamicAllocator &Allocator = S.getAllocator();
2897-
bool WasArrayAlloc = Allocator.isArrayAllocation(Source);
28982901
const Descriptor *BlockDesc = BlockToDelete->getDescriptor();
2902+
std::optional<DynamicAllocator::Form> AllocForm =
2903+
Allocator.getAllocationForm(Source);
28992904

29002905
if (!Allocator.deallocate(Source, BlockToDelete, S)) {
29012906
// Nothing has been deallocated, this must be a double-delete.
29022907
const SourceInfo &Loc = S.Current->getSource(OpPC);
29032908
S.FFDiag(Loc, diag::note_constexpr_double_delete);
29042909
return false;
29052910
}
2906-
return CheckNewDeleteForms(S, OpPC, WasArrayAlloc, DeleteIsArrayForm,
2907-
BlockDesc, Source);
2911+
2912+
assert(AllocForm);
2913+
DynamicAllocator::Form DeleteForm = DeleteIsArrayForm
2914+
? DynamicAllocator::Form::Array
2915+
: DynamicAllocator::Form::NonArray;
2916+
return CheckNewDeleteForms(S, OpPC, *AllocForm, DeleteForm, BlockDesc,
2917+
Source);
29082918
}
29092919

29102920
static inline bool IsConstantContext(InterpState &S, CodePtr OpPC) {

0 commit comments

Comments
 (0)