@@ -237,22 +237,82 @@ Expr* ReverseModeVisitor::getStdInitListSizeExpr(const Expr* E) {
237237 return atomicAddCall;
238238 }
239239
240+ namespace {
241+ // Recognises a C heap-memory builtin call and centralises the invariants
242+ // reverse mode must preserve for it, so all memory-op reasoning goes through
243+ // one place instead of ad-hoc name checks scattered across the visitor.
244+ class AllocCallInfo {
245+ public:
246+ enum class Kind { None, Malloc, Calloc, Realloc, Free };
247+
248+ AllocCallInfo () = default ;
249+
250+ // Recognise E as a memory builtin; Kind::None if it is not one. Strips the
251+ // C-style cast that wraps the call (e.g. `(double*)realloc(...)`), so
252+ // IgnoreParenCasts, not IgnoreParenImpCasts, is required here.
253+ static AllocCallInfo recognize (clang::Expr* E) {
254+ auto * CE = llvm::dyn_cast_or_null<clang::CallExpr>(
255+ E ? E->IgnoreParenCasts () : nullptr );
256+ if (!CE )
257+ return {};
258+ const clang::FunctionDecl* FD = CE ->getDirectCallee ();
259+ // getName() asserts on non-identifier names (operators, constructors),
260+ // which vector/STL code produces; the builtins are plain identifiers.
261+ if (!FD || !FD ->getDeclName ().isIdentifier ())
262+ return {};
263+ Kind k = llvm::StringSwitch<Kind>(FD ->getName ())
264+ .Case (" malloc" , Kind::Malloc)
265+ .Case (" calloc" , Kind::Calloc)
266+ .Case (" realloc" , Kind::Realloc)
267+ .Case (" free" , Kind::Free)
268+ .Default (Kind::None);
269+ return AllocCallInfo (k, CE );
270+ }
271+
272+ Kind kind () const { return m_Kind; }
273+ clang::CallExpr* call () const { return m_Call; }
274+ explicit operator bool () const { return m_Kind != Kind::None; }
275+
276+ // The number-of-bytes operand that a following memset must zero:
277+ // malloc(n) -> n, realloc(p, n) -> n. calloc self-zeroes and needs no
278+ // memset, so it (and free/none) report null here.
279+ clang::Expr* memsetByteSize () const {
280+ switch (m_Kind) {
281+ case Kind::Malloc:
282+ return m_Call->getArg (0 );
283+ case Kind::Realloc:
284+ return m_Call->getArg (1 );
285+ default :
286+ return nullptr ;
287+ }
288+ }
289+
290+ // True for an in-place `p = realloc(p, n)`: the LHS is realloc's own
291+ // pointer argument. Only then may the reallocated pointer be kept across
292+ // the call (realloc frees the old block, so a saved pointer would dangle).
293+ bool isInPlaceRealloc (const clang::Expr* LHS ) const {
294+ if (m_Kind != Kind::Realloc || m_Call->getNumArgs () == 0 )
295+ return false ;
296+ const auto * LDRE =
297+ llvm::dyn_cast<clang::DeclRefExpr>(LHS ->IgnoreParenCasts ());
298+ const auto * ArgDRE = llvm::dyn_cast<clang::DeclRefExpr>(
299+ m_Call->getArg (0 )->IgnoreParenCasts ());
300+ return LDRE && ArgDRE && LDRE ->getDecl () == ArgDRE->getDecl ();
301+ }
302+
303+ private:
304+ AllocCallInfo (Kind k, clang::CallExpr* c) : m_Kind(k), m_Call(c) {}
305+ Kind m_Kind = Kind::None;
306+ clang::CallExpr* m_Call = nullptr ;
307+ };
308+ } // namespace
309+
240310 // Both LHS (the memset destination) and the size expression are cloned here:
241311 // callers pass the derived pointer they also assign to and the malloc/realloc
242312 // size they also pass to that call, so cloning keeps the memset a distinct
243313 // subtree.
244314 Expr* ReverseModeVisitor::CheckAndBuildCallToMemset (Expr* LHS , Expr* RHS ) {
245- Expr* size = nullptr ;
246- if (auto * callExpr = dyn_cast_or_null<CallExpr>(RHS ))
247- if (auto * declRef =
248- dyn_cast<DeclRefExpr>(callExpr->getCallee ()->IgnoreImpCasts ()))
249- if (auto * FD = dyn_cast<FunctionDecl>(declRef->getDecl ())) {
250- if (FD ->getNameAsString () == " malloc" )
251- size = callExpr->getArg (0 );
252- else if (FD ->getNameAsString () == " realloc" )
253- size = callExpr->getArg (1 );
254- }
255-
315+ Expr* size = AllocCallInfo::recognize (RHS ).memsetByteSize ();
256316 if (size) {
257317 llvm::SmallVector<Expr*, 3 > args = {
258318 CloneNode (LHS ), getZeroInit (m_Context.IntTy ), CloneNode (size)};
@@ -3174,18 +3234,9 @@ Expr* ReverseModeVisitor::getStdInitListSizeExpr(const Expr* E) {
31743234 // in-place realloc is not handled -- its reverse sweep may read the
31753235 // freed tail out of bounds, as it did before this change; no test
31763236 // exercises it.
3177- bool isReallocAssignment = false ;
3178- if (opCode == BO_Assign)
3179- if (auto * RCall = dyn_cast<CallExpr>(R->IgnoreParenCasts ()))
3180- if (const FunctionDecl* RFD = RCall->getDirectCallee ())
3181- if (RFD ->getNameAsString () == " realloc" && RCall->getNumArgs ()) {
3182- // Only in-place: the LHS must be realloc's own pointer argument.
3183- const auto * LDRE = dyn_cast<DeclRefExpr>(L->IgnoreParenCasts ());
3184- const auto * ArgDRE =
3185- dyn_cast<DeclRefExpr>(RCall->getArg (0 )->IgnoreParenCasts ());
3186- isReallocAssignment =
3187- LDRE && ArgDRE && LDRE ->getDecl () == ArgDRE->getDecl ();
3188- }
3237+ bool isReallocAssignment =
3238+ opCode == BO_Assign &&
3239+ AllocCallInfo::recognize (R).isInPlaceRealloc (L);
31893240
31903241 // Store the value of the LHS of the assignment in the forward pass
31913242 // and restore it in the reverse pass
0 commit comments