[llvm] [Instrumentor] Move instrumentor stub library into its own header file (PR #199329)

Ethan Luis McDonough via llvm-commits llvm-commits at lists.llvm.org
Sat May 23 01:12:32 PDT 2026


https://github.com/EthanLuisMcDonough created https://github.com/llvm/llvm-project/pull/199329

This patch depends on #198366. It moves the instrumentor runtime helper header into its own file and makes it accessible as a string value to the instrumentor pass.

>From c679d998762cdf2db2a185c7fea0a0f0eaa3354c Mon Sep 17 00:00:00 2001
From: Johannes Doerfert <jdoerfert.llvm at gmail.com>
Date: Sun, 17 May 2026 21:27:58 -0700
Subject: [PATCH] [Instrumentor] Improve stub printer (for C/C++ and value
 packs)

The stub printer now emits a helper header to deal with value packs (in
C and C++). We also make the files C/C++ compatible and use the proper
format strings for int32_t and int64_t.
---
 .../llvm/Transforms/IPO/Instrumentor.h        |   1 +
 .../Transforms/IPO/InstrumentorStubPrinter.h  |   5 +
 llvm/lib/Transforms/IPO/Instrumentor.cpp      |  13 +-
 .../IPO/InstrumentorStubPrinter.cpp           | 415 +++++++++++++++++-
 .../Instrumentation/Instrumentor/default_rt   |  37 --
 .../Instrumentation/Instrumentor/default_rt.c | 124 ++++++
 .../Instrumentation/Instrumentor/default_rt.h | 264 +++++++++++
 .../Instrumentor/generate_rt.ll               |   3 +-
 .../Instrumentor/lit.local.cfg                |   1 +
 .../Instrumentor/rt_config.json               | 192 +++++++-
 10 files changed, 1004 insertions(+), 51 deletions(-)
 delete mode 100644 llvm/test/Instrumentation/Instrumentor/default_rt
 create mode 100644 llvm/test/Instrumentation/Instrumentor/default_rt.c
 create mode 100644 llvm/test/Instrumentation/Instrumentor/default_rt.h
 create mode 100644 llvm/test/Instrumentation/Instrumentor/lit.local.cfg

diff --git a/llvm/include/llvm/Transforms/IPO/Instrumentor.h b/llvm/include/llvm/Transforms/IPO/Instrumentor.h
index 5da1a427b242f..60078cbb3f812 100644
--- a/llvm/include/llvm/Transforms/IPO/Instrumentor.h
+++ b/llvm/include/llvm/Transforms/IPO/Instrumentor.h
@@ -64,6 +64,7 @@ struct IRTArg {
     REPLACABLE_CUSTOM = 1 << 2,
     POTENTIALLY_INDIRECT = 1 << 3,
     INDIRECT_HAS_SIZE = 1 << 4,
+    VALUE_PACK = 1 << 5,
     LAST,
   };
 
diff --git a/llvm/include/llvm/Transforms/IPO/InstrumentorStubPrinter.h b/llvm/include/llvm/Transforms/IPO/InstrumentorStubPrinter.h
index 6e1e24d5fef9e..18bb2dc1ff111 100644
--- a/llvm/include/llvm/Transforms/IPO/InstrumentorStubPrinter.h
+++ b/llvm/include/llvm/Transforms/IPO/InstrumentorStubPrinter.h
@@ -26,6 +26,11 @@ namespace instrumentor {
 void printRuntimeStub(const InstrumentationConfig &IConf,
                       StringRef StubRuntimeName, LLVMContext &Ctx);
 
+/// Print the runtime header file that provides helper structures and functions
+/// for reading data generated by the Instrumentor pass.
+void printRuntimeHeader(const InstrumentationConfig &IConf,
+                        StringRef HeaderFileName, LLVMContext &Ctx);
+
 } // end namespace instrumentor
 } // end namespace llvm
 
diff --git a/llvm/lib/Transforms/IPO/Instrumentor.cpp b/llvm/lib/Transforms/IPO/Instrumentor.cpp
index 68f4bb6dae7c9..fc5da00e903ef 100644
--- a/llvm/lib/Transforms/IPO/Instrumentor.cpp
+++ b/llvm/lib/Transforms/IPO/Instrumentor.cpp
@@ -835,12 +835,13 @@ void FunctionIO::init(InstrumentationConfig &IConf,
                "Number of function arguments (without varargs).", IRTArg::NONE,
                std::bind(&FunctionIO::getNumArguments, this, _1, _2, _3, _4)));
   if (Config.has(PassArguments))
