Skip to content

Commit c66be28

Browse files
authored
[clang][bytecode] Allocate IntegralAP and Floating types using an allocator (#144246)
Both `APInt` and `APFloat` will heap-allocate memory themselves using the system allocator when the size of their data exceeds 64 bits. This is why clang has `APNumericStorage`, which allocates its memory using an allocator (via `ASTContext`) instead. Calling `getValue()` on an ast node like that will then create a new `APInt`/`APFloat` , which will copy the data (in the `APFloat` case, we even copy it twice). That's sad but whatever. In the bytecode interpreter, we have a similar problem. Large integers and floating-point values are placement-new allocated into the `InterpStack` (or into the bytecode, which is a `vector<std::byte>`). When we then later interrupt interpretation, we don't run the destructor for all items on the stack, which means we leak the memory the `APInt`/`APFloat` (which backs the `IntegralAP`/`Floating` the interpreter uses). Fix this by using an approach similar to the one used in the AST. Add an allocator to `InterpState`, which is used for temporaries and local values. Those values will be freed at the end of interpretation. For global variables, we need to promote the values to global lifetime, which we do via `InitGlobal` and `FinishInitGlobal` ops. Interestingly, this results in a slight _improvement_ in compile times: https://llvm-compile-time-tracker.com/compare.php?from=6bfcdda9b1ddf0900f82f7e30cb5e3253a791d50&to=88d1d899127b408f0fb0f385c2c58e6283195049&stat=instructions:u (but don't ask me why). Fixes #139012
1 parent 65d590e commit c66be28

17 files changed

+929
-341
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 63 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,8 @@ bool Compiler<Emitter>::VisitFloatingLiteral(const FloatingLiteral *E) {
748748
if (DiscardResult)
749749
return true;
750750

751-
return this->emitConstFloat(E->getValue(), E);
751+
APFloat F = E->getValue();
752+
return this->emitFloat(F, E);
752753
}
753754

754755
template <class Emitter>
@@ -4185,8 +4186,10 @@ bool Compiler<Emitter>::visitZeroInitializer(PrimType T, QualType QT,
41854186
nullptr, E);
41864187
case PT_MemberPtr:
41874188
return this->emitNullMemberPtr(0, nullptr, E);
4188-
case PT_Float:
4189-
return this->emitConstFloat(APFloat::getZero(Ctx.getFloatSemantics(QT)), E);
4189+
case PT_Float: {
4190+
APFloat F = APFloat::getZero(Ctx.getFloatSemantics(QT));
4191+
return this->emitFloat(F, E);
4192+
}
41904193
case PT_FixedPoint: {
41914194
auto Sem = Ctx.getASTContext().getFixedPointSemantics(E->getType());
41924195
return this->emitConstFixedPoint(FixedPoint::zero(Sem), E);
@@ -4674,10 +4677,7 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD,
46744677
if (!visitInitializer(Init))
46754678
return false;
46764679

4677-
if (!this->emitFinishInit(Init))
4678-
return false;
4679-
4680-
return this->emitPopPtr(Init);
4680+
return this->emitFinishInitGlobal(Init);
46814681
};
46824682

46834683
DeclScope<Emitter> LocalScope(this, VD);
@@ -4698,51 +4698,45 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD,
46984698
return false;
46994699

47004700
return !Init || (checkDecl() && initGlobal(*GlobalIndex));
4701-
} else {
4702-
InitLinkScope<Emitter> ILS(this, InitLink::Decl(VD));
4703-
4704-
if (VarT) {
4705-
unsigned Offset = this->allocateLocalPrimitive(
4706-
VD, *VarT, VD->getType().isConstQualified(), nullptr,
4707-
ScopeKind::Block, IsConstexprUnknown);
4708-
if (Init) {
4709-
// If this is a toplevel declaration, create a scope for the
4710-
// initializer.
4711-
if (Toplevel) {
4712-
LocalScope<Emitter> Scope(this);
4713-
if (!this->visit(Init))
4714-
return false;
4715-
return this->emitSetLocal(*VarT, Offset, VD) && Scope.destroyLocals();
4716-
} else {
4717-
if (!this->visit(Init))
4718-
return false;
4719-
return this->emitSetLocal(*VarT, Offset, VD);
4720-
}
4721-
}
4722-
} else {
4723-
if (std::optional<unsigned> Offset =
4724-
this->allocateLocal(VD, VD->getType(), nullptr, ScopeKind::Block,
4725-
IsConstexprUnknown)) {
4726-
if (!Init)
4727-
return true;
4701+
}
4702+
// Local variables.
4703+
InitLinkScope<Emitter> ILS(this, InitLink::Decl(VD));
47284704

4729-
if (!this->emitGetPtrLocal(*Offset, Init))
4705+
if (VarT) {
4706+
unsigned Offset = this->allocateLocalPrimitive(
4707+
VD, *VarT, VD->getType().isConstQualified(), nullptr, ScopeKind::Block,
4708+
IsConstexprUnknown);
4709+
if (Init) {
4710+
// If this is a toplevel declaration, create a scope for the
4711+
// initializer.
4712+
if (Toplevel) {
4713+
LocalScope<Emitter> Scope(this);
4714+
if (!this->visit(Init))
47304715
return false;
4731-
4732-
if (!visitInitializer(Init))
4716+
return this->emitSetLocal(*VarT, Offset, VD) && Scope.destroyLocals();
4717+
} else {
4718+
if (!this->visit(Init))
47334719
return false;
4720+
return this->emitSetLocal(*VarT, Offset, VD);
4721+
}
4722+
}
4723+
} else {
4724+
if (std::optional<unsigned> Offset = this->allocateLocal(
4725+
VD, VD->getType(), nullptr, ScopeKind::Block, IsConstexprUnknown)) {
4726+
if (!Init)
4727+
return true;
47344728

4735-
if (!this->emitFinishInit(Init))
4736-
return false;
4729+
if (!this->emitGetPtrLocal(*Offset, Init))
4730+
return false;
47374731

4738-
return this->emitPopPtr(Init);
4739-
}
4740-
return false;
4732+
if (!visitInitializer(Init))
4733+
return false;
4734+
4735+
return this->emitFinishInitPop(Init);
47414736
}
4742-
return true;
4737+
return false;
47434738
}
4744-
4745-
return false;
4739+
return true;
47464740
}
47474741

