From c66be289901b3f035187d391e80e3610d7d6232e Mon Sep 17 00:00:00 2001 From: Timm Baeder Date: Tue, 17 Jun 2025 18:31:06 +0200 Subject: [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`). 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 --- clang/lib/AST/ByteCode/Compiler.cpp | 112 ++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 49 deletions(-) (limited to 'clang/lib/AST/ByteCode/Compiler.cpp') 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::VisitFloatingLiteral(const FloatingLiteral *E) { if (DiscardResult) return true; - return this->emitConstFloat(E->getValue(), E); + APFloat F = E->getValue(); + return this->emitFloat(F, E); } template @@ -4185,8 +4186,10 @@ bool Compiler::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::visitVarDecl(const VarDecl *VD, if (!visitInitializer(Init)) return false; - if (!this->emitFinishInit(Init)) - return false; - - return this->emitPopPtr(Init); + return this->emitFinishInitGlobal(Init); }; DeclScope LocalScope(this, VD); @@ -4698,51 +4698,45 @@ VarCreationState Compiler::visitVarDecl(const VarDecl *VD, return false; return !Init || (checkDecl() && initGlobal(*GlobalIndex)); - } else { - InitLinkScope 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 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 Offset = - this->allocateLocal(VD, VD->getType(), nullptr, ScopeKind::Block, - IsConstexprUnknown)) { - if (!Init) - return true; + } + // Local variables. + InitLinkScope 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 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 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 @@ -4751,8 +4745,10 @@ bool Compiler::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::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::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::emitDummyPtr(const DeclTy &D, const Expr *E) { return true; } +template +bool Compiler::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(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 is false; -- cgit v1.1