[compiler-rt] [llvm] [Instrumentor] Add runtime examples: [1/N] A flop counter (PR #205221)

Johannes Doerfert via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 23 18:59:24 PDT 2026


https://github.com/jdoerfert updated https://github.com/llvm/llvm-project/pull/205221

>From 822c2682b0266721e550abc2205fc669e2e3722d Mon Sep 17 00:00:00 2001
From: Johannes Doerfert <jdoerfert.llvm at gmail.com>
Date: Tue, 23 Jun 2026 16:36:41 -0700
Subject: [PATCH 1/2] [Instrumentor] Add subtype IDs to complement type IDs for
 vectors/arrays

If the type of an argument passed to the instrumentation is a vector or
array, we still want to filter on the underlying type, and the
instrumentation might also need to know. Thus, we can now pass a subtype
ID, which is -1 except if it's a vector or array, then it's the element
type ID. Structs need to be handled differently.
---
 .../llvm/Transforms/IPO/Instrumentor.h        | 18 ++++
 .../IPO/InstrumentorRuntimeHelper.h           |  4 +-
 llvm/lib/Transforms/IPO/Instrumentor.cpp      | 97 +++++++++++++++++--
 .../IPO/InstrumentorStubPrinter.cpp           |  9 +-
 .../Instrumentor/alloca_and_function.ll       |  8 +-
 .../test/Instrumentation/Instrumentor/cast.ll |  2 +-
 .../Instrumentor/cast_crash.ll                |  4 +-
 .../Instrumentor/default_config.json          | 20 ++++
 .../Instrumentation/Instrumentor/default_rt.c | 44 ++++-----
 .../Instrumentor/generate_rt.ll               |  1 +
 .../Instrumentor/module_and_globals.ll        | 20 ++--
 .../Instrumentor/numeric_subtypeid.ll         | 39 ++++++++
 .../numeric_subtypeid_config.json             | 19 ++++
 .../Instrumentor/rt_config.json               | 16 +++
 14 files changed, 249 insertions(+), 52 deletions(-)
 create mode 100644 llvm/test/Instrumentation/Instrumentor/numeric_subtypeid.ll
 create mode 100644 llvm/test/Instrumentation/Instrumentor/numeric_subtypeid_config.json

diff --git a/llvm/include/llvm/Transforms/IPO/Instrumentor.h b/llvm/include/llvm/Transforms/IPO/Instrumentor.h
index b08edfde66fbc..843cfe5e90c8c 100644
--- a/llvm/include/llvm/Transforms/IPO/Instrumentor.h
+++ b/llvm/include/llvm/Transforms/IPO/Instrumentor.h
@@ -65,6 +65,7 @@ struct IRTArg {
     POTENTIALLY_INDIRECT = 1 << 3,
     INDIRECT_HAS_SIZE = 1 << 4,
     VALUE_PACK = 1 << 5,
+    TYPEID = 1 << 6,
     LAST,
   };
 
@@ -909,6 +910,7 @@ struct StoreIO : public InstructionIO<Instruction::Store> {
     PassStoredValueSize,
     PassAlignment,
     PassValueTypeId,
+    PassValueSubTypeId,
     PassAtomicityOrdering,
     PassSyncScopeId,
     PassIsVolatile,
@@ -958,6 +960,9 @@ struct StoreIO : public InstructionIO<Instruction::Store> {
   LLVM_ABI static Value *getValueTypeId(Value &V, Type &Ty,
                                         InstrumentationConfig &IConf,
                                         InstrumentorIRBuilderTy &IIRB);
+  LLVM_ABI static Value *getValueSubTypeId(Value &V, Type &Ty,
+                                           InstrumentationConfig &IConf,
+                                           InstrumentorIRBuilderTy &IIRB);
   LLVM_ABI static Value *getAtomicityOrdering(Value &V, Type &Ty,
                                               InstrumentationConfig &IConf,
                                               InstrumentorIRBuilderTy &IIRB);
@@ -1002,6 +1007,7 @@ struct LoadIO : public InstructionIO<Instruction::Load> {
     PassValueSize,
     PassAlignment,
     PassValueTypeId,
+    PassValueSubTypeId,
     PassAtomicityOrdering,
     PassSyncScopeId,
     PassIsVolatile,
@@ -1051,6 +1057,9 @@ struct LoadIO : public InstructionIO<Instruction::Load> {
   LLVM_ABI static Value *getValueTypeId(Value &V, Type &Ty,
                                         InstrumentationConfig &IConf,
                                         InstrumentorIRBuilderTy &IIRB);
+  LLVM_ABI static Value *getValueSubTypeId(Value &V, Type &Ty,
+                                           InstrumentationConfig &IConf,
+                                           InstrumentorIRBuilderTy &IIRB);
   LLVM_ABI static Value *getAtomicityOrdering(Value &V, Type &Ty,
                                               InstrumentationConfig &IConf,
                                               InstrumentorIRBuilderTy &IIRB);
@@ -1089,10 +1098,12 @@ struct CastIO final
   enum ConfigKind {
     PassInput,
     PassInputTypeId,
+    PassInputSubTypeId,
     PassInputSize,
     PassResult,
     ReplaceResult,
     PassResultTypeId,
+    PassResultSubTypeId,
     PassResultSize,
     PassOpcode,
     PassId,
@@ -1114,12 +1125,18 @@ struct CastIO final
   LLVM_ABI static Value *getInputTypeId(Value &V, Type &Ty,
                                         InstrumentationConfig &IConf,
                                         InstrumentorIRBuilderTy &IIRB);
+  LLVM_ABI static Value *getInputSubTypeId(Value &V, Type &Ty,
+                                           InstrumentationConfig &IConf,
+                                           InstrumentorIRBuilderTy &IIRB);
   LLVM_ABI static Value *getInputSize(Value &V, Type &Ty,
                                       InstrumentationConfig &IConf,
                                       InstrumentorIRBuilderTy &IIRB);
   LLVM_ABI static Value *getResultTypeId(Value &V, Type &Ty,
                                          InstrumentationConfig &IConf,
                                          InstrumentorIRBuilderTy &IIRB);
+  LLVM_ABI static Value *getResultSubTypeId(Value &V, Type &Ty,
+                                            InstrumentationConfig &IConf,
+                                            InstrumentorIRBuilderTy &IIRB);
   LLVM_ABI static Value *getResultSize(Value &V, Type &Ty,
                                        InstrumentationConfig &IConf,
                                        InstrumentorIRBuilderTy &IIRB);
@@ -1151,6 +1168,7 @@ struct NumericIO final
 
   enum ConfigKind {
     PassTypeId,
+    PassSubTypeId,
     PassSize,
     PassOpcode,
     PassResult,
diff --git a/llvm/include/llvm/Transforms/IPO/InstrumentorRuntimeHelper.h b/llvm/include/llvm/Transforms/IPO/InstrumentorRuntimeHelper.h
index 641096f2c0e22..5ec3b756b43bd 100644
--- a/llvm/include/llvm/Transforms/IPO/InstrumentorRuntimeHelper.h
+++ b/llvm/include/llvm/Transforms/IPO/InstrumentorRuntimeHelper.h
@@ -151,8 +151,10 @@ enum LLVMTypeID {
 };
 
 /// Get the string name of an LLVM Type ID.
-static inline const char *getLLVMTypeIDName(uint32_t type_id) {
+static inline const char *getLLVMTypeIDName(int32_t type_id) {
   switch (type_id) {
+  case -1:
+    return "none";
   case HalfTyID:
     return "half";
   case BFloatTyID:
diff --git a/llvm/lib/Transforms/IPO/Instrumentor.cpp b/llvm/lib/Transforms/IPO/Instrumentor.cpp
index f603306b36254..c93c3e061c2d0 100644
--- a/llvm/lib/Transforms/IPO/Instrumentor.cpp
+++ b/llvm/lib/Transforms/IPO/Instrumentor.cpp
@@ -154,6 +154,19 @@ Constant *getCI(Type *IT, Ty Val, bool IsSigned = false) {
   return ConstantInt::get(IT, Val, IsSigned);
 }
 
+Constant *getSubTypeID(Type &OpTy, Type &ReqTy) {
+  switch (OpTy.getTypeID()) {
+  case Type::TypeID::ArrayTyID:
+  case Type::TypeID::FixedVectorTyID:
+  case Type::TypeID::ScalableVectorTyID:
+    return getCI(&ReqTy, OpTy.getContainedType(0)->getTypeID());
+  default:
+    break;
+  }
+
+  return getCI(&ReqTy, -1, /*IsSigned=*/true);
+}
+
 /// The core of the instrumentor pass, which instruments the module as the
 /// instrumentation configuration mandates.
 class InstrumentorImpl final {
@@ -1011,6 +1024,11 @@ static Value *getTypeId(Value &V, Type &Ty, InstrumentationConfig &IConf,
   return getCI(&Ty, V.getType()->getTypeID());
 }
 
+static Value *getSubTypeId(Value &V, Type &Ty, InstrumentationConfig &IConf,
+                           InstrumentorIRBuilderTy &IIRB) {
+  return getSubTypeID(*V.getType(), Ty);
+}
+
 static Value *getSize(Value &V, Type &Ty, InstrumentationConfig &IConf,
                       InstrumentorIRBuilderTy &IIRB) {
   auto &I = cast<Instruction>(V);
@@ -1139,9 +1157,15 @@ void StoreIO::init(InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB,
   }
   if (Config.has(PassValueTypeId)) {
     IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "value_type_id",
-                             "The type id of the stored value.", IRTArg::NONE,
+                             "The type id of the stored value.", IRTArg::TYPEID,
                              getValueTypeId));
   }
+  if (Config.has(PassValueSubTypeId)) {
+    IRTArgs.push_back(IRTArg(
+        IIRB.Int32Ty, "value_sub_type_id",
+        "The type id of the stored value (for arrays and vectors, or -1).",
+        IRTArg::TYPEID, getValueSubTypeId));
+  }
   if (Config.has(PassAtomicityOrdering)) {
     IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "atomicity_ordering",
                              "The atomicity ordering of the store.",
@@ -1213,6 +1237,13 @@ Value *StoreIO::getValueTypeId(Value &V, Type &Ty, InstrumentationConfig &IConf,
   return getCI(&Ty, SI.getValueOperand()->getType()->getTypeID());
 }
 
+Value *StoreIO::getValueSubTypeId(Value &V, Type &Ty,
+                                  InstrumentationConfig &IConf,
+                                  InstrumentorIRBuilderTy &IIRB) {
+  auto &SI = cast<StoreInst>(V);
+  return getSubTypeID(*SI.getValueOperand()->getType(), Ty);
+}
+
 Value *StoreIO::getAtomicityOrdering(Value &V, Type &Ty,
                                      InstrumentationConfig &IConf,
                                      InstrumentorIRBuilderTy &IIRB) {
@@ -1276,9 +1307,15 @@ void LoadIO::init(InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB,
   }
   if (Config.has(PassValueTypeId)) {
     IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "value_type_id",
-                             "The type id of the loaded value.", IRTArg::NONE,
+                             "The type id of the loaded value.", IRTArg::TYPEID,
                              getValueTypeId));
   }
+  if (Config.has(PassValueSubTypeId)) {
+    IRTArgs.push_back(IRTArg(
+        IIRB.Int32Ty, "value_sub_type_id",
+        "The sub type id of the loaded value (for arrays and vectors, or -1).",
+        IRTArg::TYPEID, getValueSubTypeId));
+  }
   if (Config.has(PassAtomicityOrdering)) {
     IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "atomicity_ordering",
                              "The atomicity ordering of the load.",
@@ -1349,6 +1386,13 @@ Value *LoadIO::getValueTypeId(Value &V, Type &Ty, InstrumentationConfig &IConf,
   return getCI(&Ty, LI.getType()->getTypeID());
 }
 
+Value *LoadIO::getValueSubTypeId(Value &V, Type &Ty,
+                                 InstrumentationConfig &IConf,
+                                 InstrumentorIRBuilderTy &IIRB) {
+  auto &LI = cast<LoadInst>(V);
+  return getSubTypeID(*LI.getType(), Ty);
+}
+
 Value *LoadIO::getAtomicityOrdering(Value &V, Type &Ty,
                                     InstrumentationConfig &IConf,
                                     InstrumentorIRBuilderTy &IIRB) {
@@ -1625,12 +1669,21 @@ void CastIO::init(InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB,
     Config = *UserConfig;
   bool IsPRE = getLocationKind() == InstrumentationLocation::INSTRUCTION_PRE;
   if (Config.has(PassInput))
-    IRTArgs.push_back(IRTArg(IIRB.Int64Ty, "input", "Input value of the cast.",
-                             IRTArg::POTENTIALLY_INDIRECT, getInput));
+    IRTArgs.push_back(
+        IRTArg(IIRB.Int64Ty, "input", "Input value of the cast.",
+               IRTArg::POTENTIALLY_INDIRECT |
+                   (Config.has(PassResultSize) ? IRTArg::INDIRECT_HAS_SIZE
+                                               : IRTArg::NONE),
+               getInput));
   if (Config.has(PassInputTypeId))
     IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "input_type_id",
-                             "The type id of the input value.", IRTArg::NONE,
+                             "The type id of the input value.", IRTArg::TYPEID,
                              getInputTypeId));
+  if (Config.has(PassInputSubTypeId))
+    IRTArgs.push_back(IRTArg(
+        IIRB.Int32Ty, "input_sub_type_id",
+        "The sub type id of the input value (for arrays and vectors, or -1).",
+        IRTArg::TYPEID, getInputSubTypeId));
   if (Config.has(PassInputSize))
     IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "input_size",
                              "The size of the input value.", IRTArg::NONE,
@@ -1638,12 +1691,19 @@ void CastIO::init(InstrumentationConfig &IConf, InstrumentorIRBuilderTy &IIRB,
   if (!IsPRE && Config.has(PassResult))
     IRTArgs.push_back(
         IRTArg(IIRB.Int64Ty, "result", "Result of the cast.",
-               IRTArg::REPLACABLE | IRTArg::POTENTIALLY_INDIRECT, getValue,
-               Config.has(ReplaceResult) ? replaceValue : nullptr));
+               (IRTArg::REPLACABLE | IRTArg::POTENTIALLY_INDIRECT) |
+                   (Config.has(PassResultSize) ? IRTArg::INDIRECT_HAS_SIZE
+                                               : IRTArg::NONE),
+               getValue, Config.has(ReplaceResult) ? replaceValue : nullptr));
   if (Config.has(PassResultTypeId))
     IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "result_type_id",
-                             "The type id of the result value.", IRTArg::NONE,
+                             "The type id of the result value.", IRTArg::TYPEID,
                              getResultTypeId));
+  if (Config.has(PassResultSubTypeId))
+    IRTArgs.push_back(IRTArg(
+        IIRB.Int32Ty, "result_sub_type_id",
+        "The sub type id of the result value (for arrays and vectors, or -1).",
+        IRTArg::TYPEID, getResultSubTypeId));
   if (Config.has(PassResultSize))
     IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "result_size",
                              "The size of the result value.", IRTArg::NONE,
@@ -1669,6 +1729,13 @@ Value *CastIO::getInputTypeId(Value &V, Type &Ty, InstrumentationConfig &IConf,
   return getCI(&Ty, CI.getSrcTy()->getTypeID());
 }
 
+Value *CastIO::getInputSubTypeId(Value &V, Type &Ty,
+                                 InstrumentationConfig &IConf,
+                                 InstrumentorIRBuilderTy &IIRB) {
+  auto &CI = cast<CastInst>(V);
+  return getSubTypeID(*CI.getSrcTy(), Ty);
+}
+
 Value *CastIO::getInputSize(Value &V, Type &Ty, InstrumentationConfig &IConf,
                             InstrumentorIRBuilderTy &IIRB) {
   auto &CI = cast<CastInst>(V);
@@ -1682,6 +1749,13 @@ Value *CastIO::getResultTypeId(Value &V, Type &Ty, InstrumentationConfig &IConf,
   return getCI(&Ty, CI.getDestTy()->getTypeID());
 }
 
+Value *CastIO::getResultSubTypeId(Value &V, Type &Ty,
+                                  InstrumentationConfig &IConf,
+                                  InstrumentorIRBuilderTy &IIRB) {
+  auto &CI = cast<CastInst>(V);
+  return getSubTypeID(*CI.getDestTy(), Ty);
+}
+
 Value *CastIO::getResultSize(Value &V, Type &Ty, InstrumentationConfig &IConf,
                              InstrumentorIRBuilderTy &IIRB) {
   auto &CI = cast<CastInst>(V);
@@ -1770,8 +1844,13 @@ void NumericIO::init(InstrumentationConfig &IConf,
       (Config.has(PassSize) ? IRTArg::INDIRECT_HAS_SIZE : IRTArg::NONE);
   if (Config.has(PassTypeId))
     IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "type_id",
-                             "The operation's type id.", IRTArg::NONE,
+                             "The operation's type id.", IRTArg::TYPEID,
                              getTypeId));
+  if (Config.has(PassSubTypeId))
+    IRTArgs.push_back(
+        IRTArg(IIRB.Int32Ty, "sub_type_id",
+               "The operation's sub type id (for arrays and vectors, or -1).",
+               IRTArg::TYPEID, getSubTypeId));
   if (Config.has(PassSize))
     IRTArgs.push_back(IRTArg(IIRB.Int32Ty, "size", "The operation's type size.",
                              IRTArg::NONE, getSize));
diff --git a/llvm/lib/Transforms/IPO/InstrumentorStubPrinter.cpp b/llvm/lib/Transforms/IPO/InstrumentorStubPrinter.cpp
index d892ad5de1f2e..a2c5c7660e61f 100644
--- a/llvm/lib/Transforms/IPO/InstrumentorStubPrinter.cpp
+++ b/llvm/lib/Transforms/IPO/InstrumentorStubPrinter.cpp
@@ -53,6 +53,8 @@ static std::pair<std::string, std::string> getAsCType(Type *Ty,
 /// type \p Ty. The flags in \p Flags describe the properties of the argument.
 /// See IRTArg::IRArgFlagTy.
 static std::string getPrintfFormatString(Type *Ty, unsigned Flags) {
+  if (Flags & IRTArg::TYPEID)
+    return "%s";
   if (Ty->isIntegerTy()) {
     if (Ty->getIntegerBitWidth() > 32) {
       assert(Ty->getIntegerBitWidth() == 64);
@@ -90,7 +92,12 @@ std::pair<std::string, std::string> IRTCallDescription::createCBodies() const {
     if (!First)
       AddToFormats(", ");
     First = false;
-    AddToArgs(", " + IRArg.Name);
+
+    if (!(IRArg.Flags & IRTArg::TYPEID)) {
+      AddToArgs(", " + IRArg.Name);
+    } else {
+      AddToArgs(", getLLVMTypeIDName(" + IRArg.Name + ")");
+    }
     AddToFormats(IRArg.Name + ": ");
     if (NumReplaceableArgs == 1 && (IRArg.Flags & IRTArg::REPLACABLE)) {
       DirectReturnValue = IRArg.Name;
diff --git a/llvm/test/Instrumentation/Instrumentor/alloca_and_function.ll b/llvm/test/Instrumentation/Instrumentor/alloca_and_function.ll
index f98dee743c09d..a2afecda64ebf 100644
--- a/llvm/test/Instrumentation/Instrumentor/alloca_and_function.ll
+++ b/llvm/test/Instrumentation/Instrumentor/alloca_and_function.ll
@@ -9,14 +9,12 @@ target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16
 
 declare void @use(ptr)
 
-;.
 ; CHECK: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1000, ptr @__instrumentor_ctor, ptr null }]
 ; CHECK: @__instrumentor_.str = private unnamed_addr constant [8 x i8] c"<stdin>\00", align 1
 ; CHECK: @__instrumentor_.str.1 = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
 ; CHECK: @llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1000, ptr @__instrumentor_dtor, ptr null }]
 ; CHECK: @__instrumentor_.str.2 = private unnamed_addr constant [4 x i8] c"foo\00", align 1
 ; CHECK: @__instrumentor_value_pack = internal global <{ i32, i32, [6 x i8], i16, i32, i32, [4 x i8], float }> <{ i32 2, i32 12, [6 x i8] zeroinitializer, i16 0, i32 4, i32 2, [4 x i8] zeroinitializer, float 0.000000e+00 }>
-;.
 define float @foo(i16 %a, float %b) {
 ; CHECK-LABEL: define float @foo(
 ; CHECK-SAME: i16 [[A:%.*]], float [[B:%.*]]) {
@@ -37,9 +35,9 @@ define float @foo(i16 %a, float %b) {
 ; CHECK-NEXT:    [[TMP9:%.*]] = call ptr @__instrumentor_post_alloca(ptr [[TMP8]], i64 2, i64 16, i32 -3) #[[ATTR1]]
 ; CHECK-NEXT:    [[TMP15:%.*]] = call ptr @__instrumentor_post_base_pointer_info(ptr [[TMP9]], i32 2, i32 -4) #[[ATTR1]]
 ; CHECK-NEXT:    [[TMP10:%.*]] = zext i16 [[TMP4]] to i64
-; CHECK-NEXT:    [[TMP11:%.*]] = call ptr @__instrumentor_pre_store(ptr [[TMP9]], i32 0, ptr [[TMP15]], i64 [[TMP10]], i64 2, i64 2, i32 12, i32 0, i8 1, i8 0, i32 4) #[[ATTR1]]
+; CHECK-NEXT:    [[TMP11:%.*]] = call ptr @__instrumentor_pre_store(ptr [[TMP9]], i32 0, ptr [[TMP15]], i64 [[TMP10]], i64 2, i64 2, i32 12, i32 -1, i32 0, i8 1, i8 0, i32 4) #[[ATTR1]]
 ; CHECK-NEXT:    store i16 [[TMP4]], ptr [[TMP11]], align 2
-; CHECK-NEXT:    call void @__instrumentor_post_store(ptr [[TMP9]], i32 0, ptr [[TMP15]], i64 [[TMP10]], i64 2, i64 2, i32 12, i32 0, i8 1, i8 0, i32 -4) #[[ATTR1]]
+; CHECK-NEXT:    call void @__instrumentor_post_store(ptr [[TMP9]], i32 0, ptr [[TMP15]], i64 [[TMP10]], i64 2, i64 2, i32 12, i32 -1, i32 0, i8 1, i8 0, i32 -4) #[[ATTR1]]
 ; CHECK-NEXT:    call void @use(ptr [[TMP9]])
 ; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr @__instrumentor_value_pack, i64 32, i1 false)
 ; CHECK-NEXT:    [[TMP12:%.*]] = getelementptr inbounds nuw <{ i32, i32, [6 x i8], i16, i32, i32, [4 x i8], float }>, ptr [[TMP0]], i32 0, i32 3
@@ -55,7 +53,5 @@ entry:
   call void @use(ptr %0)
   ret float %b
 }
-;.
 ; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 ; CHECK: attributes #[[ATTR1]] = { willreturn }
-;.
diff --git a/llvm/test/Instrumentation/Instrumentor/cast.ll b/llvm/test/Instrumentation/Instrumentor/cast.ll
index a8b1c96ec3878..77d9e0452784d 100644
--- a/llvm/test/Instrumentation/Instrumentor/cast.ll
+++ b/llvm/test/Instrumentation/Instrumentor/cast.ll
@@ -282,7 +282,7 @@ define i128 @test_ext(i32 %p1) {
 ; CHECK-NEXT:    [[I1:%.*]] = zext i32 [[P1]] to i128
 ; CHECK-NEXT:    store i64 [[TMP3]], ptr [[TMP2]], align 4
 ; CHECK-NEXT:    store i128 [[I1]], ptr [[TMP1]], align 4
-; CHECK-NEXT:    call void @__instrumentor_post_cast_ind(ptr [[TMP2]], i32 8, i32 12, i32 4, ptr [[TMP1]], i32 16, i32 12, i32 16, i32 40) #[[ATTR0]]
+; CHECK-NEXT:    call void @__instrumentor_post_cast_ind(ptr [[TMP2]], i32 12, i32 4, ptr [[TMP1]], i32 12, i32 16, i32 40) #[[ATTR0]]
 ; CHECK-NEXT:    [[TMP4:%.*]] = load i128, ptr [[TMP1]], align 4
 ; CHECK-NEXT:    ret i128 [[TMP4]]
 ;
diff --git a/llvm/test/Instrumentation/Instrumentor/cast_crash.ll b/llvm/test/Instrumentation/Instrumentor/cast_crash.ll
index ff4bde8b8f852..f9bf05c9c0cca 100644
--- a/llvm/test/Instrumentation/Instrumentor/cast_crash.ll
+++ b/llvm/test/Instrumentation/Instrumentor/cast_crash.ll
@@ -14,11 +14,11 @@ define i128 @test_ext(i32 %p1) {
 ; CHECK-NEXT:    [[TMP4:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i32 12
 ; CHECK-NEXT:    [[TMP5:%.*]] = load i32, ptr [[TMP4]], align 4
 ; CHECK-NEXT:    [[TMP6:%.*]] = zext i32 [[TMP5]] to i64
-; CHECK-NEXT:    call void @__instrumentor_pre_cast(i64 [[TMP6]], i32 12, i32 4, i32 12, i32 16, i32 40, i32 3) #[[ATTR1]]
+; CHECK-NEXT:    call void @__instrumentor_pre_cast(i64 [[TMP6]], i32 12, i32 -1, i32 4, i32 12, i32 -1, i32 16, i32 40, i32 3) #[[ATTR1]]
 ; CHECK-NEXT:    [[I1:%.*]] = zext i32 [[TMP5]] to i128
 ; CHECK-NEXT:    store i64 [[TMP6]], ptr [[TMP2]], align 4
 ; CHECK-NEXT:    store i128 [[I1]], ptr [[TMP1]], align 4
-; CHECK-NEXT:    call void @__instrumentor_post_cast_ind(ptr [[TMP2]], i32 8, i32 12, i32 4, ptr [[TMP1]], i32 16, i32 12, i32 16, i32 40, i32 -3) #[[ATTR1]]
+; CHECK-NEXT:    call void @__instrumentor_post_cast_ind(ptr [[TMP2]], i32 12, i32 -1, i32 4, ptr [[TMP1]], i32 12, i32 -1, i32 16, i32 40, i32 -3) #[[ATTR1]]
 ; CHECK-NEXT:    [[TMP7:%.*]] = load i128, ptr [[TMP1]], align 4
 ; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP1]], ptr @__instrumentor_value_pack, i64 16, i1 false)
 ; CHECK-NEXT:    [[TMP8:%.*]] = getelementptr inbounds nuw <{ i32, i32, [4 x i8], i32 }>, ptr [[TMP1]], i32 0, i32 3
diff --git a/llvm/test/Instrumentation/Instrumentor/default_config.json b/llvm/test/Instrumentation/Instrumentor/default_config.json
index 08bde4a265450..12c95b9407bcf 100644
--- a/llvm/test/Instrumentation/Instrumentor/default_config.json
+++ b/llvm/test/Instrumentation/Instrumentor/default_config.json
@@ -168,6 +168,8 @@
       "alignment.description": "The known access alignment.",
       "value_type_id": true,
       "value_type_id.description": "The type id of the loaded value.",
+      "value_sub_type_id": true,
+      "value_sub_type_id.description": "The sub type id of the loaded value (for arrays and vectors, or -1).",
       "atomicity_ordering": true,
       "atomicity_ordering.description": "The atomicity ordering of the load.",
       "sync_scope_id": true,
@@ -196,6 +198,8 @@
       "alignment.description": "The known access alignment.",
       "value_type_id": true,
       "value_type_id.description": "The type id of the stored value.",
+      "value_sub_type_id": true,
+      "value_sub_type_id.description": "The type id of the stored value (for arrays and vectors, or -1).",
       "atomicity_ordering": true,
       "atomicity_ordering.description": "The atomicity ordering of the store.",
       "sync_scope_id": true,
@@ -213,10 +217,14 @@
       "input.description": "Input value of the cast.",
       "input_type_id": true,
       "input_type_id.description": "The type id of the input value.",
+      "input_sub_type_id": true,
+      "input_sub_type_id.description": "The sub type id of the input value (for arrays and vectors, or -1).",
       "input_size": true,
       "input_size.description": "The size of the input value.",
       "result_type_id": true,
       "result_type_id.description": "The type id of the result value.",
+      "result_sub_type_id": true,
+      "result_sub_type_id.description": "The sub type id of the result value (for arrays and vectors, or -1).",
       "result_size": true,
       "result_size.description": "The size of the result value.",
       "opcode": true,
@@ -230,6 +238,8 @@
       "filter.description": "Static property filter to exclude instrumentation.",
       "type_id": true,
       "type_id.description": "The operation's type id.",
+      "sub_type_id": true,
+      "sub_type_id.description": "The operation's sub type id (for arrays and vectors, or -1).",
       "size": true,
       "size.description": "The operation's type size.",
       "opcode": true,
@@ -278,6 +288,8 @@
       "alignment.description": "The known access alignment.",
       "value_type_id": true,
       "value_type_id.description": "The type id of the loaded value.",
+      "value_sub_type_id": true,
+      "value_sub_type_id.description": "The sub type id of the loaded value (for arrays and vectors, or -1).",
       "atomicity_ordering": true,
       "atomicity_ordering.description": "The atomicity ordering of the load.",
       "sync_scope_id": true,
@@ -305,6 +317,8 @@
       "alignment.description": "The known access alignment.",
       "value_type_id": true,
       "value_type_id.description": "The type id of the stored value.",
+      "value_sub_type_id": true,
+      "value_sub_type_id.description": "The type id of the stored value (for arrays and vectors, or -1).",
       "atomicity_ordering": true,
       "atomicity_ordering.description": "The atomicity ordering of the store.",
       "sync_scope_id": true,
@@ -322,6 +336,8 @@
       "input.description": "Input value of the cast.",
       "input_type_id": true,
       "input_type_id.description": "The type id of the input value.",
+      "input_sub_type_id": true,
+      "input_sub_type_id.description": "The sub type id of the input value (for arrays and vectors, or -1).",
       "input_size": true,
       "input_size.description": "The size of the input value.",
       "result": true,
@@ -329,6 +345,8 @@
       "result.description": "Result of the cast.",
       "result_type_id": true,
       "result_type_id.description": "The type id of the result value.",
+      "result_sub_type_id": true,
+      "result_sub_type_id.description": "The sub type id of the result value (for arrays and vectors, or -1).",
       "result_size": true,
       "result_size.description": "The size of the result value.",
       "opcode": true,
@@ -342,6 +360,8 @@
       "filter.description": "Static property filter to exclude instrumentation.",
       "type_id": true,
       "type_id.description": "The operation's type id.",
+      "sub_type_id": true,
+      "sub_type_id.description": "The operation's sub type id (for arrays and vectors, or -1).",
       "size": true,
       "size.description": "The operation's type size.",
       "opcode": true,
diff --git a/llvm/test/Instrumentation/Instrumentor/default_rt.c b/llvm/test/Instrumentation/Instrumentor/default_rt.c
index 9ecb188605e1e..bce5fa9c63a1e 100644
--- a/llvm/test/Instrumentation/Instrumentor/default_rt.c
+++ b/llvm/test/Instrumentation/Instrumentor/default_rt.c
@@ -82,27 +82,27 @@ void __instrumentor_pre_unreachable(int32_t id) {
   printf("unreachable pre -- id: %" PRId32 "\n", id);
 }
 
-void *__instrumentor_pre_load(void *pointer, int32_t pointer_as, void *base_pointer_info, 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 ", base_pointer_info: %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, base_pointer_info, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+void *__instrumentor_pre_load(void *pointer, int32_t pointer_as, void *base_pointer_info, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t value_sub_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 ", base_pointer_info: %p, value_size: %" PRId64 ", alignment: %" PRId64 ", value_type_id: %s, value_sub_type_id: %s, atomicity_ordering: %" PRId32 ", sync_scope_id: %" PRId32 ", is_volatile: %" PRId32 ", id: %" PRId32 "\n", pointer, pointer_as, base_pointer_info, value_size, alignment, getLLVMTypeIDName(value_type_id), getLLVMTypeIDName(value_sub_type_id), atomicity_ordering, sync_scope_id, is_volatile, id);
   return pointer;
 }
 
-void *__instrumentor_pre_store(void *pointer, int32_t pointer_as, void *base_pointer_info, 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 ", base_pointer_info: %p, 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, base_pointer_info, value, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+void *__instrumentor_pre_store(void *pointer, int32_t pointer_as, void *base_pointer_info, int64_t value, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t value_sub_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 ", base_pointer_info: %p, value: %" PRId64 ", value_size: %" PRId64 ", alignment: %" PRId64 ", value_type_id: %s, value_sub_type_id: %s, atomicity_ordering: %" PRId32 ", sync_scope_id: %" PRId32 ", is_volatile: %" PRId32 ", id: %" PRId32 "\n", pointer, pointer_as, base_pointer_info, value, value_size, alignment, getLLVMTypeIDName(value_type_id), getLLVMTypeIDName(value_sub_type_id), atomicity_ordering, sync_scope_id, is_volatile, id);
   return pointer;
 }
 
-void *__instrumentor_pre_store_ind(void *pointer, int32_t pointer_as, void *base_pointer_info, 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 ", base_pointer_info: %p, 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, base_pointer_info, value_ptr, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+void *__instrumentor_pre_store_ind(void *pointer, int32_t pointer_as, void *base_pointer_info, int64_t *value_ptr, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t value_sub_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 ", base_pointer_info: %p, value: %p, value_size: %" PRId64 ", alignment: %" PRId64 ", value_type_id: %s, value_sub_type_id: %s, atomicity_ordering: %" PRId32 ", sync_scope_id: %" PRId32 ", is_volatile: %" PRId32 ", id: %" PRId32 "\n", pointer, pointer_as, base_pointer_info, value_ptr, value_size, alignment, getLLVMTypeIDName(value_type_id), getLLVMTypeIDName(value_sub_type_id), atomicity_ordering, sync_scope_id, is_volatile, id);
   return pointer;
 }
 
-void __instrumentor_pre_cast(int64_t input, int32_t input_type_id, int32_t input_size, int32_t result_type_id, int32_t result_size, int32_t opcode, int32_t id) {
-  printf("cast pre -- input: %" PRId64 ", input_type_id: %" PRId32 ", input_size: %" PRId32 ", result_type_id: %" PRId32 ", result_size: %" PRId32 ", opcode: %" PRId32 ", id: %" PRId32 "\n", input, input_type_id, input_size, result_type_id, result_size, opcode, id);
+void __instrumentor_pre_cast(int64_t input, int32_t input_type_id, int32_t input_sub_type_id, int32_t input_size, int32_t result_type_id, int32_t result_sub_type_id, int32_t result_size, int32_t opcode, int32_t id) {
+  printf("cast pre -- input: %" PRId64 ", input_type_id: %s, input_sub_type_id: %s, input_size: %" PRId32 ", result_type_id: %s, result_sub_type_id: %s, result_size: %" PRId32 ", opcode: %" PRId32 ", id: %" PRId32 "\n", input, getLLVMTypeIDName(input_type_id), getLLVMTypeIDName(input_sub_type_id), input_size, getLLVMTypeIDName(result_type_id), getLLVMTypeIDName(result_sub_type_id), result_size, opcode, id);
 }
 
-void __instrumentor_pre_cast_ind(int64_t *input_ptr, int32_t input_size, int32_t input_type_id, int32_t input_size, int32_t result_type_id, int32_t result_size, int32_t opcode, int32_t id) {
-  printf("cast pre -- input: %p, input_size: %" PRId32 ", input_type_id: %" PRId32 ", input_size: %" PRId32 ", result_type_id: %" PRId32 ", result_size: %" PRId32 ", opcode: %" PRId32 ", id: %" PRId32 "\n", input_ptr, input_size, input_type_id, input_size, result_type_id, result_size, opcode, id);
+void __instrumentor_pre_cast_ind(int64_t *input_ptr, int32_t input_type_id, int32_t input_sub_type_id, int32_t input_size, int32_t result_type_id, int32_t result_sub_type_id, int32_t result_size, int32_t opcode, int32_t id) {
+  printf("cast pre -- input: %p, input_type_id: %s, input_sub_type_id: %s, input_size: %" PRId32 ", result_type_id: %s, result_sub_type_id: %s, result_size: %" PRId32 ", opcode: %" PRId32 ", id: %" PRId32 "\n", input_ptr, getLLVMTypeIDName(input_type_id), getLLVMTypeIDName(input_sub_type_id), input_size, getLLVMTypeIDName(result_type_id), getLLVMTypeIDName(result_sub_type_id), result_size, opcode, id);
 }
 
 void *__instrumentor_post_alloca(void *address, int64_t size, int64_t alignment, int32_t id) {
@@ -110,30 +110,30 @@ void *__instrumentor_post_alloca(void *address, int64_t size, int64_t alignment,
   return address;
 }
 
-int64_t __instrumentor_post_load(void *pointer, int32_t pointer_as, void *base_pointer_info, 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 ", base_pointer_info: %p, 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, base_pointer_info, value, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+int64_t __instrumentor_post_load(void *pointer, int32_t pointer_as, void *base_pointer_info, int64_t value, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t value_sub_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 ", base_pointer_info: %p, value: %" PRId64 ", value_size: %" PRId64 ", alignment: %" PRId64 ", value_type_id: %s, value_sub_type_id: %s, atomicity_ordering: %" PRId32 ", sync_scope_id: %" PRId32 ", is_volatile: %" PRId32 ", id: %" PRId32 "\n", pointer, pointer_as, base_pointer_info, value, value_size, alignment, getLLVMTypeIDName(value_type_id), getLLVMTypeIDName(value_sub_type_id), atomicity_ordering, sync_scope_id, is_volatile, id);
   return value;
 }
 
-void __instrumentor_post_load_ind(void *pointer, int32_t pointer_as, void *base_pointer_info, 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 ", base_pointer_info: %p, 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, base_pointer_info, value_ptr, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+void __instrumentor_post_load_ind(void *pointer, int32_t pointer_as, void *base_pointer_info, int64_t *value_ptr, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t value_sub_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 ", base_pointer_info: %p, value: %p, value_size: %" PRId64 ", alignment: %" PRId64 ", value_type_id: %s, value_sub_type_id: %s, atomicity_ordering: %" PRId32 ", sync_scope_id: %" PRId32 ", is_volatile: %" PRId32 ", id: %" PRId32 "\n", pointer, pointer_as, base_pointer_info, value_ptr, value_size, alignment, getLLVMTypeIDName(value_type_id), getLLVMTypeIDName(value_sub_type_id), atomicity_ordering, sync_scope_id, is_volatile, id);
 }
 
-void __instrumentor_post_store(void *pointer, int32_t pointer_as, void *base_pointer_info, 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 ", base_pointer_info: %p, 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, base_pointer_info, value, value_size, alignment, value_type_id, atomicity_ordering, sync_scope_id, is_volatile, id);
+void __instrumentor_post_store(void *pointer, int32_t pointer_as, void *base_pointer_info, int64_t value, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t value_sub_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 ", base_pointer_info: %p, value: %" PRId64 ", value_size: %" PRId64 ", alignment: %" PRId64 ", value_type_id: %s, value_sub_type_id: %s, atomicity_ordering: %" PRId32 ", sync_scope_id: %" PRId32 ", is_volatile: %" PRId32 ", id: %" PRId32 "\n", pointer, pointer_as, base_pointer_info, value, value_size, alignment, getLLVMTypeIDName(value_type_id), getLLVMTypeIDName(value_sub_type_id), atomicity_ordering, sync_scope_id, is_volatile, id);
 }
 
-void __instrumentor_post_store_ind(void *pointer, int32_t pointer_as, void *base_pointer_info, 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 ", base_pointer_info: %p, 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, base_pointer_info, value_ptr, 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, void *base_pointer_info, int64_t *value_ptr, int64_t value_size, int64_t alignment, int32_t value_type_id, int32_t value_sub_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 ", base_pointer_info: %p, value: %p, value_size: %" PRId64 ", alignment: %" PRId64 ", value_type_id: %s, value_sub_type_id: %s, atomicity_ordering: %" PRId32 ", sync_scope_id: %" PRId32 ", is_volatile: %" PRId32 ", id: %" PRId32 "\n", pointer, pointer_as, base_pointer_info, value_ptr, value_size, alignment, getLLVMTypeIDName(value_type_id), getLLVMTypeIDName(value_sub_type_id), atomicity_ordering, sync_scope_id, is_volatile, id);
 }
 
-int64_t __instrumentor_post_cast(int64_t input, int32_t input_type_id, int32_t input_size, int64_t result, int32_t result_type_id, int32_t result_size, int32_t opcode, int32_t id) {
-  printf("cast post -- input: %" PRId64 ", input_type_id: %" PRId32 ", input_size: %" PRId32 ", result: %" PRId64 ", result_type_id: %" PRId32 ", result_size: %" PRId32 ", opcode: %" PRId32 ", id: %" PRId32 "\n", input, input_type_id, input_size, result, result_type_id, result_size, opcode, id);
+int64_t __instrumentor_post_cast(int64_t input, int32_t input_type_id, int32_t input_sub_type_id, int32_t input_size, int64_t result, int32_t result_type_id, int32_t result_sub_type_id, int32_t result_size, int32_t opcode, int32_t id) {
+  printf("cast post -- input: %" PRId64 ", input_type_id: %s, input_sub_type_id: %s, input_size: %" PRId32 ", result: %" PRId64 ", result_type_id: %s, result_sub_type_id: %s, result_size: %" PRId32 ", opcode: %" PRId32 ", id: %" PRId32 "\n", input, getLLVMTypeIDName(input_type_id), getLLVMTypeIDName(input_sub_type_id), input_size, result, getLLVMTypeIDName(result_type_id), getLLVMTypeIDName(result_sub_type_id), result_size, opcode, id);
   return result;
 }
 
-void __instrumentor_post_cast_ind(int64_t *input_ptr, int32_t input_size, int32_t input_type_id, int32_t input_size, int64_t *result_ptr, int32_t result_size, int32_t result_type_id, int32_t result_size, int32_t opcode, int32_t id) {
-  printf("cast post -- input: %p, input_size: %" PRId32 ", input_type_id: %" PRId32 ", input_size: %" PRId32 ", result: %p, result_size: %" PRId32 ", result_type_id: %" PRId32 ", result_size: %" PRId32 ", opcode: %" PRId32 ", id: %" PRId32 "\n", input_ptr, input_size, input_type_id, input_size, result_ptr, result_size, result_type_id, result_size, opcode, id);
+void __instrumentor_post_cast_ind(int64_t *input_ptr, int32_t input_type_id, int32_t input_sub_type_id, int32_t input_size, int64_t *result_ptr, int32_t result_type_id, int32_t result_sub_type_id, int32_t result_size, int32_t opcode, int32_t id) {
+  printf("cast post -- input: %p, input_type_id: %s, input_sub_type_id: %s, input_size: %" PRId32 ", result: %p, result_type_id: %s, result_sub_type_id: %s, result_size: %" PRId32 ", opcode: %" PRId32 ", id: %" PRId32 "\n", input_ptr, getLLVMTypeIDName(input_type_id), getLLVMTypeIDName(input_sub_type_id), input_size, result_ptr, getLLVMTypeIDName(result_type_id), getLLVMTypeIDName(result_sub_type_id), result_size, opcode, id);
 }
 
 void *__instrumentor_post_base_pointer_info(void *base_pointer, int32_t base_pointer_kind, int32_t id) {
diff --git a/llvm/test/Instrumentation/Instrumentor/generate_rt.ll b/llvm/test/Instrumentation/Instrumentor/generate_rt.ll
index 533561b73ce39..1e72becdcd34c 100644
--- a/llvm/test/Instrumentation/Instrumentor/generate_rt.ll
+++ b/llvm/test/Instrumentation/Instrumentor/generate_rt.ll
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
 ; 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 default_rt.c %S/default_rt.c
diff --git a/llvm/test/Instrumentation/Instrumentor/module_and_globals.ll b/llvm/test/Instrumentation/Instrumentor/module_and_globals.ll
index c76eb47030648..64cb0737d0863 100644
--- a/llvm/test/Instrumentation/Instrumentor/module_and_globals.ll
+++ b/llvm/test/Instrumentation/Instrumentor/module_and_globals.ll
@@ -39,34 +39,34 @@ entry:
 ; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @__instrumentor_post_base_pointer_info(ptr [[Y_SHADOW_LOAD]], i32 2, i32 -10) #[[ATTR0]]
 ; CHECK-NEXT:    [[X_SHADOW_LOAD:%.*]] = load ptr, ptr @__instrumentor_shadow.X, align 8
 ; CHECK-NEXT:    [[TMP2:%.*]] = call ptr @__instrumentor_post_base_pointer_info(ptr [[X_SHADOW_LOAD]], i32 2, i32 -9) #[[ATTR0]]
-; CHECK-NEXT:    [[TMP10:%.*]] = call ptr @__instrumentor_pre_load(ptr [[X_SHADOW_LOAD]], i32 0, ptr [[TMP2]], i64 4, i64 4, i32 12, i32 0, i8 1, i8 0, i32 9) #[[ATTR0]]
+; CHECK-NEXT:    [[TMP10:%.*]] = call ptr @__instrumentor_pre_load(ptr [[X_SHADOW_LOAD]], i32 0, ptr [[TMP2]], i64 4, i64 4, i32 12, i32 -1, i32 0, i8 1, i8 0, i32 9) #[[ATTR0]]
 ; CHECK-NEXT:    [[TMP11:%.*]] = load i32, ptr [[TMP10]], align 4
 ; CHECK-NEXT:    [[TMP12:%.*]] = zext i32 [[TMP11]] to i64
-; CHECK-NEXT:    [[TMP13:%.*]] = call i64 @__instrumentor_post_load(ptr [[X_SHADOW_LOAD]], i32 0, ptr [[TMP2]], i64 [[TMP12]], i64 4, i64 4, i32 12, i32 0, i8 1, i8 0, i32 -9) #[[ATTR0]]
+; CHECK-NEXT:    [[TMP13:%.*]] = call i64 @__instrumentor_post_load(ptr [[X_SHADOW_LOAD]], i32 0, ptr [[TMP2]], i64 [[TMP12]], i64 4, i64 4, i32 12, i32 -1, i32 0, i8 1, i8 0, i32 -9) #[[ATTR0]]
 ; CHECK-NEXT:    [[TMP14:%.*]] = trunc i64 [[TMP13]] to i32
-; CHECK-NEXT:    [[TMP8:%.*]] = call ptr @__instrumentor_pre_load(ptr [[Y_SHADOW_LOAD]], i32 0, ptr [[TMP1]], i64 4, i64 4, i32 12, i32 0, i8 1, i8 0, i32 10) #[[ATTR0]]
+; CHECK-NEXT:    [[TMP8:%.*]] = call ptr @__instrumentor_pre_load(ptr [[Y_SHADOW_LOAD]], i32 0, ptr [[TMP1]], i64 4, i64 4, i32 12, i32 -1, i32 0, i8 1, i8 0, i32 10) #[[ATTR0]]
 ; CHECK-NEXT:    [[TMP4:%.*]] = load i32, ptr [[TMP8]], align 4
 ; CHECK-NEXT:    [[TMP15:%.*]] = zext i32 [[TMP4]] to i64
-; CHECK-NEXT:    [[TMP21:%.*]] = call i64 @__instrumentor_post_load(ptr [[Y_SHADOW_LOAD]], i32 0, ptr [[TMP1]], i64 [[TMP15]], i64 4, i64 4, i32 12, i32 0, i8 1, i8 0, i32 -10) #[[ATTR0]]
+; CHECK-NEXT:    [[TMP21:%.*]] = call i64 @__instrumentor_post_load(ptr [[Y_SHADOW_LOAD]], i32 0, ptr [[TMP1]], i64 [[TMP15]], i64 4, i64 4, i32 12, i32 -1, i32 0, i8 1, i8 0, i32 -10) #[[ATTR0]]
 ; CHECK-NEXT:    [[TMP25:%.*]] = trunc i64 [[TMP21]] to i32
-; CHECK-NEXT:    [[TMP26:%.*]] = call ptr @__instrumentor_pre_load(ptr [[Z_SHADOW_LOAD]], i32 0, ptr [[TMP0]], i64 4, i64 4, i32 12, i32 0, i8 1, i8 0, i32 11) #[[ATTR0]]
+; CHECK-NEXT:    [[TMP26:%.*]] = call ptr @__instrumentor_pre_load(ptr [[Z_SHADOW_LOAD]], i32 0, ptr [[TMP0]], i64 4, i64 4, i32 12, i32 -1, i32 0, i8 1, i8 0, i32 11) #[[ATTR0]]
 ; CHECK-NEXT:    [[TMP9:%.*]] = load i32, ptr [[TMP26]], align 4
 ; CHECK-NEXT:    [[TMP16:%.*]] = zext i32 [[TMP9]] to i64
-; CHECK-NEXT:    [[TMP27:%.*]] = call i64 @__instrumentor_post_load(ptr [[Z_SHADOW_LOAD]], i32 0, ptr [[TMP0]], i64 [[TMP16]], i64 4, i64 4, i32 12, i32 0, i8 1, i8 0, i32 -11) #[[ATTR0]]
+; CHECK-NEXT:    [[TMP27:%.*]] = call i64 @__instrumentor_post_load(ptr [[Z_SHADOW_LOAD]], i32 0, ptr [[TMP0]], i64 [[TMP16]], i64 4, i64 4, i32 12, i32 -1, i32 0, i8 1, i8 0, i32 -11) #[[ATTR0]]
 ; CHECK-NEXT:    [[TMP28:%.*]] = trunc i64 [[TMP27]] to i32
 ; CHECK-NEXT:    [[TMP29:%.*]] = zext i32 [[TMP14]] to i64
 ; CHECK-NEXT:    [[TMP30:%.*]] = zext i32 [[TMP25]] to i64
-; CHECK-NEXT:    call void @__instrumentor_pre_numeric(i32 12, i32 4, i32 14, i64 [[TMP29]], i64 [[TMP30]], i64 1, i32 12) #[[ATTR0]]
+; CHECK-NEXT:    call void @__instrumentor_pre_numeric(i32 12, i32 -1, i32 4, i32 14, i64 [[TMP29]], i64 [[TMP30]], i64 1, i32 12) #[[ATTR0]]
 ; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[TMP14]], [[TMP25]]
 ; CHECK-NEXT:    [[TMP17:%.*]] = zext i32 [[ADD]] to i64
-; CHECK-NEXT:    [[TMP18:%.*]] = call i64 @__instrumentor_post_numeric(i32 12, i32 4, i32 14, i64 [[TMP29]], i64 [[TMP30]], i64 [[TMP17]], i64 1, i32 -12) #[[ATTR0]]
+; CHECK-NEXT:    [[TMP18:%.*]] = call i64 @__instrumentor_post_numeric(i32 12, i32 -1, i32 4, i32 14, i64 [[TMP29]], i64 [[TMP30]], i64 [[TMP17]], i64 1, i32 -12) #[[ATTR0]]
 ; CHECK-NEXT:    [[TMP19:%.*]] = trunc i64 [[TMP18]] to i32
 ; CHECK-NEXT:    [[TMP20:%.*]] = zext i32 [[TMP19]] to i64
 ; CHECK-NEXT:    [[TMP24:%.*]] = zext i32 [[TMP28]] to i64
-; CHECK-NEXT:    call void @__instrumentor_pre_numeric(i32 12, i32 4, i32 14, i64 [[TMP20]], i64 [[TMP24]], i64 1, i32 13) #[[ATTR0]]
+; CHECK-NEXT:    call void @__instrumentor_pre_numeric(i32 12, i32 -1, i32 4, i32 14, i64 [[TMP20]], i64 [[TMP24]], i64 1, i32 13) #[[ATTR0]]
 ; CHECK-NEXT:    [[ADD3:%.*]] = add nsw i32 [[TMP19]], [[TMP28]]
 ; CHECK-NEXT:    [[TMP22:%.*]] = zext i32 [[ADD3]] to i64
-; CHECK-NEXT:    [[TMP23:%.*]] = call i64 @__instrumentor_post_numeric(i32 12, i32 4, i32 14, i64 [[TMP20]], i64 [[TMP24]], i64 [[TMP22]], i64 1, i32 -13) #[[ATTR0]]
+; CHECK-NEXT:    [[TMP23:%.*]] = call i64 @__instrumentor_post_numeric(i32 12, i32 -1, i32 4, i32 14, i64 [[TMP20]], i64 [[TMP24]], i64 [[TMP22]], i64 1, i32 -13) #[[ATTR0]]
 ; CHECK-NEXT:    [[ADD2:%.*]] = trunc i64 [[TMP23]] to i32
 ; CHECK-NEXT:    call void @__instrumentor_post_function(ptr @foo, ptr @__instrumentor_.str.5, i32 0, ptr null, i8 0, i32 -15) #[[ATTR0]]
 ; CHECK-NEXT:    ret i32 [[ADD2]]
diff --git a/llvm/test/Instrumentation/Instrumentor/numeric_subtypeid.ll b/llvm/test/Instrumentation/Instrumentor/numeric_subtypeid.ll
new file mode 100644
index 0000000000000..4f6b40f19c995
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/numeric_subtypeid.ll
@@ -0,0 +1,39 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; Test that the sub_type_id parameter is correctly passed to instrumentation for vector types
+; RUN: opt < %s -passes=instrumentor -instrumentor-read-config-files=%S/numeric_subtypeid_config.json -S | FileCheck %s
+
+define <4 x float> @test_vector_fadd(<4 x float> %a, <4 x float> %b) {
+; CHECK-LABEL: define <4 x float> @test_vector_fadd(
+; CHECK-SAME: <4 x float> [[A:%.*]], <4 x float> [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = alloca <4 x float>, align 16
+; CHECK-NEXT:    [[TMP1:%.*]] = alloca <4 x float>, align 16
+; CHECK-NEXT:    [[TMP2:%.*]] = alloca <4 x float>, align 16
+; CHECK-NEXT:    [[RES:%.*]] = fadd <4 x float> [[A]], [[B]]
+; CHECK-NEXT:    store <4 x float> [[A]], ptr [[TMP2]], align 16
+; CHECK-NEXT:    store <4 x float> [[B]], ptr [[TMP1]], align 16
+; CHECK-NEXT:    store <4 x float> [[RES]], ptr [[TMP0]], align 16
+; CHECK-NEXT:    call void @__instrumentor_post_numeric_ind(i32 18, i32 2, i32 16, i32 15, ptr [[TMP2]], ptr [[TMP1]], ptr [[TMP0]], i64 0, i32 -1) #[[ATTR0:[0-9]+]]
+; CHECK-NEXT:    ret <4 x float> [[RES]]
+;
+entry:
+  %res = fadd <4 x float> %a, %b
+  ret <4 x float> %res
+}
+
+define double @test_scalar_fadd(double %a, double %b) {
+; CHECK-LABEL: define double @test_scalar_fadd(
+; CHECK-SAME: double [[A:%.*]], double [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[RES:%.*]] = fadd double [[A]], [[B]]
+; CHECK-NEXT:    [[TMP0:%.*]] = bitcast double [[A]] to i64
+; CHECK-NEXT:    [[TMP1:%.*]] = bitcast double [[B]] to i64
+; CHECK-NEXT:    [[TMP2:%.*]] = bitcast double [[RES]] to i64
+; CHECK-NEXT:    call void @__instrumentor_post_numeric(i32 3, i32 -1, i32 8, i32 15, i64 [[TMP0]], i64 [[TMP1]], i64 [[TMP2]], i64 0, i32 -2) #[[ATTR0]]
+; CHECK-NEXT:    ret double [[RES]]
+;
+entry:
+  %res = fadd double %a, %b
+  ret double %res
+}
+
diff --git a/llvm/test/Instrumentation/Instrumentor/numeric_subtypeid_config.json b/llvm/test/Instrumentation/Instrumentor/numeric_subtypeid_config.json
new file mode 100644
index 0000000000000..8f3ddc0e8cdd8
--- /dev/null
+++ b/llvm/test/Instrumentation/Instrumentor/numeric_subtypeid_config.json
@@ -0,0 +1,19 @@
+{
+  "configuration": {
+    "runtime_prefix": "__instrumentor_"
+  },
+  "instruction_post": {
+    "numeric": {
+      "enabled": true,
+      "type_id": true,
+      "sub_type_id": true,
+      "size": true,
+      "opcode": true,
+      "left": true,
+      "right": true,
+      "result": true,
+      "flags": true,
+      "id": true
+    }
+  }
+}
diff --git a/llvm/test/Instrumentation/Instrumentor/rt_config.json b/llvm/test/Instrumentation/Instrumentor/rt_config.json
index bcd6708cb5e15..42d0f0b047309 100644
--- a/llvm/test/Instrumentation/Instrumentor/rt_config.json
+++ b/llvm/test/Instrumentation/Instrumentor/rt_config.json
@@ -168,6 +168,8 @@
       "alignment.description": "The known access alignment.",
       "value_type_id": true,
       "value_type_id.description": "The type id of the loaded value.",
+      "value_sub_type_id": true,
+      "value_sub_type_id.description": "The sub type id of the loaded value (for arrays and vectors, or -1).",
       "atomicity_ordering": true,
       "atomicity_ordering.description": "The atomicity ordering of the load.",
       "sync_scope_id": true,
@@ -196,6 +198,8 @@
       "alignment.description": "The known access alignment.",
       "value_type_id": true,
       "value_type_id.description": "The type id of the stored value.",
+      "value_sub_type_id": true,
+      "value_sub_type_id.description": "The type id of the stored value (for arrays and vectors, or -1).",
       "atomicity_ordering": true,
       "atomicity_ordering.description": "The atomicity ordering of the store.",
       "sync_scope_id": true,
@@ -213,10 +217,14 @@
       "input.description": "Input value of the cast.",
       "input_type_id": true,
       "input_type_id.description": "The type id of the input value.",
+      "input_sub_type_id": true,
+      "input_sub_type_id.description": "The sub type id of the input value (for arrays and vectors, or -1).",
       "input_size": true,
       "input_size.description": "The size of the input value.",
       "result_type_id": true,
       "result_type_id.description": "The type id of the result value.",
+      "result_sub_type_id": true,
+      "result_sub_type_id.description": "The sub type id of the result value (for arrays and vectors, or -1).",
       "result_size": true,
       "result_size.description": "The size of the result value.",
       "opcode": true,
@@ -259,6 +267,8 @@
       "alignment.description": "The known access alignment.",
       "value_type_id": true,
       "value_type_id.description": "The type id of the loaded value.",
+      "value_sub_type_id": true,
+      "value_sub_type_id.description": "The sub type id of the loaded value (for arrays and vectors, or -1).",
       "atomicity_ordering": true,
       "atomicity_ordering.description": "The atomicity ordering of the load.",
       "sync_scope_id": true,
@@ -286,6 +296,8 @@
       "alignment.description": "The known access alignment.",
       "value_type_id": true,
       "value_type_id.description": "The type id of the stored value.",
+      "value_sub_type_id": true,
+      "value_sub_type_id.description": "The type id of the stored value (for arrays and vectors, or -1).",
       "atomicity_ordering": true,
       "atomicity_ordering.description": "The atomicity ordering of the store.",
       "sync_scope_id": true,
@@ -303,6 +315,8 @@
       "input.description": "Input value of the cast.",
       "input_type_id": true,
       "input_type_id.description": "The type id of the input value.",
+      "input_sub_type_id": true,
+      "input_sub_type_id.description": "The sub type id of the input value (for arrays and vectors, or -1).",
       "input_size": true,
       "input_size.description": "The size of the input value.",
       "result": true,
@@ -310,6 +324,8 @@
       "result.description": "Result of the cast.",
       "result_type_id": true,
       "result_type_id.description": "The type id of the result value.",
+      "result_sub_type_id": true,
+      "result_sub_type_id.description": "The sub type id of the result value (for arrays and vectors, or -1).",
       "result_size": true,
       "result_size.description": "The size of the result value.",
       "opcode": true,

>From e8ce6c4c7d8cd2f62265e9b499b57e80cb9f337e Mon Sep 17 00:00:00 2001
From: Johannes Doerfert <jdoerfert.llvm at gmail.com>
Date: Mon, 15 Jun 2026 11:20:02 -0700
Subject: [PATCH 2/2] [Instrumentor] Add runtime examples: [1/N] A flop counter

This adds a instrumentor-tools folder into compiler RT to showcase
use cases of the instrumentor. The initial example is a program that,
via instrumentation, counts the number of flops performed. Call and
intrinsic support will follow after #198042.

Partially developped by Claude (AI), tested and verified by me.
---
 .../cmake/Modules/AllSupportedArchDefs.cmake  |   2 +
 compiler-rt/cmake/config-ix.cmake             |   4 +
 compiler-rt/lib/CMakeLists.txt                |   3 +
 .../lib/instrumentor-tools/CMakeLists.txt     |  11 +
 compiler-rt/lib/instrumentor-tools/README.md  |  49 +++
 .../flop-counter/CMakeLists.txt               |  67 ++++
 .../instrumentor-tools/flop-counter/README.md |  77 +++++
 .../flop-counter/flop_counter_config.json     |  32 ++
 .../flop-counter/flop_counter_runtime.cpp     | 169 ++++++++++
 .../instrumentor-tools/instrumentor_runtime.h | 295 ++++++++++++++++++
 compiler-rt/test/CMakeLists.txt               |   4 +-
 .../test/instrumentor-tools/CMakeLists.txt    |  54 ++++
 .../test/instrumentor-tools/lit.cfg.py        |  75 +++++
 .../instrumentor-tools/lit.site.cfg.py.in     |  12 +
 .../test/instrumentor-tools/simple_flops.c    |  49 +++
 .../test/instrumentor-tools/vector_flops.cpp  |  46 +++
 16 files changed, 948 insertions(+), 1 deletion(-)
 create mode 100644 compiler-rt/lib/instrumentor-tools/CMakeLists.txt
 create mode 100644 compiler-rt/lib/instrumentor-tools/README.md
 create mode 100644 compiler-rt/lib/instrumentor-tools/flop-counter/CMakeLists.txt
 create mode 100644 compiler-rt/lib/instrumentor-tools/flop-counter/README.md
 create mode 100644 compiler-rt/lib/instrumentor-tools/flop-counter/flop_counter_config.json
 create mode 100644 compiler-rt/lib/instrumentor-tools/flop-counter/flop_counter_runtime.cpp
 create mode 100644 compiler-rt/lib/instrumentor-tools/instrumentor_runtime.h
 create mode 100644 compiler-rt/test/instrumentor-tools/CMakeLists.txt
 create mode 100644 compiler-rt/test/instrumentor-tools/lit.cfg.py
 create mode 100644 compiler-rt/test/instrumentor-tools/lit.site.cfg.py.in
 create mode 100644 compiler-rt/test/instrumentor-tools/simple_flops.c
 create mode 100644 compiler-rt/test/instrumentor-tools/vector_flops.cpp

diff --git a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
index a535cf9e3a8da..fee5f4a5720ed 100644
--- a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
+++ b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
@@ -138,3 +138,5 @@ endif()
 if (WIN32)
   set(ALL_ORC_SUPPORTED_ARCH ${X86_64})
 endif()
+
+set(ALL_INSTRUMENTOR_SUPPORTED_ARCH ${ALL_SANITIZER_COMMON_SUPPORTED_ARCH})
diff --git a/compiler-rt/cmake/config-ix.cmake b/compiler-rt/cmake/config-ix.cmake
index 083f1c98d0f16..de67acb937afd 100644
--- a/compiler-rt/cmake/config-ix.cmake
+++ b/compiler-rt/cmake/config-ix.cmake
@@ -704,6 +704,9 @@ if(APPLE)
   list_intersect(ORC_SUPPORTED_ARCH
     ALL_ORC_SUPPORTED_ARCH
     SANITIZER_COMMON_SUPPORTED_ARCH)
+  list_intersect(INSTRUMENTOR_SUPPORTED_ARCH
+    ALL_INSTRUMENTOR_SUPPORTED_ARCH
+    SANITIZER_COMMON_SUPPORTED_ARCH)
 
 else()
   # Architectures supported by compiler-rt libraries.
@@ -739,6 +742,7 @@ else()
   filter_available_targets(GWP_ASAN_SUPPORTED_ARCH ${ALL_GWP_ASAN_SUPPORTED_ARCH})
   filter_available_targets(NSAN_SUPPORTED_ARCH ${ALL_NSAN_SUPPORTED_ARCH})
   filter_available_targets(ORC_SUPPORTED_ARCH ${ALL_ORC_SUPPORTED_ARCH})
+  filter_available_targets(INSTRUMENTOR_SUPPORTED_ARCH ${ALL_INSTRUMENTOR_SUPPORTED_ARCH})
 endif()
 
 if (MSVC)
diff --git a/compiler-rt/lib/CMakeLists.txt b/compiler-rt/lib/CMakeLists.txt
index e6158ec408895..4240301068366 100644
--- a/compiler-rt/lib/CMakeLists.txt
+++ b/compiler-rt/lib/CMakeLists.txt
@@ -78,3 +78,6 @@ endif()
 # is true for fuzzers that exercise parts of the runtime. So we add the fuzzer
 # directories explicitly here.
 add_subdirectory(scudo/standalone/fuzz)
+
+# Instrumentor tools - educational tools demonstrating the Instrumentor pass
+add_subdirectory(instrumentor-tools)
diff --git a/compiler-rt/lib/instrumentor-tools/CMakeLists.txt b/compiler-rt/lib/instrumentor-tools/CMakeLists.txt
new file mode 100644
index 0000000000000..6f8e2fe352f5a
--- /dev/null
+++ b/compiler-rt/lib/instrumentor-tools/CMakeLists.txt
@@ -0,0 +1,11 @@
+# CMakeLists.txt for Instrumentor Examples
+#
+# This directory contains example runtimes that demonstrate how to use the
+# LLVM Instrumentor pass for various profiling and analysis tasks.
+
+include(AddCompilerRT)
+
+add_compiler_rt_component(instrumentor-tools)
+
+# Add subdirectories for specific examples
+add_subdirectory(flop-counter)
diff --git a/compiler-rt/lib/instrumentor-tools/README.md b/compiler-rt/lib/instrumentor-tools/README.md
new file mode 100644
index 0000000000000..5f50c7c7b001a
--- /dev/null
+++ b/compiler-rt/lib/instrumentor-tools/README.md
@@ -0,0 +1,49 @@
+# Instrumentor Tools
+
+This directory contains example runtime libraries that demonstrate how to use
+the LLVM Instrumentor pass for various profiling and analysis tasks.
+
+## Overview
+
+The LLVM Instrumentor is a configurable instrumentation pass that allows you to
+insert runtime calls at various program points (e.g., function entry/exit,
+memory operations, floating-point operations). Each example in this directory
+provides:
+
+1. A runtime library that implements the instrumentation callbacks
+2. An instrumentor configuration JSON file
+3. Tests demonstrating usage
+
+## Building
+
+The instrumentor tools are built as part of the compiler-rt build:
+
+```bash
+cmake -S llvm -B build -G Ninja \
+  -DCMAKE_BUILD_TYPE=RelWithDebInfo \
+  -DLLVM_ENABLE_PROJECTS="clang;compiler-rt"
+ninja -C build
+```
+
+The runtime libraries will be installed in:
+- Darwin: `lib/clang/<version>/lib/darwin/libclang_rt.<example>_osx.a`
+- Linux: `lib/clang/<version>/lib/linux/libclang_rt.<example>-<arch>.a`
+
+Configuration files will be installed in `share/llvm/instrumentor-configs/`.
+
+## Adding New Tools 
+
+To add a new instrumentor example:
+
+1. Create a new directory under `compiler-rt/lib/instrumentor-tools/`
+2. Add your runtime implementation (`.cpp` and `.h` files)
+3. Create an instrumentor configuration JSON file
+4. Add a `CMakeLists.txt` (see `flop-counter/CMakeLists.txt` as a template)
+5. Update `compiler-rt/lib/instrumentor-tools/CMakeLists.txt` to include your subdirectory
+6. Add tests in `compiler-rt/test/instrumentor-tools/`
+
+## Resources
+
+- [Instrumentor Documentation](../../../llvm/docs/Instrumentor.rst)
+- [Instrumentor Runtime Headers](../../../llvm/utils/instrumentor_runtime.h)
+- [Configuration Wizard](../../../llvm/utils/instrumentor-config-wizard.py)
diff --git a/compiler-rt/lib/instrumentor-tools/flop-counter/CMakeLists.txt b/compiler-rt/lib/instrumentor-tools/flop-counter/CMakeLists.txt
new file mode 100644
index 0000000000000..6c65afead7bb8
--- /dev/null
+++ b/compiler-rt/lib/instrumentor-tools/flop-counter/CMakeLists.txt
@@ -0,0 +1,67 @@
+# CMakeLists.txt for FLOP Counter Example
+#
+# This example demonstrates counting floating-point operations using the
+# Instrumentor pass. It provides a runtime library that can be linked with
+# instrumented code to track and report FLOP counts.
+
+add_compiler_rt_component(flop-counter)
+
+set(FLOP_COUNTER_SOURCES
+  flop_counter_runtime.cpp
+  )
+
+set(FLOP_COUNTER_HEADERS
+  )
+
+# Include paths for instrumentor runtime headers
+# The instrumentor runtime headers are in llvm/utils
+include_directories(${COMPILER_RT_SOURCE_DIR}/../llvm/utils)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+
+# Common flags
+set(FLOP_COUNTER_CFLAGS
+  ${COMPILER_RT_COMMON_CFLAGS}
+  -std=c++17
+  )
+
+# Determine supported architectures
+if(APPLE)
+  # On Darwin, use the darwin OSX architectures
+  set(FLOP_COUNTER_SUPPORTED_ARCH arm64)
+  if(NOT CMAKE_OSX_ARCHITECTURES STREQUAL "")
+    set(FLOP_COUNTER_SUPPORTED_ARCH ${CMAKE_OSX_ARCHITECTURES})
+  endif()
+  if(DARWIN_osx_ARCHS)
+    set(FLOP_COUNTER_SUPPORTED_ARCH ${DARWIN_osx_ARCHS})
+  endif()
+else()
+  # For non-Apple platforms, use the default target architecture
+  set(FLOP_COUNTER_SUPPORTED_ARCH ${COMPILER_RT_DEFAULT_TARGET_ARCH})
+endif()
+
+message(STATUS "FLOP Counter supported architectures: ${FLOP_COUNTER_SUPPORTED_ARCH}")
+
+# Build the static runtime library for Apple platforms
+if(APPLE)
+  add_compiler_rt_runtime(clang_rt.flop_counter
+    STATIC
+    OS osx
+    ARCHS ${FLOP_COUNTER_SUPPORTED_ARCH}
+    CFLAGS ${FLOP_COUNTER_CFLAGS}
+    SOURCES ${FLOP_COUNTER_SOURCES}
+    ADDITIONAL_HEADERS ${FLOP_COUNTER_HEADERS}
+    PARENT_TARGET flop-counter)
+else()
+  add_compiler_rt_runtime(clang_rt.flop_counter
+    STATIC
+    ARCHS ${FLOP_COUNTER_SUPPORTED_ARCH}
+    CFLAGS ${FLOP_COUNTER_CFLAGS}
+    SOURCES ${FLOP_COUNTER_SOURCES}
+    ADDITIONAL_HEADERS ${FLOP_COUNTER_HEADERS}
+    PARENT_TARGET flop-counter)
+endif()
+
+# Install the configuration file as a resource
+install(FILES flop_counter_config.json
+        DESTINATION share/llvm/instrumentor-configs
+        COMPONENT flop-counter)
diff --git a/compiler-rt/lib/instrumentor-tools/flop-counter/README.md b/compiler-rt/lib/instrumentor-tools/flop-counter/README.md
new file mode 100644
index 0000000000000..c00a3e57d1a65
--- /dev/null
+++ b/compiler-rt/lib/instrumentor-tools/flop-counter/README.md
@@ -0,0 +1,77 @@
+# FLOP Counter
+
+A runtime library for counting floating-point operations in programs using the LLVM Instrumentor pass.
+
+## Features
+
+- **Precision Tracking**: Separates counts for single (float), double, and extended precision operations
+- **Operation Categorization**: Tracks adds, multiplications, divisions, FMA operations (TODO), and others (sqrt, sin, cos, etc.) (TODO)
+- **Vector Support**: Counts FLOPs in vector operations
+- **Thread-Safe**: Uses atomic operations for counter updates
+- **Low Overhead**: Minimal runtime overhead for counting
+- **Automatic Reporting**: Prints statistics at program exit
+
+## Usage
+
+### Basic Example
+
+```c
+#include <stdio.h>
+#include <math.h>
+
+double compute(double a, double b) {
+  return sqrt(a * a + b * b);
+}
+
+int main() {
+  double result = compute(3.0, 4.0);
+  printf("Result: %f\n", result);
+  return 0;
+}
+```
+
+Compile with:
+```bash
+clangxx -O2 -finstrumentor=flop_counter_config.json example.cpp \
+        -lclang_rt.flop_counter -o example
+```
+
+Run:
+```bash
+./example
+```
+
+Output:
+```
+Result: 5.000000
+
+=================================================
+           FLOP Counter Statistics
+=================================================
+Total FLOPs:                             3
+...
+```
+
+## Implementation Details
+
+### Instrumentation Points
+
+The FLOP counter instruments:
+
+1. **Binary FP Operations**: `fadd`, `fsub`, `fmul`, `fdiv`, `frem`
+2. **Unary FP Operations**: `fneg`
+3. TODO: **FP Intrinsics**: `llvm.fma`, `llvm.sqrt`, `llvm.sin`, `llvm.cos`, etc.
+
+### FLOP Counting Rules
+
+- **Regular operations**: 1 FLOP per operation
+- **FMA (Fused Multiply-Add)**: 2 FLOPs (multiply + add)
+- **Vector operations**: Counted per element
+- **Intrinsics**: TODO
+
+### Configuration
+
+The `flop_counter_config.json` file configures the instrumentor to:
+- Insert callbacks after floating-point binary/unary operations
+- Pass value size, type IDs, and opcodes to the runtime
+- Filter to only instrument FP math operations
diff --git a/compiler-rt/lib/instrumentor-tools/flop-counter/flop_counter_config.json b/compiler-rt/lib/instrumentor-tools/flop-counter/flop_counter_config.json
new file mode 100644
index 0000000000000..c3131c363fded
--- /dev/null
+++ b/compiler-rt/lib/instrumentor-tools/flop-counter/flop_counter_config.json
@@ -0,0 +1,32 @@
+{
+  "configuration": {
+    "runtime_prefix": "__flop_counter_",
+    "runtime_prefix.description": "The runtime API prefix.",
+    "runtime_stubs_file": "rt",
+    "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"
+  },
+  "instruction_post": {
+    "numeric": {
+      "enabled": true,
+      "filter": "type_id < 7 || ((type_id == 17 || type_id == 18) && sub_type_id < 7)",
+      "filter.description": "Static property filter to exclude instrumentation.",
+      "type_id": true,
+      "type_id.description": "The operation's type id.",
+      "sub_type_id": true,
+      "sub_type_id.description": "The operation's type id.",
+      "size": true,
+      "size.description": "The operation's type size.",
+      "opcode": true,
+      "opcode.description": "The instruction opcode."
+    }
+  }
+}
diff --git a/compiler-rt/lib/instrumentor-tools/flop-counter/flop_counter_runtime.cpp b/compiler-rt/lib/instrumentor-tools/flop-counter/flop_counter_runtime.cpp
new file mode 100644
index 0000000000000..5dc08777c5e48
--- /dev/null
+++ b/compiler-rt/lib/instrumentor-tools/flop-counter/flop_counter_runtime.cpp
@@ -0,0 +1,169 @@
+//===-- flop_counter_runtime.cpp - FLOP Counter Runtime ------------------===//
+//
+// 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 runtime for counting floating-point operations.
+// It hooks into instrumentation points inserted by the LLVM Instrumentor pass.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../instrumentor_runtime.h"
+
+#include <atomic>
+#include <cinttypes>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+namespace {
+
+/// FLOP counter statistics (thread-safe using atomics)
+struct FlopCounterStats {
+  std::atomic<uint64_t> TotalFlops{0};
+  std::atomic<uint64_t> FloatOps{0};  // 32-bit float operations
+  std::atomic<uint64_t> DoubleOps{0}; // 64-bit double operations
+  std::atomic<uint64_t> ExtendedOps{
+      0}; // 80/128-bit extended precision operations
+  std::atomic<uint64_t> VectorFlops{0}; // Total FLOPs from vector operations
+  std::atomic<uint64_t> AddOps{0};
+  std::atomic<uint64_t> MulOps{0};
+  std::atomic<uint64_t> DivOps{0};
+  std::atomic<uint64_t> FmaOps{0};   // Fused multiply-add operations
+  std::atomic<uint64_t> OtherOps{0}; // sqrt, sin, cos, etc.
+};
+
+// Global statistics counters
+static FlopCounterStats *Stats = nullptr;
+
+enum {
+  LLVMOpcodeFAdd = 15,
+  LLVMOpcodeFSub = 17,
+  LLVMOpcodeFMul = 19,
+  LLVMOpcodeFDiv = 22,
+  LLVMOpcodeFRem = 25,
+  LLVMOpcodeFNeg = 13,
+};
+
+} // namespace
+
+extern "C" {
+
+__attribute__((constructor(1000))) void __flop_counter_initialize() {
+  Stats = new FlopCounterStats();
+}
+
+__attribute__((destructor(1000))) void __flop_counter_finalize() {
+  std::printf("\n");
+  std::printf("=================================================\n");
+  std::printf("           FLOP Counter Statistics\n");
+  std::printf("=================================================\n");
+  std::printf("Total FLOPs:              %20llu\n",
+              Stats->TotalFlops.load(std::memory_order_relaxed));
+  std::printf("\n");
+  std::printf("By Precision:\n");
+  std::printf("  Single (float):         %20llu\n",
+              Stats->FloatOps.load(std::memory_order_relaxed));
+  std::printf("  Double (double):        %20llu\n",
+              Stats->DoubleOps.load(std::memory_order_relaxed));
+  std::printf("  Extended (fp80/fp128):  %20llu\n",
+              Stats->ExtendedOps.load(std::memory_order_relaxed));
+  std::printf("  Vector FLOPs:           %20llu\n",
+              Stats->VectorFlops.load(std::memory_order_relaxed));
+  std::printf("\n");
+  std::printf("By Operation:\n");
+  std::printf("  Addition/Subtraction:   %20llu\n",
+              Stats->AddOps.load(std::memory_order_relaxed));
+  std::printf("  Multiplication:         %20llu\n",
+              Stats->MulOps.load(std::memory_order_relaxed));
+  std::printf("  Division:               %20llu\n",
+              Stats->DivOps.load(std::memory_order_relaxed));
+  std::printf("  Fused Multiply-Add:     %20llu\n",
+              Stats->FmaOps.load(std::memory_order_relaxed));
+  std::printf("  Other (sqrt, sin, ...): %20llu\n",
+              Stats->OtherOps.load(std::memory_order_relaxed));
+  std::printf("=================================================\n");
+
+  delete Stats;
+}
+
+void __flop_counter_post_numeric(int32_t TypeId, int32_t SubTypeId,
+                                 int32_t Size, int32_t Opcode) {
+  bool IsVector = false;
+  switch (TypeId) {
+  case FixedVectorTyID:
+  case ScalableVectorTyID:
+    IsVector = true;
+    TypeId = SubTypeId;
+    break;
+  default:
+    break;
+  };
+
+  int32_t TypeSize = Size;
+  switch (TypeId) {
+  case HalfTyID:
+  case BFloatTyID:
+    TypeSize = 2;
+    break;
+  case FloatTyID:
+    TypeSize = 4;
+    break;
+  case DoubleTyID:
+    TypeSize = 8;
+    break;
+  case X86_FP80TyID:
+  case FP128TyID:
+  case PPC_FP128TyID:
+    TypeSize = 16;
+    break;
+  default:
+    break;
+  };
+
+  // Determine FLOP count based on whether it's a vector operation
+  uint64_t FlopCount = Size / TypeSize;
+  if (IsVector) {
+    Stats->VectorFlops.fetch_add(FlopCount, std::memory_order_relaxed);
+  } else {
+    // Categorize by precision
+    if (TypeId == 2) {
+      Stats->FloatOps.fetch_add(1, std::memory_order_relaxed);
+    } else if (TypeId == 3) {
+      Stats->DoubleOps.fetch_add(1, std::memory_order_relaxed);
+    } else {
+      Stats->ExtendedOps.fetch_add(1, std::memory_order_relaxed);
+    }
+  }
+
+  // Categorize by operation type
+  switch (Opcode) {
+  case LLVMOpcodeFAdd:
+  case LLVMOpcodeFSub:
+    Stats->AddOps.fetch_add(FlopCount, std::memory_order_relaxed);
+    break;
+  case LLVMOpcodeFMul:
+    Stats->MulOps.fetch_add(FlopCount, std::memory_order_relaxed);
+    break;
+  case LLVMOpcodeFDiv:
+  case LLVMOpcodeFRem:
+    Stats->DivOps.fetch_add(FlopCount, std::memory_order_relaxed);
+    break;
+  default:
+    Stats->OtherOps.fetch_add(FlopCount, std::memory_order_relaxed);
+    break;
+  }
+
+  Stats->TotalFlops.fetch_add(FlopCount, std::memory_order_relaxed);
+}
+
+void __flop_counter_post_numeric_ind(int32_t TypeId, int32_t SubTypeId,
+                                     int32_t Size, int32_t Opcode) {
+  return __flop_counter_post_numeric(TypeId, SubTypeId, Size, Opcode);
+}
+
+} // extern "C"
diff --git a/compiler-rt/lib/instrumentor-tools/instrumentor_runtime.h b/compiler-rt/lib/instrumentor-tools/instrumentor_runtime.h
new file mode 100644
index 0000000000000..77d9bc6819218
--- /dev/null
+++ b/compiler-rt/lib/instrumentor-tools/instrumentor_runtime.h
@@ -0,0 +1,295 @@
+//===-- 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.
+//
+//===----------------------------------------------------------------------===//
+
+#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
+
+// Generated with runtime prefix: __flop_counter_
diff --git a/compiler-rt/test/CMakeLists.txt b/compiler-rt/test/CMakeLists.txt
index 3fab82518e75f..dae3e72711a18 100644
--- a/compiler-rt/test/CMakeLists.txt
+++ b/compiler-rt/test/CMakeLists.txt
@@ -60,7 +60,7 @@ umbrella_lit_testsuite_begin(check-compiler-rt)
 set(COMPILER_RT_KNOWN_TEST_SUITES
     builtins;ctx_profile;fuzzer;interception;lsan;memprof;metadata
     ;orc;profile;sanitizer_common;shadowcallstack
-    ;ubsan;xray)
+    ;ubsan;xray;instrumentor-tools)
 list(APPEND COMPILER_RT_KNOWN_TEST_SUITES ${ALL_SANITIZERS})
 list(REMOVE_DUPLICATES COMPILER_RT_KNOWN_TEST_SUITES)
 # Sort the list so that's easier to read when emitting errors.
@@ -170,6 +170,8 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS)
   # ShadowCallStack does not yet provide a runtime with compiler-rt, the tests
   # include their own minimal runtime
   compiler_rt_test_runtime(shadowcallstack NO_COMPILER_RT_HAS_GUARD)
+
+  compiler_rt_test_runtime(instrumentor-tools NO_COMPILER_RT_HAS_GUARD)
 endif()
 
 # Now that we've traversed all the directories and know all the lit testsuites,
diff --git a/compiler-rt/test/instrumentor-tools/CMakeLists.txt b/compiler-rt/test/instrumentor-tools/CMakeLists.txt
new file mode 100644
index 0000000000000..cda35a017fed5
--- /dev/null
+++ b/compiler-rt/test/instrumentor-tools/CMakeLists.txt
@@ -0,0 +1,54 @@
+set(INSTRUMENTOR_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+set(INSTRUMENTOR_LIT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
+
+# Debug: Print all relevant variables
+set(INSTRUMENTOR_TESTSUITES)
+set(INSTRUMENTOR_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS})
+list(APPEND INSTRUMENTOR_TEST_DEPS flop-counter)
+
+# Check if INSTRUMENTOR_SUPPORTED_ARCH is defined
+if(NOT DEFINED INSTRUMENTOR_SUPPORTED_ARCH)
+  message(STATUS "INSTRUMENTOR_SUPPORTED_ARCH is NOT DEFINED - using fallback")
+  # Use the same architectures as FLOP counter
+  if(APPLE)
+    set(INSTRUMENTOR_SUPPORTED_ARCH ${DARWIN_osx_ARCHS})
+  else()
+    set(INSTRUMENTOR_SUPPORTED_ARCH ${COMPILER_RT_DEFAULT_TARGET_ARCH})
+  endif()
+endif()
+
+set(INSTRUMENTOR_TEST_ARCH ${INSTRUMENTOR_SUPPORTED_ARCH})
+if(APPLE)
+  darwin_filter_host_archs(INSTRUMENTOR_SUPPORTED_ARCH INSTRUMENTOR_TEST_ARCH)
+endif()
+
+
+macro(add_instrumentor_testsuite test_mode sanitizer arch)
+  set(INSTRUMENTOR_LIT_TEST_MODE "${test_mode}")
+  set(CONFIG_NAME ${test_mode}-${arch})
+
+  set(INSTRUMENTOR_TEST_TARGET_ARCH ${arch})
+  get_test_cc_for_arch(${arch} INSTRUMENTOR_TEST_TARGET_CC INSTRUMENTOR_TEST_TARGET_CFLAGS)
+
+  configure_lit_site_cfg(
+    ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in
+    ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg.py)
+  list(APPEND INSTRUMENTOR_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME})
+
+endmacro()
+
+if(INSTRUMENTOR_TEST_ARCH)
+  foreach(arch ${INSTRUMENTOR_TEST_ARCH})
+    add_instrumentor_testsuite("InstrumentorTools" instrumentor-tools ${arch})
+  endforeach()
+else()
+  message(WARNING "No architectures configured for instrumentor-tools tests!")
+endif()
+
+if(INSTRUMENTOR_TESTSUITES)
+  add_lit_testsuite(check-instrumentor-tools "Running the instrumentor-tools tests"
+    ${INSTRUMENTOR_TESTSUITES}
+    DEPENDS ${INSTRUMENTOR_TEST_DEPS})
+else()
+  message(WARNING "No test suites configured for instrumentor-tools!")
+endif()
diff --git a/compiler-rt/test/instrumentor-tools/lit.cfg.py b/compiler-rt/test/instrumentor-tools/lit.cfg.py
new file mode 100644
index 0000000000000..24a4897efbbb5
--- /dev/null
+++ b/compiler-rt/test/instrumentor-tools/lit.cfg.py
@@ -0,0 +1,75 @@
+# -*- Python -*-
+
+import os
+
+
+def get_required_attr(config, attr_name):
+    attr_value = getattr(config, attr_name, None)
+    if attr_value is None:
+        lit_config.fatal(
+            "No attribute %r in test configuration! You may need to run "
+            "tests from your build directory or add this attribute "
+            "to lit.site.cfg.py " % attr_name
+        )
+    return attr_value
+
+
+# Setup config name.
+config.name = "InstrumentorTools-" + config.target_arch
+
+# Setup source root.
+config.test_source_root = os.path.dirname(__file__)
+
+# Setup executable root.
+if (
+    hasattr(config, "instrumentor_lit_binary_dir")
+    and config.instrumentor_lit_binary_dir is not None
+):
+    config.test_exec_root = os.path.join(
+        config.instrumentor_lit_binary_dir, config.name
+    )
+
+# Test suffixes.
+config.suffixes = [".c", ".cpp", ".m", ".mm", ".ll", ".test"]
+
+# What to exclude.
+config.excludes = ["Inputs"]
+
+# Clang flags.
+target_cflags = [get_required_attr(config, "target_cflags")]
+clang_cflags = target_cflags
+clang_cxxflags = config.cxx_mode_flags + clang_cflags
+
+
+def build_invocation(compile_flags):
+    return " " + " ".join([config.clang] + compile_flags) + " "
+
+
+def make_lib_name(name):
+    if config.target_os != "Darwin":
+        return "clang_rt." + name
+    return "clang_rt." + name + "_osx"
+
+
+# Add clang substitutions.
+config.substitutions.append(("%clang ", build_invocation(clang_cflags)))
+config.substitutions.append(("%clangxx ", build_invocation(clang_cxxflags)))
+
+flop_counter_lib = make_lib_name("flop_counter")
+config.substitutions.append(("%flop_counter_lib", flop_counter_lib))
+
+config.substitutions.append(("%lib_dir", config.compiler_rt_libdir))
+
+# Add path to instrumentor config files
+config_dir = os.path.join(
+    config.test_source_root, "..", "..", "lib", "instrumentor-tools"
+)
+config.substitutions.append(("%config_dir", config_dir))
+
+# Check if running on a supported platform
+if config.target_os not in [
+    "Darwin",
+    "Linux",
+    "FreeBSD",
+]:
+    config.unsupported = True
diff --git a/compiler-rt/test/instrumentor-tools/lit.site.cfg.py.in b/compiler-rt/test/instrumentor-tools/lit.site.cfg.py.in
new file mode 100644
index 0000000000000..8474c796f8eeb
--- /dev/null
+++ b/compiler-rt/test/instrumentor-tools/lit.site.cfg.py.in
@@ -0,0 +1,12 @@
+ at LIT_SITE_CFG_IN_HEADER@
+
+# Tool-specific config options.
+config.instrumentor_lit_binary_dir = "@INSTRUMENTOR_LIT_BINARY_DIR@"
+config.target_cflags = "@INSTRUMENTOR_TEST_TARGET_CFLAGS@"
+config.target_arch = "@INSTRUMENTOR_TEST_TARGET_ARCH@"
+
+# Load common config for all compiler-rt lit tests.
+lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")
+
+# Load tool-specific config that would do the real work.
+lit_config.load_config(config, "@INSTRUMENTOR_LIT_SOURCE_DIR@/lit.cfg.py")
diff --git a/compiler-rt/test/instrumentor-tools/simple_flops.c b/compiler-rt/test/instrumentor-tools/simple_flops.c
new file mode 100644
index 0000000000000..9434cb506f602
--- /dev/null
+++ b/compiler-rt/test/instrumentor-tools/simple_flops.c
@@ -0,0 +1,49 @@
+// Test basic FLOP counting functionality
+//
+// This test verifies that the FLOP counter correctly counts floating-point
+// operations in a simple program.
+//
+// RUN: %clangxx -O0 -g -mllvm -enable-instrumentor -mllvm -instrumentor-read-config-files=%config_dir/flop-counter/flop_counter_config.json %s -L%lib_dir -l%flop_counter_lib -o %t
+// RUN: %t | FileCheck %s
+//
+// TODO: For the correct values we need to track fmuladd calls too.
+//
+// CHECK: Total FLOPs: 400
+// CHECK: Single (float):  100
+// CHECK: Double (double): 300
+
+#include <stdio.h>
+
+// Simple function with known FLOP count
+float compute_float(float a, float b, float c) {
+  // 3 FLOPs: add, mul, add
+  return a + b * c;
+}
+
+double compute_double(double a, double b) {
+  // 4 FLOPs: mul, mul, add, div
+  return (a * a + b * b) / 2.0;
+}
+
+int main(void) {
+  float f1 = 1.0f, f2 = 2.0f, f3 = 3.0f;
+  double d1 = 4.0, d2 = 5.0;
+
+  // Call functions multiple times to get meaningful counts
+  float result_f = 0.0f;
+  for (int i = 0; i < 100; i++) {
+    result_f += compute_float(f1, f2, f3);
+  }
+
+  double result_d = 0.0;
+  for (int i = 0; i < 100; i++) {
+    result_d += compute_double(d1, d2);
+  }
+
+  // Prevent optimization from removing the computations
+  if (result_f > 0.0f && result_d > 0.0) {
+    printf("Computation complete\n");
+  }
+
+  return 0;
+}
diff --git a/compiler-rt/test/instrumentor-tools/vector_flops.cpp b/compiler-rt/test/instrumentor-tools/vector_flops.cpp
new file mode 100644
index 0000000000000..f5dcb625d4552
--- /dev/null
+++ b/compiler-rt/test/instrumentor-tools/vector_flops.cpp
@@ -0,0 +1,46 @@
+// Test FLOP counting with vector operations
+//
+// This test verifies that the FLOP counter correctly counts vector
+// floating-point operations.
+//
+// RUN: %clangxx -O2 -g -mllvm -enable-instrumentor -mllvm -instrumentor-read-config-files=%config_dir/flop-counter/flop_counter_config.json %s -L%lib_dir -l%flop_counter_lib -o %t
+// RUN: %t | FileCheck %s
+//
+// CHECK: Total FLOPs: 1000
+// CHECK: Vector FLOPs: 1000
+
+#include <cmath>
+#include <stdio.h>
+
+// Function using vector operations (if vectorized by the compiler)
+void vector_compute(float *a, float *b, float *c, int n) {
+  for (int i = 0; i < n; i++) {
+    c[i] = std::sqrt(a[i] * a[i] + b[i] * b[i]);
+  }
+}
+
+int main(void) {
+  const int N = 1000;
+  float a[N], b[N], c[N];
+
+  // Initialize arrays
+  for (int i = 0; i < N; i++) {
+    a[i] = (float)i;
+    b[i] = (float)(i + 1);
+  }
+
+  // Compute
+  vector_compute(a, b, c, N);
+
+  // Prevent optimization
+  float sum = 0.0f;
+  for (int i = 0; i < N; i++) {
+    sum += c[i];
+  }
+
+  if (sum > 0.0f) {
+    printf("Vector computation complete\n");
+  }
+
+  return 0;
+}



More information about the llvm-commits mailing list