[llvm] [CopyProf] Add CopyProf instrumentation passes. (PR #207385)

Snehasish Kumar via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 16 23:07:36 PDT 2026


================
@@ -0,0 +1,324 @@
+//===-- CopyProf.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// This file implements the LLVM IR instrumentation passes for CopyProf.
+/// It adds enter/exit callbacks to C++ special member functions, and
+/// instruments store instructions.
+///
+/// The basic idea of the CopyProf algorithm works like this:
+/// An object copy Y is made from original object X. The shadow memory
+/// corresponding to (and owned by) Y is marked as "copied". Any subsequent
+/// memory store to the memory corresponding to Y marks the shadow memory as
+/// "modified". When Y is destroyed and all of its corresponding shadow memory
+/// is marked as "copied", the object is reported as an unnecessary copy.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Instrumentation/CopyProf.h"
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Transforms/Utils/Instrumentation.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
+#include <array>
+#include <cstddef>
+#include <cstdint>
+
+// TODO: Convert CopyProfPass and CopyProfStoresPass to module passes so that
+// the runtime callbacks can be cached, thus avoiding repetitive symbol table
+// lookups.
+
+using namespace llvm;
+
+// Names for the module c'tor to initialize the runtime, and the runtime
+// initialization function itself.
+constexpr StringRef CopyProfModuleCtorName = "copyprof.module_ctor";
+constexpr StringRef CopyProfInitName = "__copyprof_init";
+
+// Runtime callback function names.
+constexpr StringRef CopyProfCtorEnterCallbackName =
+    "__copyprof_ctor_enter_callback";
+constexpr StringRef CopyProfCtorExitCallbackName =
+    "__copyprof_ctor_exit_callback";
+constexpr StringRef CopyProfCopyCtorEnterCallbackName =
+    "__copyprof_copy_ctor_enter_callback";
+constexpr StringRef CopyProfCopyCtorExitCallbackName =
+    "__copyprof_copy_ctor_exit_callback";
+constexpr StringRef CopyProfCopyAssignOpEnterCallbackName =
+    "__copyprof_copy_assign_op_enter_callback";
+constexpr StringRef CopyProfCopyAssignOpExitCallbackName =
+    "__copyprof_copy_assign_op_exit_callback";
+constexpr StringRef CopyProfDtorEnterCallbackName =
+    "__copyprof_dtor_enter_callback";
+constexpr StringRef CopyProfDtorExitCallbackName =
+    "__copyprof_dtor_exit_callback";
+constexpr StringRef CopyProfStoreCallbackName = "__copyprof_store_callback";
+
+// Attribute strings used by the frontend to mark special member functions.
+constexpr StringRef CopyProfCtorAttr = "copyprof-ctor";
+constexpr StringRef CopyProfCopyCtorAttr = "copyprof-copy-ctor";
+constexpr StringRef CopyProfCopyAssignAttr = "copyprof-copy-assign-op";
+constexpr StringRef CopyProfDtorAttr = "copyprof-dtor";
+
+static bool insertModuleCtor(Module &M) {
+  bool Modified = false;
+  getOrCreateSanitizerCtorAndInitFunctions(
+      M, CopyProfModuleCtorName, CopyProfInitName,
+      /*InitArgTypes=*/{},
+      /*InitArgs=*/{}, [&](Function *Ctor, FunctionCallee) {
+        // Mark the ctor so it's never instrumented itself.
+        Ctor->addFnAttr(Attribute::DisableSanitizerInstrumentation);
+        appendToGlobalCtors(M, Ctor, 0);
+        Modified = true;
+      });
+  return Modified;
+}
+
+static bool isCopyProfCandidate(const Function &F) {
+  // Must not instrument functions that are explicitly disallowed for
+  // instrumentation, or naked functions.
+  if (F.isDeclaration() ||
+      F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation) ||
+      F.hasFnAttribute(Attribute::Naked))
+    return false;
+
+  // Don't instrument a function at all if it's ending with a tail call.
+  // Alternatively, the exit callback could be placed before the tail call, but
+  // that would risk missing observable side-effects needed by CopyProf to infer
+  // memory ownership (potentially leading to flase positive reports).
+  // Skipping this function favors false negatives over false positives.
+  for (const BasicBlock &BB : F)
+    if (BB.getTerminatingMustTailCall())
----------------
snehasish wrote:

I think you may need an additional check for regular tail calls in addition to musttail.

https://llvm.org/doxygen/classllvm_1_1EscapeEnumerator.html

https://github.com/llvm/llvm-project/pull/207385


More information about the llvm-commits mailing list