aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/AST/ByteCode/Compiler.cpp
diff options
context:
space:
mode:
authorTimm Baeder <tbaeder@redhat.com>2025-06-17 18:31:06 +0200
committerGitHub <noreply@github.com>2025-06-17 18:31:06 +0200
commitc66be289901b3f035187d391e80e3610d7d6232e (patch)
treea703b857bb4b2441ca2fe7a8bbcf873c1080d7ab /clang/lib/AST/ByteCode/Compiler.cpp
parent65d590e8d012df9dabbf8b3ec929fd1543c7398a (diff)
downloadllvm-c66be289901b3f035187d391e80e3610d7d6232e.zip
llvm-c66be289901b3f035187d391e80e3610d7d6232e.tar.gz
llvm-c66be289901b3f035187d391e80e3610d7d6232e.tar.bz2
[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 https://github.com/llvm/llvm-project/issues/139012
Diffstat (limited to 'clang/lib/AST/ByteCode/Compiler.cpp')
-rw-r--r--clang/lib/AST/ByteCode/Compiler.cpp112
1 files changed, 63 insertions, 49 deletions
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 9fe4803..3f884ed 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -748,7 +748,8 @@ bool Compiler<Emitter>::VisitFloatingLiteral(const FloatingLiteral *E) {
if (DiscardResult)
return true;
- return this->emitConstFloat(E->getValue(), E);
+ APFloat F = E->getValue();
+ return this->emitFloat(F, E);
}
template <class Emitter>
@@ -4185,8 +4186,10 @@ bool Compiler<Emitter>::visitZeroInitializer(PrimType T, QualType QT,
nullptr, E);
case PT_MemberPtr:
return this->emitNullMemberPtr(0, nullptr, E);
- case PT_Float:
- return this->emitConstFloat(APFloat::getZero(Ctx.getFloatSemantics(QT)), E);
+ case PT_Float: {
+ APFloat F = APFloat::getZero(Ctx.getFloatSemantics(QT));
+ return this->emitFloat(F, E);
+ }
case PT_FixedPoint: {
auto Sem = Ctx.getASTContext().getFixedPointSemantics(E->getType());
return this->emitConstFixedPoint(FixedPoint::zero(Sem), E);
@@ -4674,10 +4677,7 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD,
if (!visitInitializer(Init))
return false;
- if (!this->emitFinishInit(Init))
- return false;
-
- return this->emitPopPtr(Init);
+ return this->emitFinishInitGlobal(Init);
};
DeclScope<Emitter> LocalScope(this, VD);
@@ -4698,51 +4698,45 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD,
return false;
return !Init || (checkDecl() && initGlobal(*GlobalIndex));
- } else {
- InitLinkScope<Emitter> ILS(this, InitLink::Decl(VD));
-
- if (VarT) {
- unsigned Offset = this->allocateLocalPrimitive(
- VD, *VarT, VD->getType().isConstQualified(), nullptr,
- ScopeKind::Block, IsConstexprUnknown);
- if (Init) {
- // If this is a toplevel declaration, create a scope for the
- // initializer.
- if (Toplevel) {
- LocalScope<Emitter> Scope(this);
- if (!this->visit(Init))
- return false;
- return this->emitSetLocal(*VarT, Offset, VD) && Scope.destroyLocals();
- } else {
- if (!this->visit(Init))
- return false;
- return this->emitSetLocal(*VarT, Offset, VD);
- }
- }
- } else {
- if (std::optional<unsigned> Offset =
- this->allocateLocal(VD, VD->getType(), nullptr, ScopeKind::Block,
- IsConstexprUnknown)) {
- if (!Init)
- return true;
+ }
+ // Local variables.
+ InitLinkScope<Emitter> ILS(this, InitLink::Decl(VD));
- if (!this->emitGetPtrLocal(*Offset, Init))
+ if (VarT) {
+ unsigned Offset = this->allocateLocalPrimitive(
+ VD, *VarT, VD->getType().isConstQualified(), nullptr, ScopeKind::Block,
+ IsConstexprUnknown);
+ if (Init) {
+ // If this is a toplevel declaration, create a scope for the
+ // initializer.
+ if (Toplevel) {
+ LocalScope<Emitter> Scope(this);
+ if (!this->visit(Init))
return false;
-
- if (!visitInitializer(Init))
+ return this->emitSetLocal(*VarT, Offset, VD) && Scope.destroyLocals();
+ } else {
+ if (!this->visit(Init))
return false;
+ return this->emitSetLocal(*VarT, Offset, VD);
+ }
+ }
+ } else {
+ if (std::optional<unsigned> Offset = this->allocateLocal(
+ VD, VD->getType(), nullptr, ScopeKind::Block, IsConstexprUnknown)) {
+ if (!Init)
+ return true;
- if (!this->emitFinishInit(Init))
- return false;
+ if (!this->emitGetPtrLocal(*Offset, Init))
+ return false;
- return this->emitPopPtr(Init);
- }
- return false;
+ if (!visitInitializer(Init))
+ return false;
+
+ return this->emitFinishInitPop(Init);
}
- return true;
+ return false;
}
-
- return false;
+ return true;
}
template <class Emitter>
@@ -4751,8 +4745,10 @@ bool Compiler<Emitter>::visitAPValue(const APValue &Val, PrimType ValType,
assert(!DiscardResult);
if (Val.isInt())
return this->emitConst(Val.getInt(), ValType, E);
- else if (Val.isFloat())
- return this->emitConstFloat(Val.getFloat(), E);
+ else if (Val.isFloat()) {
+ APFloat F = Val.getFloat();
+ return this->emitFloat(F, E);
+ }
if (Val.isLValue()) {
if (Val.isNullPointer())
@@ -6133,8 +6129,10 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
const auto &TargetSemantics = Ctx.getFloatSemantics(E->getType());
if (!this->emitLoadFloat(E))
return false;
- if (!this->emitConstFloat(llvm::APFloat(TargetSemantics, 1), E))
+ APFloat F(TargetSemantics, 1);
+ if (!this->emitFloat(F, E))
return false;
+
if (!this->emitAddf(getFPOptions(E), E))
return false;
if (!this->emitStoreFloat(E))
@@ -6176,8 +6174,10 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
const auto &TargetSemantics = Ctx.getFloatSemantics(E->getType());
if (!this->emitLoadFloat(E))
return false;
- if (!this->emitConstFloat(llvm::APFloat(TargetSemantics, 1), E))
+ APFloat F(TargetSemantics, 1);
+ if (!this->emitFloat(F, E))
return false;
+
if (!this->emitSubf(getFPOptions(E), E))
return false;
if (!this->emitStoreFloat(E))
@@ -6953,6 +6953,20 @@ bool Compiler<Emitter>::emitDummyPtr(const DeclTy &D, const Expr *E) {
return true;
}
+template <class Emitter>
+bool Compiler<Emitter>::emitFloat(const APFloat &F, const Expr *E) {
+ assert(!DiscardResult && "Should've been checked before");
+
+ if (Floating::singleWord(F.getSemantics()))
+ return this->emitConstFloat(Floating(F), E);
+
+ APInt I = F.bitcastToAPInt();
+ return this->emitConstFloat(
+ Floating(const_cast<uint64_t *>(I.getRawData()),
+ llvm::APFloatBase::SemanticsToEnum(F.getSemantics())),
+ E);
+}
+
// This function is constexpr if and only if To, From, and the types of
// all subobjects of To and From are types T such that...
// (3.1) - is_union_v<T> is false;