47484742
template <class Emitter>
@@ -4751,8 +4745,10 @@ bool Compiler<Emitter>::visitAPValue(const APValue &Val, PrimType ValType,
47514745
assert(!DiscardResult);
47524746
if (Val.isInt())
47534747
return this->emitConst(Val.getInt(), ValType, E);
4754-
else if (Val.isFloat())
4755-
return this->emitConstFloat(Val.getFloat(), E);
4748+
else if (Val.isFloat()) {
4749+
APFloat F = Val.getFloat();
4750+
return this->emitFloat(F, E);
4751+
}
47564752

47574753
if (Val.isLValue()) {
47584754
if (Val.isNullPointer())
@@ -6133,8 +6129,10 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
61336129
const auto &TargetSemantics = Ctx.getFloatSemantics(E->getType());
61346130
if (!this->emitLoadFloat(E))
61356131
return false;
6136-
if (!this->emitConstFloat(llvm::APFloat(TargetSemantics, 1), E))
6132+
APFloat F(TargetSemantics, 1);
6133+
if (!this->emitFloat(F, E))
61376134
return false;
6135+
61386136
if (!this->emitAddf(getFPOptions(E), E))
61396137
return false;
61406138
if (!this->emitStoreFloat(E))
@@ -6176,8 +6174,10 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
61766174
const auto &TargetSemantics = Ctx.getFloatSemantics(E->getType());
61776175
if (!this->emitLoadFloat(E))
61786176
return false;
6179-
if (!this->emitConstFloat(llvm::APFloat(TargetSemantics, 1), E))
6177+
APFloat F(TargetSemantics, 1);
6178+
if (!this->emitFloat(F, E))
61806179
return false;
6180+
61816181
if (!this->emitSubf(getFPOptions(E), E))
61826182
return false;
61836183
if (!this->emitStoreFloat(E))
@@ -6953,6 +6953,20 @@ bool Compiler<Emitter>::emitDummyPtr(const DeclTy &D, const Expr *E) {
69536953
return true;
69546954
}
69556955

6956+
template <class Emitter>
6957+
bool Compiler<Emitter>::emitFloat(const APFloat &F, const Expr *E) {
6958+
assert(!DiscardResult && "Should've been checked before");
6959+
6960+
if (Floating::singleWord(F.getSemantics()))
6961+
return this->emitConstFloat(Floating(F), E);
6962+
6963+
APInt I = F.bitcastToAPInt();
6964+
return this->emitConstFloat(
6965+
Floating(const_cast<uint64_t *>(I.getRawData()),
6966+
llvm::APFloatBase::SemanticsToEnum(F.getSemantics())),
6967+
E);
6968+
}
6969+
69566970
// This function is constexpr if and only if To, From, and the types of
69576971
// all subobjects of To and From are types T such that...
69586972
// (3.1) - is_union_v<T> is false;

clang/lib/AST/ByteCode/Compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
391391
bool emitRecordDestruction(const Record *R, SourceInfo Loc);
392392
bool emitDestruction(const Descriptor *Desc, SourceInfo Loc);
393393
bool emitDummyPtr(const DeclTy &D, const Expr *E);
394+
bool emitFloat(const APFloat &F, const Expr *E);
394395
unsigned collectBaseOffset(const QualType BaseType,
395396
const QualType DerivedType);
396397
bool emitLambdaStaticInvokerBody(const CXXMethodDecl *MD);

clang/lib/AST/ByteCode/Descriptor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
368368
bool IsTemporary, bool IsConst, UnknownSize)
369369
: Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark),
370370
MDSize(MD.value_or(0)),
371-
AllocSize(MDSize + sizeof(InitMapPtr) + alignof(void *)),
371+
AllocSize(MDSize + sizeof(InitMapPtr) + alignof(void *)), PrimT(Type),
372372
IsConst(IsConst), IsMutable(false), IsTemporary(IsTemporary),
373373
IsArray(true), CtorFn(getCtorArrayPrim(Type)),
374374
DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) {

clang/lib/AST/ByteCode/Disasm.cpp

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -50,34 +50,56 @@ inline static std::string printArg(Program &P, CodePtr &OpPC) {
5050
}
5151

5252
template <> inline std::string printArg<Floating>(Program &P, CodePtr &OpPC) {
53-
auto F = Floating::deserialize(*OpPC);
54-
OpPC += align(F.bytesToSerialize());
53+
auto Sem = Floating::deserializeSemantics(*OpPC);
5554

56-
std::string Result;
57-
llvm::raw_string_ostream SS(Result);
58-
SS << F;
59-
return Result;
55+
unsigned BitWidth = llvm::APFloatBase::semanticsSizeInBits(
56+
llvm::APFloatBase::EnumToSemantics(Sem));
57+
auto Memory =
58+
std::make_unique<uint64_t[]>(llvm::APInt::getNumWords(BitWidth));
59+
Floating Result(Memory.get(), Sem);
60+
Floating::deserialize(*OpPC, &Result);
61+
62+
OpPC += align(Result.bytesToSerialize());
63+
64+
std::string S;
65+
llvm::raw_string_ostream SS(S);
66+
SS << Result;
67+
return S;
6068
}
6169

6270
template <>
6371
inline std::string printArg<IntegralAP<false>>(Program &P, CodePtr &OpPC) {
64-
auto F = IntegralAP<false>::deserialize(*OpPC);
65-
OpPC += align(F.bytesToSerialize());
66-
67-
std::string Result;
68-
llvm::raw_string_ostream SS(Result);
69-
SS << F;
70-
return Result;
72+
using T = IntegralAP<false>;
73+
unsigned BitWidth = T::deserializeSize(*OpPC);
74+
auto Memory =
75+
std::make_unique<uint64_t[]>(llvm::APInt::getNumWords(BitWidth));
76+
77+
T Result(Memory.get(), BitWidth);
78+
T::deserialize(*OpPC, &Result);
79+
80+
OpPC += Result.bytesToSerialize();
81+
std::string Str;
82+
llvm::raw_string_ostream SS(Str);
83+
SS << Result;
84+
return Str;
7185
}
86+
7287
template <>
7388
inline std::string printArg<IntegralAP<true>>(Program &P, CodePtr &OpPC) {
74-
auto F = IntegralAP<true>::deserialize(*OpPC);
75-
OpPC += align(F.bytesToSerialize());
89+
using T = IntegralAP<true>;
90+
unsigned BitWidth = T::deserializeSize(*OpPC);
91+
auto Memory =
92+
std::make_unique<uint64_t[]>(llvm::APInt::getNumWords(BitWidth));
7693

77-
std::string Result;
78-
llvm::raw_string_ostream SS(Result);
79-
SS << F;
80-
return Result;
94+
T Result(Memory.get(), BitWidth);
95+
T::deserialize(*OpPC, &Result);
96+
97+
std::string Str;
98+
llvm::raw_string_ostream SS(Str);
99+
SS << Result;
100+
101+
OpPC += Result.bytesToSerialize();
102+
return Str;
81103
}
82104

83105
template <> inline std::string printArg<FixedPoint>(Program &P, CodePtr &OpPC) {

0 commit comments

Comments
 (0)