-    IRTArgs.push_back(
-        IRTArg(IIRB.PtrTy, "arguments", "Description of the arguments.",
-               IsPRE && Config.has(ReplaceArguments) ? IRTArg::REPLACABLE_CUSTOM
-                                                     : IRTArg::NONE,
-               std::bind(&FunctionIO::getArguments, this, _1, _2, _3, _4),
-               std::bind(&FunctionIO::setArguments, this, _1, _2, _3, _4)));
+    IRTArgs.push_back(IRTArg(
+        IIRB.PtrTy, "arguments", "Description of the arguments.",
+        (IsPRE && Config.has(ReplaceArguments) ? IRTArg::REPLACABLE_CUSTOM
+                                               : IRTArg::NONE) |
+            IRTArg::VALUE_PACK,
+        std::bind(&FunctionIO::getArguments, this, _1, _2, _3, _4),
+        std::bind(&FunctionIO::setArguments, this, _1, _2, _3, _4)));
   if (Config.has(PassIsMain))
     IRTArgs.push_back(IRTArg(IIRB.Int8Ty, "is_main",
                              "Flag to indicate it is the main function.",
diff --git a/llvm/lib/Transforms/IPO/InstrumentorStubPrinter.cpp b/llvm/lib/Transforms/IPO/InstrumentorStubPrinter.cpp
index 2e720e346c5ec..846ad85982e7f 100644
--- a/llvm/lib/Transforms/IPO/InstrumentorStubPrinter.cpp
+++ b/llvm/lib/Transforms/IPO/InstrumentorStubPrinter.cpp
@@ -16,6 +16,7 @@
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/IR/LLVMContext.h"
+#include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
 
 #include <cassert>
@@ -54,9 +55,9 @@ static std::string getPrintfFormatString(Type *Ty, unsigned Flags) {
   if (Ty->isIntegerTy()) {
     if (Ty->getIntegerBitWidth() > 32) {
       assert(Ty->getIntegerBitWidth() == 64);
-      return "%lli";
+      return "%\" PRId64 \"";
     }
-    return "%i";
+    return "%\" PRId32 \"";
   }
   if (Ty->isPointerTy())
     return Flags & IRTArg::STRING ? "%s" : "%p";
@@ -95,6 +96,14 @@ std::pair<std::string, std::string> IRTCallDescription::createCBodies() const {
       if (!isPotentiallyIndirect(IRArg))
         IndirectReturnValue = IRArg.Name;
     }
+
+    // Handle value pack arguments specially
+    if (IRArg.Flags & IRTArg::VALUE_PACK) {
+      DirectFormat += "[value pack at %p]";
+      IndirectFormat += "[value pack at %p]";
+      continue;
+    }
+
     if (!isPotentiallyIndirect(IRArg)) {
       AddToFormats(getPrintfFormatString(IRArg.Ty, IRArg.Flags));
     } else {
@@ -103,7 +112,7 @@ std::pair<std::string, std::string> IRTCallDescription::createCBodies() const {
       IndirectArg += "_ptr";
       // Add the indirect argument size
       if (!(IRArg.Flags & IRTArg::INDIRECT_HAS_SIZE)) {
-        IndirectFormat += ", " + IRArg.Name.str() + "_size: %i";
+        IndirectFormat += ", " + IRArg.Name.str() + "_size: %\" PRId32 \"";
         IndirectArg += ", " + IRArg.Name.str() + "_size";
       }
     }
@@ -111,6 +120,52 @@ std::pair<std::string, std::string> IRTCallDescription::createCBodies() const {
 
   std::string DirectBody = DirectFormat + "\\n\"" + DirectArg + ");\n";
   std::string IndirectBody = IndirectFormat + "\\n\"" + IndirectArg + ");\n";
+
+  // Add value pack element printing
+  for (size_t ArgIdx = 0; ArgIdx < IO.IRTArgs.size(); ++ArgIdx) {
+    auto &IRArg = IO.IRTArgs[ArgIdx];
+    if (!IRArg.Enabled || !(IRArg.Flags & IRTArg::VALUE_PACK))
+      continue;
+
+    // Find the count parameter - it should be the previous enabled argument
+    std::string CountParam;
+    for (int PrevIdx = ArgIdx - 1; PrevIdx >= 0; --PrevIdx) {
+      if (IO.IRTArgs[PrevIdx].Enabled &&
+          IO.IRTArgs[PrevIdx].Name.equals_insensitive(
+              ("num_" + IRArg.Name).str())) {
+        CountParam = IO.IRTArgs[PrevIdx].Name.str();
+        break;
+      }
+    }
+
+    // If no count parameter found, use 0 (will skip iteration)
+    if (CountParam.empty())
+      CountParam = "0 /* count not enabled! */";
+
+    auto AddToBodies = [&](Twine T) {
+      DirectBody += T.str();
+      IndirectBody += T.str();
+    };
+
+    // Direct version: iterate through the value pack at the pointer
+    AddToBodies("  ValuePackIterator iter_" + IRArg.Name.str() + ";\n");
+    AddToBodies("  initValuePackIterator(&iter_" + IRArg.Name.str() + ", " +
+                IRArg.Name.str() + ", " + CountParam + ");\n");
+    AddToBodies("  while (iter_" + IRArg.Name.str() + ".index < iter_" +
+                IRArg.Name.str() + ".count) {\n");
+    AddToBodies("    ValuePackHeader header_" + IRArg.Name.str() +
+                " = getValuePackHeader(&iter_" + IRArg.Name.str() + ");\n");
+    AddToBodies("    const void *data_" + IRArg.Name.str() +
+                " = getValuePackData(&iter_" + IRArg.Name.str() + ");\n");
+    AddToBodies("    printf(\"  [%" PRIu32 "] type=%s size=%" PRIu32
+                " data=%p\\n\", iter_" +
+                IRArg.Name.str() + ".index, getLLVMTypeIDName(header_" +
+                IRArg.Name.str() + ".type_id), header_" + IRArg.Name.str() +
+                ".size, data_" + IRArg.Name.str() + ");\n");
+    AddToBodies("    nextValuePack(&iter_" + IRArg.Name.str() + ");\n");
+    AddToBodies("  }\n");
+  }
+
   if (RetTy)
     IndirectReturnValue = DirectReturnValue = "0";
   if (!DirectReturnValue.empty())
@@ -171,6 +226,316 @@ IRTCallDescription::createCSignature(const InstrumentationConfig &IConf) const {
           MakeSignature(IndirectRetTy, IndirectName, IndirectArgs)};
 }
 
+void printRuntimeHeader(const InstrumentationConfig &IConf,
+                        StringRef HeaderFileName, LLVMContext &Ctx) {
+  if (HeaderFileName.empty())
+    return;
+
+  std::error_code EC;
+  raw_fd_ostream OS(HeaderFileName, EC);
+  if (EC) {
+    Ctx.emitError(
+        Twine("failed to open instrumentor runtime header file for writing: ") +
+        EC.message());
+    return;
+  }
+
+  StringRef Prefix = IConf.getRTName();
+
+  OS << "//===-- Instrumentor Runtime Helper Header "
+        "-------------------------------===//\n";
+  OS << "//\n";
+  OS << "// Part of the LLVM Project, under the Apache License v2.0 with LLVM "
+        "Exceptions.\n";
+  OS << "// See https://llvm.org/LICENSE.txt for license information.\n";
+  OS << "// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n";
+  OS << "//\n";
+  OS << "//"
+        "===-------------------------------------------------------------------"
+        "---===//\n";
+  OS << "//\n";
+  OS << "// This header provides helper structures and functions for reading "
+        "data\n";
+  OS << "// generated by the LLVM Instrumentor pass and passed to runtime "
+        "functions.\n";
+  OS << "//\n";
+  OS << "// Generated with runtime prefix: " << Prefix << "\n";
+  OS << "//\n";
+  OS << "//"
+        "===-------------------------------------------------------------------"
+        "---===//\n\n";
+
+  OS << "#ifndef INSTRUMENTOR_RUNTIME_H\n";
+  OS << "#define INSTRUMENTOR_RUNTIME_H\n\n";
+
+  OS << "#ifdef __cplusplus\n";
+  OS << "extern \"C\" {\n";
+  OS << "#endif\n\n";
+
+  OS << "#include <stdint.h>\n";
+  OS << "#include <string.h>\n\n";
+
+  OS << "#ifdef __cplusplus\n";
+  OS << "}\n";
+  OS << "#endif\n\n";
+
+  // Value pack structures
+  OS << "/// Header for each value in a value pack.\n";
+  OS << "/// Value packs are used to pass function arguments and other "
+        "variable-length\n";
+  OS << "/// data to the runtime. The format is:\n";
+  OS << "///   [ValueHeader][Padding][Value Data]\n";
+  OS << "/// where padding aligns the value data to 8-byte boundaries.\n";
+  OS << "typedef struct {\n";
+  OS << "  uint32_t size;    // Size of the value in bytes\n";
+  OS << "  uint32_t type_id; // LLVM Type::TypeID of the value\n";
+  OS << "} ValuePackHeader;\n\n";
+
+  // Value pack iterator
+  OS << "/// Iterator for reading values from a value pack.\n";
+  OS << "typedef struct {\n";
+  OS << "  const char *current;  // Current position in the pack\n";
+  OS << "  uint64_t offset;      // Byte offset from the start\n";
+  OS << "  uint32_t count;       // Number of elements in the pack\n";
+  OS << "  uint32_t index;       // Current element index\n";
+  OS << "} ValuePackIterator;\n\n";
+
+  OS << "/// Initialize a value pack iterator.\n";
+  OS << "/// \\param iter The iterator to initialize\n";
+  OS << "/// \\param pack_ptr Pointer to the start of the value pack\n";
+  OS << "/// \\param num_elements Number of elements in the pack\n";
+  OS << "static inline void initValuePackIterator(ValuePackIterator *iter, "
+        "const void *pack_ptr, uint32_t num_elements) {\n";
+  OS << "  iter->current = (const char *)pack_ptr;\n";
+  OS << "  iter->offset = 0;\n";
+  OS << "  iter->count = num_elements;\n";
+  OS << "  iter->index = 0;\n";
+  OS << "}\n\n";
+
+  OS << "/// Get the header for the current value.\n";
+  OS << "static inline ValuePackHeader getValuePackHeader(const "
+        "ValuePackIterator *iter) {\n";
+  OS << "  const ValuePackHeader *header = (const ValuePackHeader "
+        "*)iter->current;\n";
+  OS << "  return *header;\n";
+  OS << "}\n\n";
+
+  OS << "/// Get a pointer to the current value data.\n";
+  OS << "static inline const void *getValuePackData(const ValuePackIterator "
+        "*iter) {\n";
+  OS << "  // Skip header (8 bytes: size + type_id)\n";
+  OS << "  const char *data_start = iter->current + sizeof(ValuePackHeader);\n";
+  OS << "  // Calculate padding for 8-byte alignment\n";
+  OS << "  ValuePackHeader header = getValuePackHeader(iter);\n";
+  OS << "  uint32_t padding = (8 - (header.size % 8)) % 8;\n";
+  OS << "  // Skip padding\n";
+  OS << "  return data_start + padding;\n";
+  OS << "}\n\n";
+
+  OS << "/// Move to the next value in the pack.\n";
+  OS << "static inline void nextValuePack(ValuePackIterator *iter) {\n";
+  OS << "  if (iter->index >= iter->count) {\n";
+  OS << "    iter->current = NULL;\n";
+  OS << "    return;\n";
+  OS << "  }\n";
+  OS << "  ValuePackHeader header = getValuePackHeader(iter);\n";
+  OS << "  uint32_t padding = (8 - (header.size % 8)) % 8;\n";
+  OS << "  uint64_t advance = sizeof(ValuePackHeader) + padding + "
+        "header.size;\n";
+  OS << "  iter->current += advance;\n";
+  OS << "  iter->offset += advance;\n";
+  OS << "  iter->index++;\n";
+  OS << "}\n\n";
+
+  OS << "/// Get the current offset in bytes from the start of the pack.\n";
+  OS << "static inline uint64_t getValuePackOffset(const ValuePackIterator "
+        "*iter) {\n";
+  OS << "  return iter->offset;\n";
+  OS << "}\n\n";
+
+  OS << "/// Extract a specific value from a value pack by index.\n";
+  OS << "///\n";
+  OS << "/// \\param pack_ptr Pointer to the start of the value pack\n";
+  OS << "/// \\param num_elements Number of elements in the pack\n";
+  OS << "/// \\param index Zero-based index of the value to extract\n";
+  OS << "/// \\param header Output parameter for the value header (can be "
+        "NULL)\n";
+  OS << "/// \\return Pointer to the value data, or NULL if index is out of "
+        "bounds\n";
+  OS << "static inline const void *getValuePackEntry(const void *pack_ptr, "
+        "uint32_t num_elements,\n";
+  OS << "                                             uint32_t index, "
+        "ValuePackHeader *header) {\n";
+  OS << "  if (!pack_ptr || index >= num_elements)\n";
+  OS << "    return NULL;\n\n";
+  OS << "  ValuePackIterator iter;\n";
+  OS << "  initValuePackIterator(&iter, pack_ptr, num_elements);\n\n";
+  OS << "  while (iter.current != NULL && iter.index < iter.count) {\n";
+  OS << "    ValuePackHeader h = getValuePackHeader(&iter);\n";
+  OS << "    if (iter.index == index) {\n";
+  OS << "      if (header)\n";
+  OS << "        *header = h;\n";
+  OS << "      return getValuePackData(&iter);\n";
+  OS << "    }\n";
+  OS << "    nextValuePack(&iter);\n";
+  OS << "  }\n\n";
+  OS << "  return NULL; // Index out of bounds\n";
+  OS << "}\n\n";
+
+  // LLVM Type IDs enum
+  OS << "/// LLVM Type IDs for interpreting value pack data.\n";
+  OS << "/// These correspond to llvm::Type::TypeID enum values.\n";
+  OS << "enum LLVMTypeID {\n";
+  OS << "  HalfTyID = 0,  ///< 16-bit floating point type\n";
+  OS << "  BFloatTyID,    ///< 16-bit floating point type (7-bit "
+        "significand)\n";
+  OS << "  FloatTyID,     ///< 32-bit floating point type\n";
+  OS << "  DoubleTyID,    ///< 64-bit floating point type\n";
+  OS << "  X86_FP80TyID,  ///< 80-bit floating point type (X87)\n";
+  OS << "  FP128TyID,     ///< 128-bit floating point type (112-bit "
+        "significand)\n";
+  OS << "  PPC_FP128TyID, ///< 128-bit floating point type (two 64-bits, "
+        "PowerPC)\n";
+  OS << "  VoidTyID,      ///< type with no size\n";
+  OS << "  LabelTyID,     ///< Labels\n";
+  OS << "  MetadataTyID,  ///< Metadata\n";
+  OS << "  X86_AMXTyID,   ///< AMX vectors (8192 bits, X86 specific)\n";
+  OS << "  TokenTyID,     ///< Tokens\n";
+
+  OS << "  // Derived types... see DerivedTypes.h file.\n";
+  OS << "  IntegerTyID,        ///< Arbitrary bit width integers\n";
+  OS << "  ByteTyID,           ///< Arbitrary bit width bytes\n";
+  OS << "  FunctionTyID,       ///< Functions\n";
+  OS << "  PointerTyID,        ///< Pointers\n";
+  OS << "  StructTyID,         ///< Structures\n";
+  OS << "  ArrayTyID,          ///< Arrays\n";
+  OS << "  FixedVectorTyID,    ///< Fixed width SIMD vector type\n";
+  OS << "  ScalableVectorTyID, ///< Scalable SIMD vector type\n";
+  OS << "  TypedPointerTyID,   ///< Typed pointer used by some GPU targets\n";
+  OS << "  TargetExtTyID,      ///< Target extension type\n";
+  OS << "};\n\n";
+
+  // Type ID printer function
+  OS << "/// Get the string name of an LLVM Type ID.\n";
+  OS << "static inline const char *getLLVMTypeIDName(uint32_t type_id) {\n";
+  OS << "  switch (type_id) {\n";
+  OS << "  case HalfTyID: return \"half\";\n";
+  OS << "  case BFloatTyID: return \"bfloat\";\n";
+  OS << "  case FloatTyID: return \"float\";\n";
+  OS << "  case DoubleTyID: return \"double\";\n";
+  OS << "  case X86_FP80TyID: return \"x86_fp80\";\n";
+  OS << "  case FP128TyID: return \"fp128\";\n";
+  OS << "  case PPC_FP128TyID: return \"ppc_fp128\";\n";
+  OS << "  case VoidTyID: return \"void\";\n";
+  OS << "  case LabelTyID: return \"label\";\n";
+  OS << "  case MetadataTyID: return \"metadata\";\n";
+  OS << "  case X86_AMXTyID: return \"x86_amx\";\n";
+  OS << "  case TokenTyID: return \"token\";\n";
+  OS << "  case IntegerTyID: return \"integer\";\n";
+  OS << "  case ByteTyID: return \"integer\";\n";
+  OS << "  case FunctionTyID: return \"function\";\n";
+  OS << "  case PointerTyID: return \"pointer\";\n";
+  OS << "  case StructTyID: return \"struct\";\n";
+  OS << "  case ArrayTyID: return \"array\";\n";
+  OS << "  case FixedVectorTyID: return \"fixed_vector\";\n";
+  OS << "  case ScalableVectorTyID: return \"scalable_vector\";\n";
+  OS << "  case TypedPointerTyID: return \"typed_pointer\";\n";
+  OS << "  case TargetExtTyID: return \"target_ext\";\n";
+  OS << "  default: return \"unknown\";\n";
+  OS << "  }\n";
+  OS << "}\n\n";
+
+  // C++ overlays section
+  OS << "#ifdef __cplusplus\n\n";
+
+  OS << "// C++ overlays for range-based iteration and quality of life "
+        "improvements\n\n";
+
+  // ValuePackRange class
+  OS << "/// Range wrapper for value packs enabling range-based for loops.\n";
+  OS << "/// Example:\n";
+  OS << "///   for (auto val : ValuePackRange(pack_ptr, num_elements)) {\n";
+  OS << "///     // val provides access to header and data\n";
+  OS << "///   }\n";
+  OS << "class ValuePackRange {\n";
+  OS << "public:\n";
+  OS << "  struct ValueRef {\n";
+  OS << "    ValuePackHeader header;\n";
+  OS << "    const void *data;\n\n";
+  OS << "    uint32_t type_id() const { return header.type_id; }\n";
+  OS << "    uint32_t size() const { return header.size; }\n";
+  OS << "    const char *type_name() const { return "
+        "getLLVMTypeIDName(header.type_id); }\n\n";
+  OS << "    template <typename T> const T &as() const {\n";
+  OS << "      return *static_cast<const T*>(data);\n";
+  OS << "    }\n";
+  OS << "    template <typename T> const T *ptr() const {\n";
+  OS << "      return static_cast<const T*>(data);\n";
+  OS << "    }\n";
+  OS << "  };\n\n";
+
+  OS << "  class iterator {\n";
+  OS << "  public:\n";
+  OS << "    iterator(const void *ptr, uint32_t num_elements, uint64_t "
+        "max_offset)\n";
+  OS << "        : max_offset_(max_offset) {\n";
+  OS << "      initValuePackIterator(&iter_, ptr, num_elements);\n";
+  OS << "      if (ptr && !is_valid_position()) iter_.current = nullptr;\n";
+  OS << "    }\n\n";
+  OS << "    ValueRef operator*() const {\n";
+  OS << "      return ValueRef{\n";
+  OS << "        getValuePackHeader(&iter_),\n";
+  OS << "        getValuePackData(&iter_)\n";
+  OS << "      };\n";
+  OS << "    }\n\n";
+  OS << "    iterator &operator++() {\n";
+  OS << "      nextValuePack(&iter_);\n";
+  OS << "      if (!is_valid_position()) iter_.current = nullptr;\n";
+  OS << "      return *this;\n";
+  OS << "    }\n\n";
+  OS << "    bool operator!=(const iterator &other) const {\n";
+  OS << "      return iter_.current != other.iter_.current;\n";
+  OS << "    }\n\n";
+  OS << "  private:\n";
+  OS << "    bool is_valid_position() const {\n";
+  OS << "      if (!iter_.current) return false;\n";
+  OS << "      if (iter_.index >= iter_.count) return false;\n";
+  OS << "      if (max_offset_ > 0 && iter_.offset >= max_offset_) return "
+        "false;\n";
+  OS << "      return true;\n";
+  OS << "    }\n\n";
+  OS << "    ValuePackIterator iter_;\n";
+  OS << "    uint64_t max_offset_;\n";
+  OS << "  };\n\n";
+
+  OS << "  ValuePackRange(const void *ptr, uint32_t num_elements, uint64_t "
+        "max_size = 0)\n";
+  OS << "      : ptr_(ptr), num_elements_(num_elements), max_size_(max_size) "
+        "{}\n\n";
+  OS << "  iterator begin() const { return iterator(ptr_, num_elements_, "
+        "max_size_); }\n";
+  OS << "  iterator end() const { return iterator(nullptr, 0, 0); }\n\n";
+  OS << "private:\n";
+  OS << "  const void *ptr_;\n";
+  OS << "  uint32_t num_elements_;\n";
+  OS << "  uint64_t max_size_;\n";
+  OS << "};\n\n";
+
+  // Helper template functions
+  OS << "/// Template helper to extract a typed value from a value pack by "
+        "index.\n";
+  OS << "template <typename T>\n";
+  OS << "inline const T *getValueAs(const void *pack_ptr, uint32_t "
+        "num_elements, uint32_t index) {\n";
+  OS << "  return static_cast<const T*>(getValuePackEntry(pack_ptr, "
+        "num_elements, index, nullptr));\n";
+  OS << "}\n\n";
+
+  OS << "#endif // __cplusplus\n\n";
+
+  OS << "#endif // INSTRUMENTOR_RUNTIME_H\n";
+}
+
 void printRuntimeStub(const InstrumentationConfig &IConf,
                       StringRef StubRuntimeName, LLVMContext &Ctx) {
   if (StubRuntimeName.empty())
@@ -185,9 +550,45 @@ void printRuntimeStub(const InstrumentationConfig &IConf,
     return;
   }
 
-  OS << "// LLVM Instrumentor stub runtime\n\n";
+  // Generate the header file alongside the stub
+  StringRef Prefix = IConf.getRTName();
+  std::string HeaderFileName = StubRuntimeName.str();
+  size_t DotPos = HeaderFileName.rfind('.');
+  if (DotPos != std::string::npos)
+    HeaderFileName = HeaderFileName.substr(0, DotPos);
+  HeaderFileName += ".h";
+  printRuntimeHeader(IConf, HeaderFileName, Ctx);
+
+  OS << "//===-- Instrumentor Runtime Stub "
+        "-----------------------------------------===//\n";
+  OS << "//\n";
+  OS << "// Part of the LLVM Project, under the Apache License v2.0 with LLVM "
+        "Exceptions.\n";
+  OS << "// See https://llvm.org/LICENSE.txt for license information.\n";
+  OS << "// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n";
+  OS << "//\n";
+  OS << "//"
+        "===-------------------------------------------------------------------"
+        "---===//\n";
+  OS << "//\n";
+  OS << "// This file is auto-generated by the LLVM Instrumentor pass.\n";
+  OS << "// It provides stub implementations of instrumentation runtime "
+        "functions\n";
+  OS << "// that print human-readable information about instrumentation "
+        "events.\n";
+  OS << "//\n";
+  OS << "// Generated with runtime prefix: " << Prefix << "\n";
+  OS << "//\n";
+  OS << "//"
+        "===-------------------------------------------------------------------"
+        "---===//\n\n";
+  OS << "#include <inttypes.h>\n";
   OS << "#include <stdint.h>\n";
-  OS << "#include <stdio.h>\n\n";
+  OS << "#include <stdio.h>\n";
+  OS << "#include \"" << llvm::sys::path::filename(HeaderFileName) << "\"\n\n";
+  OS << "#ifdef __cplusplus\n";
+  OS << "extern \"C\" {\n";
+  OS << "#endif\n\n";
 
   for (auto &ChoiceMap : IConf.IChoices) {
     for (auto &[_, IO] : ChoiceMap) {
@@ -206,6 +607,10 @@ void printRuntimeStub(const InstrumentationConfig &IConf,
       }
     }
   }
+
+  OS << "#ifdef __cplusplus\n";
+  OS << "}\n";
+  OS << "#endif\n";
 }
 
 } // end namespace instrumentor
diff --git a/llvm/test/Instrumentation/Instrumentor/default_rt b/llvm/test/Instrumentation/Instrumentor/default_rt
deleted file mode 100644
index 1e9750b2f6874..0000000000000
--- a/llvm/test/Instrumentation/Instrumentor/default_rt
+++ /dev/null
@@ -1,37 +0,0 @@
-// LLVM Instrumentor stub runtime
-
-#include <stdint.h>
-#include <stdio.h>
-
-void *__instrumentor_pre_load(void *pointer, int32_t pointer_as, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
-  printf("load pre -- pointer: %p, pointer_as: %i, value_size: %lli, alignment: %lli, value_type_id: %i, atomicity_ordering: %i, sync_scope_id: %i, is_volatile: %i, id: %i\n", pointer, pointer_as, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
-  return pointer;
-}
-
-void *__instrumentor_pre_store(void *pointer, int32_t pointer_as, int64_t value, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
-  printf("store pre -- pointer: %p, pointer_as: %i, value: %lli, value_size: %lli, alignment: %lli, value_type_id: %i, atomicity_ordering: %i, sync_scope_id: %i, is_volatile: %i, id: %i\n", pointer, pointer_as, value, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
-  return pointer;
-}
-
-void *__instrumentor_pre_store_ind(void *pointer, int32_t pointer_as, int64_t *value_ptr, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
-  printf("store pre -- pointer: %p, pointer_as: %i, value: %p, value_size: %lli, alignment: %lli, value_type_id: %i, atomicity_ordering: %i, sync_scope_id: %i, is_volatile: %i, id: %i\n", pointer, pointer_as, value_ptr, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
-  return pointer;
-}
-
-int64_t __instrumentor_post_load(void *pointer, int32_t pointer_as, int64_t value, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
-  printf("load post -- pointer: %p, pointer_as: %i, value: %lli, value_size: %lli, alignment: %lli, value_type_id: %i, atomicity_ordering: %i, sync_scope_id: %i, is_volatile: %i, id: %i\n", pointer, pointer_as, value, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
-  return value;
-}
-
-void __instrumentor_post_load_ind(void *pointer, int32_t pointer_as, int64_t *value_ptr, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
-  printf("load post -- pointer: %p, pointer_as: %i, value: %p, value_size: %lli, alignment: %lli, value_type_id: %i, atomicity_ordering: %i, sync_scope_id: %i, is_volatile: %i, id: %i\n", pointer, pointer_as, value_ptr, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
-}
-
-void __instrumentor_post_store(void *pointer, int32_t pointer_as, int64_t value, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
-  printf("store post -- pointer: %p, pointer_as: %i, value: %lli, value_size: %lli, alignment: %lli, value_type_id: %i, atomicity_ordering: %i, sync_scope_id: %i, is_volatile: %i, id: %i\n", pointer, pointer_as, value, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
-}
-
-void __instrumentor_post_store_ind(void *pointer, int32_t pointer_as, int64_t *value_ptr, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
-  printf("store post -- pointer: %p, pointer_as: %i, value: %p, value_size: %lli, alignment: %lli, value_type_id: %i, atomicity_ordering: %i, sync_scope_id: %i, is_volatile: %i, id: %i\n", pointer, pointer_as, value_ptr, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
-}
-
diff --git a/llvm/test/Instrumentation/Instrumentor/default_rt.c b/llvm/test/Instrumentation/Instrumentor/default_rt.c
new file mode 100644
index 0000000000000..d0bbf6e992b59
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/default_rt.c
@@ -0,0 +1,124 @@
+//===-- Instrumentor Runtime Stub -----------------------------------------===//
+//
+// 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 is auto-generated by the LLVM Instrumentor pass.
+// It provides stub implementations of instrumentation runtime functions
+// that print human-readable information about instrumentation events.
+//
+// Generated with runtime prefix: __instrumentor_
+//
+//===----------------------------------------------------------------------===//
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include "default_rt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __instrumentor_pre_module(char *module_name, char *target_triple, int32_t id) {
+  printf("module pre -- module_name: %s, target_triple: %s, id: %" PRId32 "\n", module_name, target_triple, id);
+}
+
+void __instrumentor_post_module(char *module_name, char *target_triple, int32_t id) {
+  printf("module post -- module_name: %s, target_triple: %s, id: %" PRId32 "\n", module_name, target_triple, id);
+}
+
+void *__instrumentor_pre_global(void *address, int32_t address_space, int64_t declared_size, int64_t alignment, char *name, int64_t initial_value, int8_t is_constant, int8_t is_definition, int32_t id) {
+  printf("global pre -- address: %p, address_space: %" PRId32 ", declared_size: %" PRId64 ", alignment: %" PRId64 ", name: %s, initial_value: %" PRId64 ", is_constant: %" PRId32 ", is_definition: %" PRId32 ", id: %" PRId32 "\n", address, address_space, declared_size, alignment, name, initial_value, is_constant, is_definition, id);
+  return address;
+}
+
+void *__instrumentor_pre_global_ind(void *address, int32_t address_space, int64_t declared_size, int64_t alignment, char *name, int64_t *initial_value_ptr, int8_t is_constant, int8_t is_definition, int32_t id) {
+  printf("global pre -- address: %p, address_space: %" PRId32 ", declared_size: %" PRId64 ", alignment: %" PRId64 ", name: %s, initial_value: %p, is_constant: %" PRId32 ", is_definition: %" PRId32 ", id: %" PRId32 "\n", address, address_space, declared_size, alignment, name, initial_value_ptr, is_constant, is_definition, id);
+  return address;
+}
+
+void __instrumentor_post_global(void *address, int32_t address_space, int64_t declared_size, int64_t alignment, char *name, int64_t initial_value, int8_t is_constant, int8_t is_definition, int32_t id) {
+  printf("global post -- address: %p, address_space: %" PRId32 ", declared_size: %" PRId64 ", alignment: %" PRId64 ", name: %s, initial_value: %" PRId64 ", is_constant: %" PRId32 ", is_definition: %" PRId32 ", id: %" PRId32 "\n", address, address_space, declared_size, alignment, name, initial_value, is_constant, is_definition, id);
+}
+
+void __instrumentor_post_global_ind(void *address, int32_t address_space, int64_t declared_size, int64_t alignment, char *name, int64_t *initial_value_ptr, int8_t is_constant, int8_t is_definition, int32_t id) {
+  printf("global post -- address: %p, address_space: %" PRId32 ", declared_size: %" PRId64 ", alignment: %" PRId64 ", name: %s, initial_value: %p, is_constant: %" PRId32 ", is_definition: %" PRId32 ", id: %" PRId32 "\n", address, address_space, declared_size, alignment, name, initial_value_ptr, is_constant, is_definition, id);
+}
+
+void __instrumentor_pre_function(void *address, char *name, int32_t num_arguments, void *arguments, int8_t is_main, int32_t id) {
+  printf("function pre -- address: %p, name: %s, num_arguments: %" PRId32 ", arguments: [value pack at %p], is_main: %" PRId32 ", id: %" PRId32 "\n", address, name, num_arguments, arguments, is_main, id);
+  ValuePackIterator iter_arguments;
+  initValuePackIterator(&iter_arguments, arguments, num_arguments);
+  while (iter_arguments.index < iter_arguments.count) {
+    ValuePackHeader header_arguments = getValuePackHeader(&iter_arguments);
+    const void *data_arguments = getValuePackData(&iter_arguments);
+    printf("  [%u] type=%s size=%u data=%p\n", iter_arguments.index, getLLVMTypeIDName(header_arguments.type_id), header_arguments.size, data_arguments);
+    nextValuePack(&iter_arguments);
+  }
+}
+
+void __instrumentor_post_function(void *address, char *name, int32_t num_arguments, void *arguments, int8_t is_main, int32_t id) {
+  printf("function post -- address: %p, name: %s, num_arguments: %" PRId32 ", arguments: [value pack at %p], is_main: %" PRId32 ", id: %" PRId32 "\n", address, name, num_arguments, arguments, is_main, id);
+  ValuePackIterator iter_arguments;
+  initValuePackIterator(&iter_arguments, arguments, num_arguments);
+  while (iter_arguments.index < iter_arguments.count) {
+    ValuePackHeader header_arguments = getValuePackHeader(&iter_arguments);
+    const void *data_arguments = getValuePackData(&iter_arguments);
+    printf("  [%u] type=%s size=%u data=%p\n", iter_arguments.index, getLLVMTypeIDName(header_arguments.type_id), header_arguments.size, data_arguments);
+    nextValuePack(&iter_arguments);
+  }
+}
+
+void __instrumentor_pre_unreachable(int32_t id) {
+  printf("unreachable pre -- id: %" PRId32 "\n", id);
+}
+
+void *__instrumentor_pre_load(void *pointer, int32_t pointer_as, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
+  printf("load pre -- pointer: %p, pointer_as: %" PRId32 ", value_size: %" PRId64 ", alignment: %" PRId64 ", value_type_id: %" PRId32 ", atomicity_ordering: %" PRId32 ", sync_scope_id: %" PRId32 ", is_volatile: %" PRId32 ", id: %" PRId32 "\n", pointer, pointer_as, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+  return pointer;
+}
+
+int64_t __instrumentor_pre_alloca(int64_t size, int64_t alignment, int32_t id) {
+  printf("alloca pre -- size: %" PRId64 ", alignment: %" PRId64 ", id: %" PRId32 "\n", size, alignment, id);
+  return size;
+}
+
+void *__instrumentor_pre_store(void *pointer, int32_t pointer_as, int64_t value, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
+  printf("store pre -- pointer: %p, pointer_as: %" PRId32 ", value: %" PRId64 ", value_size: %" PRId64 ", alignment: %" PRId64 ", value_type_id: %" PRId32 ", atomicity_ordering: %" PRId32 ", sync_scope_id: %" PRId32 ", is_volatile: %" PRId32 ", id: %" PRId32 "\n", pointer, pointer_as, value, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+  return pointer;
+}
+
+void *__instrumentor_pre_store_ind(void *pointer, int32_t pointer_as, int64_t *value_ptr, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
+  printf("store pre -- pointer: %p, pointer_as: %" PRId32 ", value: %p, value_size: %" PRId64 ", alignment: %" PRId64 ", value_type_id: %" PRId32 ", atomicity_ordering: %" PRId32 ", sync_scope_id: %" PRId32 ", is_volatile: %" PRId32 ", id: %" PRId32 "\n", pointer, pointer_as, value_ptr, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+  return pointer;
+}
+
+int64_t __instrumentor_post_load(void *pointer, int32_t pointer_as, int64_t value, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
+  printf("load post -- pointer: %p, pointer_as: %" PRId32 ", value: %" PRId64 ", value_size: %" PRId64 ", alignment: %" PRId64 ", value_type_id: %" PRId32 ", atomicity_ordering: %" PRId32 ", sync_scope_id: %" PRId32 ", is_volatile: %" PRId32 ", id: %" PRId32 "\n", pointer, pointer_as, value, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+  return value;
+}
+
+void __instrumentor_post_load_ind(void *pointer, int32_t pointer_as, int64_t *value_ptr, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
+  printf("load post -- pointer: %p, pointer_as: %" PRId32 ", value: %p, value_size: %" PRId64 ", alignment: %" PRId64 ", value_type_id: %" PRId32 ", atomicity_ordering: %" PRId32 ", sync_scope_id: %" PRId32 ", is_volatile: %" PRId32 ", id: %" PRId32 "\n", pointer, pointer_as, value_ptr, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+}
+
+void *__instrumentor_post_alloca(void *address, int64_t size, int64_t alignment, int32_t id) {
+  printf("alloca post -- address: %p, size: %" PRId64 ", alignment: %" PRId64 ", id: %" PRId32 "\n", address, size, alignment, id);
+  return address;
+}
+
+void __instrumentor_post_store(void *pointer, int32_t pointer_as, int64_t value, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
+  printf("store post -- pointer: %p, pointer_as: %" PRId32 ", value: %" PRId64 ", value_size: %" PRId64 ", alignment: %" PRId64 ", value_type_id: %" PRId32 ", atomicity_ordering: %" PRId32 ", sync_scope_id: %" PRId32 ", is_volatile: %" PRId32 ", id: %" PRId32 "\n", pointer, pointer_as, value, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+}
+
+void __instrumentor_post_store_ind(void *pointer, int32_t pointer_as, int64_t *value_ptr, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t atomicity_ordering, int8_t sync_scope_id, int8_t is_volatile, int32_t id) {
+  printf("store post -- pointer: %p, pointer_as: %" PRId32 ", value: %p, value_size: %" PRId64 ", alignment: %" PRId64 ", value_type_id: %" PRId32 ", atomicity_ordering: %" PRId32 ", sync_scope_id: %" PRId32 ", is_volatile: %" PRId32 ", id: %" PRId32 "\n", pointer, pointer_as, value_ptr, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/llvm/test/Instrumentation/Instrumentor/default_rt.h b/llvm/test/Instrumentation/Instrumentor/default_rt.h
new file mode 100644
index 0000000000000..174e6a6934124
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/default_rt.h
@@ -0,0 +1,264 @@
+//===-- Instrumentor Runtime Helper Header -------------------------------===//
+//
+// 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 header provides helper structures and functions for reading data
+// generated by the LLVM Instrumentor pass and passed to runtime functions.
+//
+// Generated with runtime prefix: __instrumentor_
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef INSTRUMENTOR_RUNTIME_H
+#define INSTRUMENTOR_RUNTIME_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <string.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+/// Header for each value in a value pack.
+/// Value packs are used to pass function arguments and other variable-length
+/// data to the runtime. The format is:
+///   [ValueHeader][Padding][Value Data]
+/// where padding aligns the value data to 8-byte boundaries.
+typedef struct {
+  uint32_t size;    // Size of the value in bytes
+  uint32_t type_id; // LLVM Type::TypeID of the value
+} ValuePackHeader;
+
+/// Iterator for reading values from a value pack.
+typedef struct {
+  const char *current;  // Current position in the pack
+  uint64_t offset;      // Byte offset from the start
+  uint32_t count;       // Number of elements in the pack
+  uint32_t index;       // Current element index
+} ValuePackIterator;
+
+/// Initialize a value pack iterator.
+/// \param iter The iterator to initialize
+/// \param pack_ptr Pointer to the start of the value pack
+/// \param num_elements Number of elements in the pack
+static inline void initValuePackIterator(ValuePackIterator *iter, const void *pack_ptr, uint32_t num_elements) {
+  iter->current = (const char *)pack_ptr;
+  iter->offset = 0;
+  iter->count = num_elements;
+  iter->index = 0;
+}
+
+/// Get the header for the current value.
+static inline ValuePackHeader getValuePackHeader(const ValuePackIterator *iter) {
+  const ValuePackHeader *header = (const ValuePackHeader *)iter->current;
+  return *header;
+}
+
+/// Get a pointer to the current value data.
+static inline const void *getValuePackData(const ValuePackIterator *iter) {
+  // Skip header (8 bytes: size + type_id)
+  const char *data_start = iter->current + sizeof(ValuePackHeader);
+  // Calculate padding for 8-byte alignment
+  ValuePackHeader header = getValuePackHeader(iter);
+  uint32_t padding = (8 - (header.size % 8)) % 8;
+  // Skip padding
+  return data_start + padding;
+}
+
+/// Move to the next value in the pack.
+static inline void nextValuePack(ValuePackIterator *iter) {
+  if (iter->index >= iter->count) {
+    iter->current = NULL;
+    return;
+  }
+  ValuePackHeader header = getValuePackHeader(iter);
+  uint32_t padding = (8 - (header.size % 8)) % 8;
+  uint64_t advance = sizeof(ValuePackHeader) + padding + header.size;
+  iter->current += advance;
+  iter->offset += advance;
+  iter->index++;
+}
+
+/// Get the current offset in bytes from the start of the pack.
+static inline uint64_t getValuePackOffset(const ValuePackIterator *iter) {
+  return iter->offset;
+}
+
+/// Extract a specific value from a value pack by index.
+///
+/// \param pack_ptr Pointer to the start of the value pack
+/// \param num_elements Number of elements in the pack
+/// \param index Zero-based index of the value to extract
+/// \param header Output parameter for the value header (can be NULL)
+/// \return Pointer to the value data, or NULL if index is out of bounds
+static inline const void *getValuePackEntry(const void *pack_ptr, uint32_t num_elements,
+                                             uint32_t index, ValuePackHeader *header) {
+  if (!pack_ptr || index >= num_elements)
+    return NULL;
+
+  ValuePackIterator iter;
+  initValuePackIterator(&iter, pack_ptr, num_elements);
+
+  while (iter.current != NULL && iter.index < iter.count) {
+    ValuePackHeader h = getValuePackHeader(&iter);
+    if (iter.index == index) {
+      if (header)
+        *header = h;
+      return getValuePackData(&iter);
+    }
+    nextValuePack(&iter);
+  }
+
+  return NULL; // Index out of bounds
+}
+
+/// LLVM Type IDs for interpreting value pack data.
+/// These correspond to llvm::Type::TypeID enum values.
+enum LLVMTypeID {
+  HalfTyID = 0,  ///< 16-bit floating point type
+  BFloatTyID,    ///< 16-bit floating point type (7-bit significand)
+  FloatTyID,     ///< 32-bit floating point type
+  DoubleTyID,    ///< 64-bit floating point type
+  X86_FP80TyID,  ///< 80-bit floating point type (X87)
+  FP128TyID,     ///< 128-bit floating point type (112-bit significand)
+  PPC_FP128TyID, ///< 128-bit floating point type (two 64-bits, PowerPC)
+  VoidTyID,      ///< type with no size
+  LabelTyID,     ///< Labels
+  MetadataTyID,  ///< Metadata
+  X86_AMXTyID,   ///< AMX vectors (8192 bits, X86 specific)
+  TokenTyID,     ///< Tokens
+  // Derived types... see DerivedTypes.h file.
+  IntegerTyID,        ///< Arbitrary bit width integers
+  ByteTyID,           ///< Arbitrary bit width bytes
+  FunctionTyID,       ///< Functions
+  PointerTyID,        ///< Pointers
+  StructTyID,         ///< Structures
+  ArrayTyID,          ///< Arrays
+  FixedVectorTyID,    ///< Fixed width SIMD vector type
+  ScalableVectorTyID, ///< Scalable SIMD vector type
+  TypedPointerTyID,   ///< Typed pointer used by some GPU targets
+  TargetExtTyID,      ///< Target extension type
+};
+
+/// Get the string name of an LLVM Type ID.
+static inline const char *getLLVMTypeIDName(uint32_t type_id) {
+  switch (type_id) {
+  case HalfTyID: return "half";
+  case BFloatTyID: return "bfloat";
+  case FloatTyID: return "float";
+  case DoubleTyID: return "double";
+  case X86_FP80TyID: return "x86_fp80";
+  case FP128TyID: return "fp128";
+  case PPC_FP128TyID: return "ppc_fp128";
+  case VoidTyID: return "void";
+  case LabelTyID: return "label";
+  case MetadataTyID: return "metadata";
+  case X86_AMXTyID: return "x86_amx";
+  case TokenTyID: return "token";
+  case IntegerTyID: return "integer";
+  case ByteTyID: return "integer";
+  case FunctionTyID: return "function";
+  case PointerTyID: return "pointer";
+  case StructTyID: return "struct";
+  case ArrayTyID: return "array";
+  case FixedVectorTyID: return "fixed_vector";
+  case ScalableVectorTyID: return "scalable_vector";
+  case TypedPointerTyID: return "typed_pointer";
+  case TargetExtTyID: return "target_ext";
+  default: return "unknown";
+  }
+}
+
+#ifdef __cplusplus
+
+// C++ overlays for range-based iteration and quality of life improvements
+
+/// Range wrapper for value packs enabling range-based for loops.
+/// Example:
+///   for (auto val : ValuePackRange(pack_ptr, num_elements)) {
+///     // val provides access to header and data
+///   }
+class ValuePackRange {
+public:
+  struct ValueRef {
+    ValuePackHeader header;
+    const void *data;
+
+    uint32_t type_id() const { return header.type_id; }
+    uint32_t size() const { return header.size; }
+    const char *type_name() const { return getLLVMTypeIDName(header.type_id); }
+
+    template <typename T> const T &as() const {
+      return *static_cast<const T*>(data);
+    }
+    template <typename T> const T *ptr() const {
+      return static_cast<const T*>(data);
+    }
+  };
+
+  class iterator {
+  public:
+    iterator(const void *ptr, uint32_t num_elements, uint64_t max_offset)
+        : max_offset_(max_offset) {
+      initValuePackIterator(&iter_, ptr, num_elements);
+      if (ptr && !is_valid_position()) iter_.current = nullptr;
+    }
+
+    ValueRef operator*() const {
+      return ValueRef{
+        getValuePackHeader(&iter_),
+        getValuePackData(&iter_)
+      };
+    }
+
+    iterator &operator++() {
+      nextValuePack(&iter_);
+      if (!is_valid_position()) iter_.current = nullptr;
+      return *this;
+    }
+
+    bool operator!=(const iterator &other) const {
+      return iter_.current != other.iter_.current;
+    }
+
+  private:
+    bool is_valid_position() const {
+      if (!iter_.current) return false;
+      if (iter_.index >= iter_.count) return false;
+      if (max_offset_ > 0 && iter_.offset >= max_offset_) return false;
+      return true;
+    }
+
+    ValuePackIterator iter_;
+    uint64_t max_offset_;
+  };
+
+  ValuePackRange(const void *ptr, uint32_t num_elements, uint64_t max_size = 0)
+      : ptr_(ptr), num_elements_(num_elements), max_size_(max_size) {}
+
+  iterator begin() const { return iterator(ptr_, num_elements_, max_size_); }
+  iterator end() const { return iterator(nullptr, 0, 0); }
+
+private:
+  const void *ptr_;
+  uint32_t num_elements_;
+  uint64_t max_size_;
+};
+
+/// Template helper to extract a typed value from a value pack by index.
+template <typename T>
+inline const T *getValueAs(const void *pack_ptr, uint32_t num_elements, uint32_t index) {
+  return static_cast<const T*>(getValuePackEntry(pack_ptr, num_elements, index, nullptr));
+}
+
+#endif // __cplusplus
+
+#endif // INSTRUMENTOR_RUNTIME_H
diff --git a/llvm/test/Instrumentation/Instrumentor/generate_rt.ll b/llvm/test/Instrumentation/Instrumentor/generate_rt.ll
index 9e6b444ba895c..533561b73ce39 100644
--- a/llvm/test/Instrumentation/Instrumentor/generate_rt.ll
+++ b/llvm/test/Instrumentation/Instrumentor/generate_rt.ll
@@ -1,3 +1,4 @@
 ; RUN: rm -rf %t && mkdir -p %t && cd %t
 ; RUN: opt < %s -passes=instrumentor -instrumentor-read-config-files=%S/rt_config.json -S
-; RUN: diff -b rt.c %S/default_rt
+; RUN: diff -b default_rt.c %S/default_rt.c
+; RUN: diff -b default_rt.h %S/default_rt.h
diff --git a/llvm/test/Instrumentation/Instrumentor/lit.local.cfg b/llvm/test/Instrumentation/Instrumentor/lit.local.cfg
new file mode 100644
index 0000000000000..f400568be0c61
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/lit.local.cfg
@@ -0,0 +1 @@
+config.excludes = ["default_rt.c"]
diff --git a/llvm/test/Instrumentation/Instrumentor/rt_config.json b/llvm/test/Instrumentation/Instrumentor/rt_config.json
index 2af8f11eb9ad3..a55d75fd40597 100644
--- a/llvm/test/Instrumentation/Instrumentor/rt_config.json
+++ b/llvm/test/Instrumentation/Instrumentor/rt_config.json
@@ -2,12 +2,147 @@
   "configuration": {
     "runtime_prefix": "__instrumentor_",
     "runtime_prefix.description": "The runtime API prefix.",
-    "runtime_stubs_file": "rt.c",
-    "runtime_stubs_file.description": "The file into which runtime stubs should be written."
+    "runtime_stubs_file": "default_rt.c",
+    "runtime_stubs_file.description": "The file into which runtime stubs should be written.",
+    "target_regex": "",
+    "target_regex.description": "Regular expression to be matched against the module target. Only targets that match this regex will be instrumented.",
+    "function_regex": "",
+    "function_regex.description": "Regular expression to be matched against a function name. Only functions that match this regex will be instrumented.",
+    "demangle_function_names": true,
+    "demangle_function_names.description": "Demangle functions names passed to the runtime.",
+    "host_enabled": true,
+    "host_enabled.description": "Instrument non-GPU targets",
+    "gpu_enabled": true,
+    "gpu_enabled.description": "Instrument GPU targets"
+  },
+  "module_pre": {
+    "module": {
+      "enabled": true,
+      "filter": "",
+      "filter.description": "Static property filter to exclude instrumentation.",
+      "module_name": true,
+      "module_name.description": "The module/translation unit name.",
+      "target_triple": true,
+      "target_triple.description": "The target triple.",
+      "id": true,
+      "id.description": "A unique ID associated with the given instrumentor call"
+    }
+  },
+  "module_post": {
+    "module": {
+      "enabled": true,
+      "filter": "",
+      "filter.description": "Static property filter to exclude instrumentation.",
+      "module_name": true,
+      "module_name.description": "The module/translation unit name.",
+      "target_triple": true,
+      "target_triple.description": "The target triple.",
+      "id": true,
+      "id.description": "A unique ID associated with the given instrumentor call"
+    }
+  },
+  "global_pre": {
+    "global": {
+      "enabled": true,
+      "filter": "",
+      "filter.description": "Static property filter to exclude instrumentation.",
+      "address": true,
+      "address.replace": true,
+      "address.description": "The address of the global (replaceable for definitions).",
+      "address_space": true,
+      "address_space.description": "The address space of the global.",
+      "declared_size": true,
+      "declared_size.description": "The size of the declared type of the global.",
+      "alignment": true,
+      "alignment.description": "The allocation alignment.",
+      "name": true,
+      "name.description": "The name of the global.",
+      "initial_value": true,
+      "initial_value.description": "The initial value of the global.",
+      "is_constant": true,
+      "is_constant.description": "Flag to indicate constant globals.",
+      "is_definition": true,
+      "is_definition.description": "Flag to indicate global definitions.",
+      "id": true,
+      "id.description": "A unique ID associated with the given instrumentor call"
+    }
+  },
+  "global_post": {
+    "global": {
+      "enabled": true,
+      "filter": "",
+      "filter.description": "Static property filter to exclude instrumentation.",
+      "address": true,
+      "address.description": "The address of the global (replaceable for definitions).",
+      "address_space": true,
+      "address_space.description": "The address space of the global.",
+      "declared_size": true,
+      "declared_size.description": "The size of the declared type of the global.",
+      "alignment": true,
+      "alignment.description": "The allocation alignment.",
+      "name": true,
+      "name.description": "The name of the global.",
+      "initial_value": true,
+      "initial_value.description": "The initial value of the global.",
+      "is_constant": true,
+      "is_constant.description": "Flag to indicate constant globals.",
+      "is_definition": true,
+      "is_definition.description": "Flag to indicate global definitions.",
+      "id": true,
+      "id.description": "A unique ID associated with the given instrumentor call"
+    }
+  },
+  "function_pre": {
+    "function": {
+      "enabled": true,
+      "filter": "",
+      "filter.description": "Static property filter to exclude instrumentation.",
+      "address": true,
+      "address.description": "The function address.",
+      "name": true,
+      "name.description": "The function name.",
+      "num_arguments": true,
+      "num_arguments.description": "Number of function arguments (without varargs).",
+      "arguments": true,
+      "arguments.replace": true,
+      "arguments.description": "Description of the arguments.",
+      "is_main": true,
+      "is_main.description": "Flag to indicate it is the main function.",
+      "id": true,
+      "id.description": "A unique ID associated with the given instrumentor call"
+    }
+  },
+  "function_post": {
+    "function": {
+      "enabled": true,
+      "filter": "",
+      "filter.description": "Static property filter to exclude instrumentation.",
+      "address": true,
+      "address.description": "The function address.",
+      "name": true,
+      "name.description": "The function name.",
+      "num_arguments": true,
+      "num_arguments.description": "Number of function arguments (without varargs).",
+      "arguments": true,
+      "arguments.description": "Description of the arguments.",
+      "is_main": true,
+      "is_main.description": "Flag to indicate it is the main function.",
+      "id": true,
+      "id.description": "A unique ID associated with the given instrumentor call"
+    }
   },
   "instruction_pre": {
+    "unreachable": {
+      "enabled": true,
+      "filter": "",
+      "filter.description": "Static property filter to exclude instrumentation.",
+      "id": true,
+      "id.description": "A unique ID associated with the given instrumentor call"
+    },
     "load": {
       "enabled": true,
+      "filter": "",
+      "filter.description": "Static property filter to exclude instrumentation.",
       "pointer": true,
       "pointer.replace": true,
       "pointer.description": "The accessed pointer.",
@@ -28,8 +163,31 @@
       "id": true,
       "id.description": "A unique ID associated with the given instrumentor call"
     },
+    "ptrtoint": {
+      "enabled": true,
+      "filter": "",
+      "filter.description": "Static property filter to exclude instrumentation.",
+      "pointer": true,
+      "pointer.description": "Input pointer of the ptr to int.",
+      "id": true,
+      "id.description": "A unique ID associated with the given instrumentor call"
+    },
+    "alloca": {
+      "enabled": true,
+      "filter": "",
+      "filter.description": "Static property filter to exclude instrumentation.",
+      "size": true,
+      "size.replace": true,
+      "size.description": "The allocation size.",
+      "alignment": true,
+      "alignment.description": "The allocation alignment.",
+      "id": true,
+      "id.description": "A unique ID associated with the given instrumentor call"
+    },
     "store": {
       "enabled": true,
+      "filter": "",
+      "filter.description": "Static property filter to exclude instrumentation.",
       "pointer": true,
       "pointer.replace": true,
       "pointer.description": "The accessed pointer.",
@@ -56,6 +214,8 @@
   "instruction_post": {
     "load": {
       "enabled": true,
+      "filter": "",
+      "filter.description": "Static property filter to exclude instrumentation.",
       "pointer": true,
       "pointer.description": "The accessed pointer.",
       "pointer_as": true,
@@ -78,8 +238,36 @@
       "id": true,
       "id.description": "A unique ID associated with the given instrumentor call"
     },
+    "ptrtoint": {
+      "enabled": true,
+      "filter": "",
+      "filter.description": "Static property filter to exclude instrumentation.",
+      "pointer": true,
+      "pointer.description": "Input pointer of the ptr to int.",
+      "value": true,
+      "value.replace": true,
+      "value.description": "Result of the ptr to int.",
+      "id": true,
+      "id.description": "A unique ID associated with the given instrumentor call"
+    },
+    "alloca": {
+      "enabled": true,
+      "filter": "",
+      "filter.description": "Static property filter to exclude instrumentation.",
+      "address": true,
+      "address.replace": true,
+      "address.description": "The allocated memory address.",
+      "size": true,
+      "size.description": "The allocation size.",
+      "alignment": true,
+      "alignment.description": "The allocation alignment.",
+      "id": true,
+      "id.description": "A unique ID associated with the given instrumentor call"
+    },
     "store": {
       "enabled": true,
+      "filter": "",
+      "filter.description": "Static property filter to exclude instrumentation.",
       "pointer": true,
       "pointer.description": "The accessed pointer.",
       "pointer_as": true,



More information about the llvm-commits mailing list