[Mlir-commits] [mlir] [XeVM] Add translation for XeVM cache-control attributes. (PR #181856)

Md Abdullah Shahneous Bari llvmlistbot at llvm.org
Sun Mar 1 12:39:31 PST 2026


https://github.com/mshahneo updated https://github.com/llvm/llvm-project/pull/181856

>From e70596763b41f4c73e94d8f37f730367e101eade Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Tue, 17 Feb 2026 16:22:39 +0000
Subject: [PATCH 1/9] Add translation for cache-control attribute.

Use llvm.ptr.annotation to attach cache-control metadata to a pointer.
Each cache-control attribute produces its own annotation call; multiple
attributes are chained so every annotation sits on the same pointer.

This approach protects the metadata across optimizations.
---
 .../Dialect/XeVM/XeVMToLLVMIRTranslation.cpp  | 132 +++++++++++++++++-
 1 file changed, 129 insertions(+), 3 deletions(-)

diff --git a/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
index ba098aa5fde50..e2afd4e0bafc2 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
@@ -17,19 +17,145 @@
 #include "mlir/IR/Operation.h"
 #include "mlir/Target/LLVMIR/ModuleTranslation.h"
 
+#include "mlir/Support/LLVM.h"
 #include "llvm/ADT/TypeSwitch.h"
+#include "llvm/IR/ConstantRange.h"
 #include "llvm/IR/Constants.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/DebugLoc.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Metadata.h"
-
-#include "llvm/IR/ConstantRange.h"
-#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Module.h"
 #include "llvm/Support/raw_ostream.h"
 
 using namespace mlir;
 using namespace mlir::LLVM;
 
 namespace {
+//===----------------------------------------------------------------------===//
+// Utility functions for the translation
+//===----------------------------------------------------------------------===//
+// Extract the source filename from the debug location of \p Inst, if available.
+static std::string getSourceFilename(const llvm::Instruction *Inst) {
+  if (const llvm::DebugLoc &DbgLoc = Inst->getDebugLoc()) {
+    if (auto *Loc = DbgLoc.get()) {
+      if (llvm::DIFile *File = Loc->getFile()) {
+        if (!File->getDirectory().empty())
+          return (File->getDirectory() + "/" + File->getFilename()).str();
+        return File->getFilename().str();
+      }
+    }
+  }
+  return "";
+}
+
+// Build one cache-control payload string per attribute.
+//
+// Each mlir::Attribute is expected to be an ArrayAttr of (at least) 3
+// IntegerAttr values: [SPIR-V token number of that attribute, value for L1
+// cache, value for L3 cache].
+//
+// A single entry produces a string that appears in LLVM IR as:
+//   {6442:\220,1\22}\00
+static llvm::SmallVector<std::string>
+buildCacheControlPayloads(llvm::ArrayRef<mlir::Attribute> Attrs) {
+  llvm::SmallVector<std::string> Payloads;
+  llvm::StringMap<bool> Seen;
+
+  for (mlir::Attribute A : Attrs) {
+    auto Arr = mlir::dyn_cast<mlir::ArrayAttr>(A);
+    if (!Arr)
+      continue;
+
+    auto Vals = Arr.getValue();
+    // Assert that the attribute has exactly 3 integer values: [SPIR-V token, L1
+    // value, L3 value].
+    llvm::assert(Vals.size() == 3 &&
+                 "Expected 3 integer values in cache control attribute.");
+
+    auto FirstAttr = mlir::dyn_cast<mlir::IntegerAttr>(Vals[0]);
+    auto SecondAttr = mlir::dyn_cast<mlir::IntegerAttr>(Vals[1]);
+    auto ThirdAttr = mlir::dyn_cast<mlir::IntegerAttr>(Vals[2]);
+
+    if (!FirstAttr || !SecondAttr || !ThirdAttr)
+      continue;
+
+    uint64_t First = FirstAttr.getValue().getZExtValue();
+    uint64_t Second = SecondAttr.getValue().getZExtValue();
+    uint64_t Third = ThirdAttr.getValue().getZExtValue();
+
+    // Produce: {FIRST:\22SECOND,THIRD\22}
+    // where \22 is the 0x22 byte ("), which LLVM IR prints as \22.
+    // The null terminator (\00) is added by ConstantDataArray::getString.
+    std::string Entry;
+    Entry.push_back('{');
+    Entry += std::to_string(First);
+    Entry.push_back(':');
+    Entry.push_back('"'); // 0x22 → prints as \22 in LLVM IR
+    Entry += std::to_string(Second);
+    Entry.push_back(',');
+    Entry += std::to_string(Third);
+    Entry.push_back('"'); // 0x22 → prints as \22 in LLVM IR
+    Entry.push_back('}');
+
+    // Skip duplicates — identical annotations on the same pointer are
+    // redundant.
+    if (!Seen.insert({Entry, true}).second)
+      continue;
+
+    Payloads.push_back(std::move(Entry));
+  }
+
+  return Payloads;
+}
+
+// Create (or reuse) an addrspace(1) global string in section "llvm.metadata"
+// and return an i8 addrspace(1)* pointer to its first element.
+//
+// A module-level StringMap caches previously created globals so that
+// identical strings are not duplicated.
+static llvm::Value *getOrCreateAS1MetadataStringPtr(llvm::Module &M,
+                                                    llvm::StringRef Str,
+                                                    llvm::StringRef NameHint) {
+  // Per-module cache keyed on the string content.
+  static llvm::DenseMap<llvm::Module *, llvm::StringMap<llvm::Value *>> Cache;
+  auto &ModuleCache = Cache[&M];
+
+  auto It = ModuleCache.find(Str);
+  if (It != ModuleCache.end())
+    return It->second;
+
+  llvm::LLVMContext &Ctx = M.getContext();
+
+  llvm::Constant *Arr =
+      llvm::ConstantDataArray::getString(Ctx, Str, /*AddNull=*/true);
+  auto *ArrTy = llvm::cast<llvm::ArrayType>(Arr->getType());
+
+  auto *GV = new llvm::GlobalVariable(
+      M, ArrTy,
+      /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, Arr, NameHint,
+      /*InsertBefore=*/nullptr, llvm::GlobalValue::NotThreadLocal,
+      /*AddressSpace=*/1);
+
+  GV->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
+  GV->setSection("llvm.metadata");
+  GV->setAlignment(llvm::Align(1));
+
+  llvm::Constant *Zero = llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx), 0);
+  llvm::Constant *Idxs[] = {Zero, Zero};
+
+  llvm::Constant *GEP =
+      llvm::ConstantExpr::getInBoundsGetElementPtr(ArrTy, GV, Idxs);
+
+  auto *AS1PtrTy = llvm::PointerType::get(Ctx, /*AddrSpace=*/1);
+  llvm::Value *Result = llvm::ConstantExpr::getBitCast(GEP, AS1PtrTy);
+
+  ModuleCache[Str] = Result;
+  return Result;
+}
+
 /// Implementation of the dialect interface that converts operations belonging
 /// to the XeVM dialect to LLVM IR.
 class XeVMDialectLLVMIRTranslationInterface

>From 34152c2669c2bd10cf039c9e31afb04a37038116 Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Tue, 17 Feb 2026 16:39:18 +0000
Subject: [PATCH 2/9] Fix a small error.

---
 .../Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp    | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
index e2afd4e0bafc2..eb1ea9a5b13dc 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
@@ -72,8 +72,8 @@ buildCacheControlPayloads(llvm::ArrayRef<mlir::Attribute> Attrs) {
     auto Vals = Arr.getValue();
     // Assert that the attribute has exactly 3 integer values: [SPIR-V token, L1
     // value, L3 value].
-    llvm::assert(Vals.size() == 3 &&
-                 "Expected 3 integer values in cache control attribute.");
+    assert(Vals.size() == 3 &&
+           "Expected 3 integer values in cache control attribute.");
 
     auto FirstAttr = mlir::dyn_cast<mlir::IntegerAttr>(Vals[0]);
     auto SecondAttr = mlir::dyn_cast<mlir::IntegerAttr>(Vals[1]);

>From 62a6fd353963acf3d305e240a74b04ba65bb0e45 Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Mon, 23 Feb 2026 16:45:38 +0000
Subject: [PATCH 3/9] Update address space value of the intrinsic to use base
 Ptr's addressspace.

Modify some naming and comment to be in sync with MLIR-style.
---
 .../Dialect/XeVM/XeVMToLLVMIRTranslation.cpp  | 235 +++++++++++-------
 1 file changed, 150 insertions(+), 85 deletions(-)

diff --git a/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
index eb1ea9a5b13dc..0d76a71f866a9 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
@@ -37,14 +37,14 @@ namespace {
 //===----------------------------------------------------------------------===//
 // Utility functions for the translation
 //===----------------------------------------------------------------------===//
-// Extract the source filename from the debug location of \p Inst, if available.
-static std::string getSourceFilename(const llvm::Instruction *Inst) {
-  if (const llvm::DebugLoc &DbgLoc = Inst->getDebugLoc()) {
-    if (auto *Loc = DbgLoc.get()) {
-      if (llvm::DIFile *File = Loc->getFile()) {
-        if (!File->getDirectory().empty())
-          return (File->getDirectory() + "/" + File->getFilename()).str();
-        return File->getFilename().str();
+// Extract the source filename from the debug location of \p inst, if available.
+static std::string getSourceFilename(const llvm::Instruction *inst) {
+  if (const llvm::DebugLoc &dbgLoc = inst->getDebugLoc()) {
+    if (auto *loc = dbgLoc.get()) {
+      if (llvm::DIFile *file = loc->getFile()) {
+        if (!file->getDirectory().empty())
+          return (file->getDirectory() + "/" + file->getFilename()).str();
+        return file->getFilename().str();
       }
     }
   }
@@ -60,55 +60,55 @@ static std::string getSourceFilename(const llvm::Instruction *Inst) {
 // A single entry produces a string that appears in LLVM IR as:
 //   {6442:\220,1\22}\00
 static llvm::SmallVector<std::string>
-buildCacheControlPayloads(llvm::ArrayRef<mlir::Attribute> Attrs) {
-  llvm::SmallVector<std::string> Payloads;
-  llvm::StringMap<bool> Seen;
+buildCacheControlPayloads(llvm::ArrayRef<mlir::Attribute> attrs) {
+  llvm::SmallVector<std::string> payloads;
+  llvm::StringMap<bool> seen;
 
-  for (mlir::Attribute A : Attrs) {
-    auto Arr = mlir::dyn_cast<mlir::ArrayAttr>(A);
-    if (!Arr)
+  for (mlir::Attribute a : attrs) {
+    auto arr = mlir::dyn_cast<mlir::ArrayAttr>(a);
+    if (!arr)
       continue;
 
-    auto Vals = Arr.getValue();
+    auto vals = arr.getValue();
     // Assert that the attribute has exactly 3 integer values: [SPIR-V token, L1
     // value, L3 value].
-    assert(Vals.size() == 3 &&
+    assert(vals.size() == 3 &&
            "Expected 3 integer values in cache control attribute.");
 
-    auto FirstAttr = mlir::dyn_cast<mlir::IntegerAttr>(Vals[0]);
-    auto SecondAttr = mlir::dyn_cast<mlir::IntegerAttr>(Vals[1]);
-    auto ThirdAttr = mlir::dyn_cast<mlir::IntegerAttr>(Vals[2]);
+    auto firstAttr = mlir::dyn_cast<mlir::IntegerAttr>(vals[0]);
+    auto secondAttr = mlir::dyn_cast<mlir::IntegerAttr>(vals[1]);
+    auto thirdAttr = mlir::dyn_cast<mlir::IntegerAttr>(vals[2]);
 
-    if (!FirstAttr || !SecondAttr || !ThirdAttr)
+    if (!firstAttr || !secondAttr || !thirdAttr)
       continue;
 
-    uint64_t First = FirstAttr.getValue().getZExtValue();
-    uint64_t Second = SecondAttr.getValue().getZExtValue();
-    uint64_t Third = ThirdAttr.getValue().getZExtValue();
+    uint64_t first = firstAttr.getValue().getZExtValue();
+    uint64_t second = secondAttr.getValue().getZExtValue();
+    uint64_t third = thirdAttr.getValue().getZExtValue();
 
     // Produce: {FIRST:\22SECOND,THIRD\22}
     // where \22 is the 0x22 byte ("), which LLVM IR prints as \22.
     // The null terminator (\00) is added by ConstantDataArray::getString.
-    std::string Entry;
-    Entry.push_back('{');
-    Entry += std::to_string(First);
-    Entry.push_back(':');
-    Entry.push_back('"'); // 0x22 → prints as \22 in LLVM IR
-    Entry += std::to_string(Second);
-    Entry.push_back(',');
-    Entry += std::to_string(Third);
-    Entry.push_back('"'); // 0x22 → prints as \22 in LLVM IR
-    Entry.push_back('}');
+    std::string entry;
+    entry.push_back('{');
+    entry += std::to_string(first);
+    entry.push_back(':');
+    entry.push_back('"'); // 0x22 → prints as \22 in LLVM IR
+    entry += std::to_string(second);
+    entry.push_back(',');
+    entry += std::to_string(third);
+    entry.push_back('"'); // 0x22 → prints as \22 in LLVM IR
+    entry.push_back('}');
 
     // Skip duplicates — identical annotations on the same pointer are
     // redundant.
-    if (!Seen.insert({Entry, true}).second)
+    if (!seen.insert({entry, true}).second)
       continue;
 
-    Payloads.push_back(std::move(Entry));
+    payloads.push_back(std::move(entry));
   }
 
-  return Payloads;
+  return payloads;
 }
 
 // Create (or reuse) an addrspace(1) global string in section "llvm.metadata"
@@ -116,46 +116,45 @@ buildCacheControlPayloads(llvm::ArrayRef<mlir::Attribute> Attrs) {
 //
 // A module-level StringMap caches previously created globals so that
 // identical strings are not duplicated.
-static llvm::Value *getOrCreateAS1MetadataStringPtr(llvm::Module &M,
-                                                    llvm::StringRef Str,
-                                                    llvm::StringRef NameHint) {
+static llvm::Value *getOrCreateAS1MetadataStringPtr(llvm::Module &module,
+                                                    llvm::StringRef str,
+                                                    llvm::StringRef nameHint) {
   // Per-module cache keyed on the string content.
-  static llvm::DenseMap<llvm::Module *, llvm::StringMap<llvm::Value *>> Cache;
-  auto &ModuleCache = Cache[&M];
+  static llvm::DenseMap<llvm::Module *, llvm::StringMap<llvm::Value *>> cache;
+  auto &moduleCache = cache[&module];
 
-  auto It = ModuleCache.find(Str);
-  if (It != ModuleCache.end())
-    return It->second;
+  auto it = moduleCache.find(str);
+  if (it != moduleCache.end())
+    return it->second;
 
-  llvm::LLVMContext &Ctx = M.getContext();
+  llvm::LLVMContext &ctx = module.getContext();
 
-  llvm::Constant *Arr =
-      llvm::ConstantDataArray::getString(Ctx, Str, /*AddNull=*/true);
-  auto *ArrTy = llvm::cast<llvm::ArrayType>(Arr->getType());
+  llvm::Constant *arr =
+      llvm::ConstantDataArray::getString(ctx, str, /*AddNull=*/true);
+  auto *arrTy = llvm::cast<llvm::ArrayType>(arr->getType());
 
-  auto *GV = new llvm::GlobalVariable(
-      M, ArrTy,
-      /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, Arr, NameHint,
+  auto *gv = new llvm::GlobalVariable(
+      module, arrTy,
+      /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, arr, nameHint,
       /*InsertBefore=*/nullptr, llvm::GlobalValue::NotThreadLocal,
       /*AddressSpace=*/1);
 
-  GV->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
-  GV->setSection("llvm.metadata");
-  GV->setAlignment(llvm::Align(1));
+  gv->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
+  gv->setSection("llvm.metadata");
+  gv->setAlignment(llvm::Align(1));
 
-  llvm::Constant *Zero = llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx), 0);
-  llvm::Constant *Idxs[] = {Zero, Zero};
+  llvm::Constant *zero = llvm::ConstantInt::get(llvm::Type::getInt32Ty(ctx), 0);
+  llvm::Constant *idxs[] = {zero, zero};
 
-  llvm::Constant *GEP =
-      llvm::ConstantExpr::getInBoundsGetElementPtr(ArrTy, GV, Idxs);
+  llvm::Constant *gep =
+      llvm::ConstantExpr::getInBoundsGetElementPtr(arrTy, gv, idxs);
 
-  auto *AS1PtrTy = llvm::PointerType::get(Ctx, /*AddrSpace=*/1);
-  llvm::Value *Result = llvm::ConstantExpr::getBitCast(GEP, AS1PtrTy);
+  auto *as1PtrTy = llvm::PointerType::get(ctx, /*AddrSpace=*/1);
+  llvm::Value *result = llvm::ConstantExpr::getBitCast(gep, as1PtrTy);
 
-  ModuleCache[Str] = Result;
-  return Result;
+  moduleCache[str] = result;
+  return result;
 }
-
 /// Implementation of the dialect interface that converts operations belonging
 /// to the XeVM dialect to LLVM IR.
 class XeVMDialectLLVMIRTranslationInterface
@@ -185,28 +184,94 @@ class XeVMDialectLLVMIRTranslationInterface
   }
 
 private:
-  static LogicalResult handleDecorationCacheControl(llvm::Instruction *inst,
-                                                    ArrayRef<Attribute> attrs) {
-    SmallVector<llvm::Metadata *> decorations;
-    llvm::LLVMContext &ctx = inst->getContext();
-    llvm::Type *i32Ty = llvm::IntegerType::getInt32Ty(ctx);
-    llvm::transform(
-        attrs, std::back_inserter(decorations),
-        [&ctx, i32Ty](Attribute attr) -> llvm::Metadata * {
-          auto valuesArray = dyn_cast<ArrayAttr>(attr).getValue();
-          std::array<llvm::Metadata *, 3> metadata;
-          llvm::transform(
-              valuesArray, metadata.begin(), [i32Ty](Attribute valueAttr) {
-                return llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
-                    i32Ty, cast<IntegerAttr>(valueAttr).getValue()));
-              });
-          return llvm::MDNode::get(ctx, metadata);
-        });
-    constexpr llvm::StringLiteral decorationCacheControlMDName =
-        "spirv.DecorationCacheControlINTEL";
-    inst->setMetadata(decorationCacheControlMDName,
-                      llvm::MDNode::get(ctx, decorations));
-    return success();
+  // Attach cache-control metadata to the pointer operand of \p inst.
+  //
+  // Each attribute in \p attrs becomes a separate
+  // llvm.ptr.annotation.p<AS>.p1 call, where AS is the address space of the
+  // input pointer.  When there are multiple attributes the calls are chained:
+  // the result of the first annotation is fed as the pointer into the second,
+  // and so on, so that every annotation is associated with the same logical
+  // pointer.
+  //
+  // Example:
+  //   ; For a ptr addrspace(1) operand -> llvm.ptr.annotation.p1.p1
+  //   %a1 = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(
+  //              ptr addrspace(1) %ptr, ... "{6442:\220,1\22}" ...)
+  //   %a2 = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(
+  //              ptr addrspace(1) %a1,  ... "{6442:\221,1\22}" ...)
+  //   ; instruction now uses %a2
+  //
+  //   ; For a ptr addrspace(0) operand -> llvm.ptr.annotation.p0.p1
+  //   %a1 = call ptr @llvm.ptr.annotation.p0.p1(
+  //              ptr %ptr, ... "{6442:\220,1\22}" ...)
+  static mlir::LogicalResult
+  handleDecorationCacheControl(llvm::Instruction *inst,
+                               llvm::ArrayRef<mlir::Attribute> attrs) {
+    llvm::Module *module = inst->getModule();
+    if (!module)
+      return mlir::failure();
+
+    // @TODO: Expect the pointer to be the first operand, but this is not a
+    // strict requirement of the protocol; it can be adjusted if needed.
+    constexpr unsigned ptrIdx = 0;
+    llvm::Value *ptr = inst->getOperand(ptrIdx);
+
+    if (!ptr || !ptr->getType()->isPointerTy())
+      return mlir::success();
+
+    auto *ptrTy = llvm::cast<llvm::PointerType>(ptr->getType());
+
+    llvm::SmallVector<std::string> payloads = buildCacheControlPayloads(attrs);
+    if (payloads.empty())
+      return mlir::success();
+
+    llvm::LLVMContext &ctx = module->getContext();
+    llvm::IRBuilder<> builder(inst);
+    auto *as1PtrTy = llvm::PointerType::get(ctx, 1);
+
+    // Shared across all annotations for this instruction.
+    std::string fileName = getSourceFilename(inst);
+    llvm::Value *fileStrAS1 = getOrCreateAS1MetadataStringPtr(
+        *module,
+        fileName.empty() ? llvm::StringRef("") : llvm::StringRef(fileName),
+        ".str.file");
+
+    unsigned line = 0;
+    if (llvm::DILocation *loc = inst->getDebugLoc().get())
+      line = loc->getLine();
+    llvm::Value *lineVal =
+        llvm::ConstantInt::get(llvm::Type::getInt32Ty(ctx), line);
+
+    llvm::Value *nullAS1 =
+        llvm::ConstantPointerNull::get(llvm::cast<llvm::PointerType>(as1PtrTy));
+
+    unsigned ptrAS = ptrTy->getAddressSpace();
+
+    llvm::FunctionType *fTy = llvm::FunctionType::get(
+        ptrTy,
+        {ptrTy, as1PtrTy, as1PtrTy, llvm::Type::getInt32Ty(ctx), as1PtrTy},
+        /*isVarArg=*/false);
+
+    // llvm.ptr.annotation.p<ptrAS>.p1
+    std::string intrinsicName =
+        "llvm.ptr.annotation.p" + std::to_string(ptrAS) + ".p1";
+    llvm::FunctionCallee callee =
+        module->getOrInsertFunction(intrinsicName, fTy);
+
+    // Chain: each annotation takes the result of the previous one as its
+    // pointer operand.
+    llvm::Value *curPtr = ptr;
+    for (const std::string &payload : payloads) {
+      llvm::Value *annStrAS1 = getOrCreateAS1MetadataStringPtr(
+          *module, payload, ".str.cachecontrol");
+
+      curPtr = builder.CreateCall(
+          callee, {curPtr, annStrAS1, fileStrAS1, lineVal, nullAS1},
+          "annotated.ptr");
+    }
+
+    inst->setOperand(ptrIdx, curPtr);
+    return mlir::success();
   }
 };
 } // namespace

>From bf87ec5a6d05bb7e639faa61deb6b30ef607162b Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Mon, 23 Feb 2026 17:06:27 +0000
Subject: [PATCH 4/9] Update the cache control values.

Only use L1 and L3 values, and ignore L2, since Intel GPUs only
have L1 and L3.
---
 .../Dialect/XeVM/XeVMToLLVMIRTranslation.cpp  | 20 +++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
index 0d76a71f866a9..8a05f24b659d0 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
@@ -70,14 +70,18 @@ buildCacheControlPayloads(llvm::ArrayRef<mlir::Attribute> attrs) {
       continue;
 
     auto vals = arr.getValue();
-    // Assert that the attribute has exactly 3 integer values: [SPIR-V token, L1
-    // value, L3 value].
-    assert(vals.size() == 3 &&
-           "Expected 3 integer values in cache control attribute.");
-
-    auto firstAttr = mlir::dyn_cast<mlir::IntegerAttr>(vals[0]);
-    auto secondAttr = mlir::dyn_cast<mlir::IntegerAttr>(vals[1]);
-    auto thirdAttr = mlir::dyn_cast<mlir::IntegerAttr>(vals[2]);
+    // Assert that the attribute has at most 4 integer values: [SPIR-V token, L1
+    // value, L3 value, optional extra value].
+    assert(vals.size() <= 4 &&
+           "Expected at most 4 integer values in cache control attribute.");
+
+    //  Although the caching value is allowed for 3 levels (L1, L2, L3), current
+    //  Intel GPUs only have L1, and L3. So we only use L1 and L3 values. The L2
+    //  value is ignored.
+    auto firstAttr = mlir::dyn_cast<mlir::IntegerAttr>(vals[0]); // Token number
+    auto secondAttr = mlir::dyn_cast<mlir::IntegerAttr>(vals[1]); // L1 value
+    // L2 value is ignored: vals[2]
+    auto thirdAttr = mlir::dyn_cast<mlir::IntegerAttr>(vals[3]); // L3 value
 
     if (!firstAttr || !secondAttr || !thirdAttr)
       continue;

>From 1596734a2a653d8e6fc10cd7cf93c2edc6f1e4fc Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Mon, 23 Feb 2026 20:09:15 +0000
Subject: [PATCH 5/9] Add handling for llvm.store.

All the instructions that can have the cache_control metadata,
their pointer argument that needs to be decorated are at operand
index 0 (i.e., first operand). Only in `llvm.store`, it is at
operand index 1 (i.e., second operand). This adds necessary
handling for that.
---
 .../LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
index 8a05f24b659d0..ecb10f35bcb27 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
@@ -215,9 +215,16 @@ class XeVMDialectLLVMIRTranslationInterface
     if (!module)
       return mlir::failure();
 
-    // @TODO: Expect the pointer to be the first operand, but this is not a
-    // strict requirement of the protocol; it can be adjusted if needed.
-    constexpr unsigned ptrIdx = 0;
+    // @TODO: Currently we expect the pointer to be the first operand for all
+    // instructions (which can have cache control attributes) except llvm.store.
+    // but this is not a strict requirement of the protocol; it can be adjusted
+    // if needed.
+    unsigned ptrIdx = 0;
+
+    // Check if the instruction is a LLVM Store instruction, which has the
+    // pointer as the second operand.
+    if (llvm::isa<llvm::StoreInst>(inst))
+      ptrIdx = 1;
     llvm::Value *ptr = inst->getOperand(ptrIdx);
 
     if (!ptr || !ptr->getType()->isPointerTy())

>From 77e38fa2d66c6d1ba63ad0570315f506fadeda59 Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Mon, 23 Feb 2026 20:14:08 +0000
Subject: [PATCH 6/9] Update the test case.

---
 mlir/test/Target/LLVMIR/xevm.mlir | 57 +++++++++++++++----------------
 1 file changed, 27 insertions(+), 30 deletions(-)

diff --git a/mlir/test/Target/LLVMIR/xevm.mlir b/mlir/test/Target/LLVMIR/xevm.mlir
index fcb0d6990ce8f..0d3562a1d4f17 100644
--- a/mlir/test/Target/LLVMIR/xevm.mlir
+++ b/mlir/test/Target/LLVMIR/xevm.mlir
@@ -2,52 +2,49 @@
 
 module {
   llvm.func spir_funccc @_Z8prefetchPU3AS1Kcm(!llvm.ptr<1>, i64)
+
   llvm.func @prefetch(%arg0: !llvm.ptr<1>) {
     %0 = llvm.mlir.constant(1 : i64) : i64
-    // CHECK-LABEL: call spir_func void @_Z8prefetchPU3AS1Kcm
-    // CHECK-SAME: !spirv.DecorationCacheControlINTEL ![[DECO1:.*]]
-    llvm.call spir_funccc @_Z8prefetchPU3AS1Kcm(%arg0, %0)
-      {function_type = !llvm.func<void (ptr<1>, i64)>, linkage = #llvm.linkage<external>,
-       no_unwind, sym_name = "_Z8prefetchPU3AS1Kcm", visibility_ = 0 : i64,
-       xevm.DecorationCacheControl = [[6442 : i32, 0 : i32, 1 : i32], [6442 : i32, 1 : i32, 1 : i32]]}
-      : (!llvm.ptr<1>, i64) -> ()
+    // CHECK: @[[CACHECONTROL:.*]] = private unnamed_addr addrspace(1) constant [13 x i8] c"{6442:\220,0\22}\00", section "llvm.metadata"
+    // CHECK: @[[CACHECONTROL1:.*]] = private unnamed_addr addrspace(1) constant [13 x i8] c"{6442:\221,0\22}\00", section "llvm.metadata"
+    // CHECK-LABEL: define void @prefetch
+    // CHECK-SAME: ptr addrspace(1) %[[ARG0:.*]])
+    // CHECK: %[[ANNOTATED:.*]] = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(ptr addrspace(1) %[[ARG0]], ptr addrspace(1) @[[CACHECONTROL]],
+    // CHECK: %[[ANNOTATED1:.*]] = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(ptr addrspace(1) %[[ANNOTATED]], ptr addrspace(1) @[[CACHECONTROL1]],
+    // CHECK: call spir_func void @_Z8prefetchPU3AS1Kcm(ptr addrspace(1) %[[ANNOTATED1]], i64 1)
+    llvm.call spir_funccc @_Z8prefetchPU3AS1Kcm(%arg0, %0) {function_type = !llvm.func<void (ptr<1>, i64)>, linkage = #llvm.linkage<external>, no_unwind, sym_name = "_Z8prefetchPU3AS1Kcm", visibility_ = 0 : i64, xevm.DecorationCacheControl = [[6442 : i32, 0 : i32, 1 : i32, 0 : i32], [6442 : i32, 1 : i32, 1 : i32, 0 : i32]]} : (!llvm.ptr<1>, i64) -> ()
     llvm.return
   }
 }
 
-// CHECK: ![[DECO1]] = !{![[DECO2:.*]], ![[DECO3:.*]]}
-// CHECK: ![[DECO2]] = !{i32 6442, i32 0, i32 1}
-// CHECK: ![[DECO3]] = !{i32 6442, i32 1, i32 1}
-
 // -----
+
 module {
-  // CHECK-LABEL: define i32 @load(ptr addrspace(1)
-  // CHECK-SAME: %[[ARG0:.*]]) {
+  // CHECK: @[[CACHECONTROL:.*]] = private unnamed_addr addrspace(1) constant [13 x i8] c"{6442:\220,0\22}\00", section "llvm.metadata"
+  // CHECK: @[[CACHECONTROL1:.*]] = private unnamed_addr addrspace(1) constant [13 x i8] c"{6442:\221,0\22}\00", section "llvm.metadata"
+  // CHECK-LABEL: define i32 @load
+  // CHECK-SAME: ptr addrspace(1) %[[ARG0:.*]])
   llvm.func @load(%arg0: !llvm.ptr<1>) -> i32 {
-    // CHECK: load i32, ptr addrspace(1) %[[ARG0]], align 4,
-    // CHECK-SAME: !spirv.DecorationCacheControlINTEL ![[DECO1:.*]]
-    %0 = llvm.load %arg0 {xevm.DecorationCacheControl = [[6442 : i32, 0 : i32, 1 : i32], [6442 : i32, 1 : i32, 1 : i32]]} : !llvm.ptr<1> -> i32
+    // CHECK: %[[ANNOTATED:.*]] = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(ptr addrspace(1) %[[ARG0]], ptr addrspace(1) @[[CACHECONTROL]],
+    // CHECK: %[[ANNOTATED1:.*]] = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(ptr addrspace(1) %[[ANNOTATED]], ptr addrspace(1) @[[CACHECONTROL1]],
+    // CHECK: load i32, ptr addrspace(1) %[[ANNOTATED1]], align 4
+    %0 = llvm.load %arg0 {xevm.DecorationCacheControl = [[6442 : i32, 0 : i32, 1 : i32, 0 : i32], [6442 : i32, 1 : i32, 1 : i32, 0 : i32]]} : !llvm.ptr<1> -> i32
     llvm.return %0 : i32
   }
 }
 
-// CHECK: ![[DECO1]] = !{![[DECO2:.*]], ![[DECO3:.*]]}
-// CHECK: ![[DECO2]] = !{i32 6442, i32 0, i32 1}
-// CHECK: ![[DECO3]] = !{i32 6442, i32 1, i32 1}
-
 // -----
+
 module {
-  // CHECK-LABEL: define void @store(ptr addrspace(1)
-  // CHECK-SAME: %[[ARG0:.*]], i32 %[[ARG1:.*]]) {
+  // CHECK: @[[CACHECONTROL:.*]] = private unnamed_addr addrspace(1) constant [13 x i8] c"{6443:\220,0\22}\00", section "llvm.metadata"
+  // CHECK: @[[CACHECONTROL1:.*]] = private unnamed_addr addrspace(1) constant [13 x i8] c"{6443:\221,0\22}\00", section "llvm.metadata"
+  // CHECK-LABEL: define void @store
+  // CHECK-SAME: ptr addrspace(1) %[[ARG0:.*]], i32 %[[ARG1:.*]])
   llvm.func @store(%arg0: !llvm.ptr<1>, %arg1: i32) {
-    // CHECK: store i32 %[[ARG1]], ptr addrspace(1) %[[ARG0]], align 4,
-    // CHECK-SAME: !spirv.DecorationCacheControlINTEL ![[DECO1:.*]]
-    llvm.store %arg1, %arg0 {xevm.DecorationCacheControl = [[6443 : i32, 0 : i32, 2 : i32], [6443 : i32, 1 : i32, 2 : i32]]} : i32, !llvm.ptr<1>
+    // CHECK: %[[ANNOTATED:.*]] = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(ptr addrspace(1) %[[ARG0]], ptr addrspace(1) @[[CACHECONTROL]],
+    // CHECK: %[[ANNOTATED1:.*]] = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(ptr addrspace(1) %[[ANNOTATED]], ptr addrspace(1) @[[CACHECONTROL1]],
+    // CHECK: store i32 %[[ARG1]], ptr addrspace(1) %[[ANNOTATED1]], align 4
+    llvm.store %arg1, %arg0 {xevm.DecorationCacheControl = [[6443 : i32, 0 : i32, 2 : i32, 0 : i32], [6443 : i32, 1 : i32, 2 : i32, 0 : i32]]} : i32, !llvm.ptr<1>
     llvm.return
   }
 }
-
-// CHECK: ![[DECO1]] = !{![[DECO2:.*]], ![[DECO3:.*]]}
-// CHECK: ![[DECO2]] = !{i32 6443, i32 0, i32 2}
-// CHECK: ![[DECO3]] = !{i32 6443, i32 1, i32 2}
-

>From a355afdb0ce10270d04a8ed4a258fb96410e3268 Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Sun, 1 Mar 2026 20:34:45 +0000
Subject: [PATCH 7/9] Add cache_control handling at the XeVMToLLVM pass.

This keeps the handling of the cache_control decoration strictly
in MLIR level.
---
 mlir/lib/Conversion/XeVMToLLVM/XeVMToLLVM.cpp | 262 ++++++++++++++++--
 .../Conversion/XeVMToLLVM/xevm-to-llvm.mlir   | 128 ++++++---
 2 files changed, 330 insertions(+), 60 deletions(-)

diff --git a/mlir/lib/Conversion/XeVMToLLVM/XeVMToLLVM.cpp b/mlir/lib/Conversion/XeVMToLLVM/XeVMToLLVM.cpp
index e7537ba1f0a79..2f4c004baa7a7 100644
--- a/mlir/lib/Conversion/XeVMToLLVM/XeVMToLLVM.cpp
+++ b/mlir/lib/Conversion/XeVMToLLVM/XeVMToLLVM.cpp
@@ -263,6 +263,204 @@ getCacheControlMetadata(ConversionPatternRewriter &rewriter, OpType op) {
   return rewriter.getArrayAttr(combinedAttrs);
 }
 
+//===----------------------------------------------------------------------===//
+// Cache control annotation utilities
+//
+// Instead of attaching cache control as MLIR attributes and handling them
+// during LLVM translation, we directly emit llvm.intr.ptr.annotation op MLIR.
+//===----------------------------------------------------------------------===//
+
+/// Build one cache-control payload string per attribute.
+///
+/// Each Attribute is expected to be an ArrayAttr of 3 IntegerAttr values:
+///   [SPIR-V decoration token, cache level, cache control value]
+///
+/// A single entry produces a string like:  {6442:"0,1"}
+/// where the quote characters (0x22) will appear as \22 in LLVM IR textual
+/// form.
+static SmallVector<std::string>
+buildCacheControlPayloads(ArrayRef<Attribute> attrs) {
+  SmallVector<std::string> payloads;
+  llvm::StringMap<bool> seen;
+
+  for (Attribute a : attrs) {
+    auto arr = dyn_cast<ArrayAttr>(a);
+    if (!arr)
+      continue;
+
+    auto vals = arr.getValue();
+    assert(vals.size() == 3 &&
+           "Expected exactly 3 integer values (Token, CacheLevel, "
+           "ControlValue) in cache control attribute.");
+
+    auto tokenAttr = dyn_cast<IntegerAttr>(vals[0]);
+    auto secondAttr = dyn_cast<IntegerAttr>(vals[1]);
+    auto thirdAttr = dyn_cast<IntegerAttr>(vals[2]);
+
+    if (!tokenAttr || !secondAttr || !thirdAttr)
+      continue;
+
+    // Produce: {SPIR-V decoration token:"L1 cache control,L3 cache control"}
+    // The quote char (0x22) is embedded literally; LLVM IR prints it as \22.
+    std::string entry = llvm::formatv("'{'{0}:\"{1},{2}\"'}'",
+                                      tokenAttr.getValue().getZExtValue(),
+                                      secondAttr.getValue().getZExtValue(),
+                                      thirdAttr.getValue().getZExtValue());
+
+    // Deduplicate identical annotations.
+    if (!seen.insert({entry, true}).second)
+      continue;
+
+    payloads.push_back(std::move(entry));
+  }
+  return payloads;
+}
+/// Counter for generating unique global variable names.
+static std::atomic<uint64_t> globalNameCounter{0};
+
+/// Get or create a global metadata string and return a !llvm.ptr<1> value
+/// pointing to it. The AddressOfOp is created at the current rewriter
+/// insertion point; the GlobalOp is created at the module start.
+static Value createMetadataStringPtr(ConversionPatternRewriter &rewriter,
+                                     Operation *moduleOp, Location loc,
+                                     StringRef value, StringRef nameHint) {
+  // Build null-terminated string.
+  std::string strWithNull = value.str();
+  strWithNull.push_back('\0');
+  StringRef strRef(strWithNull.data(), strWithNull.size());
+
+  auto as1PtrTy = LLVM::LLVMPointerType::get(rewriter.getContext(), 1);
+
+  // Search for an existing global with the same content.
+  for (auto &op : moduleOp->getRegion(0).front()) {
+    if (auto existingGlobal = dyn_cast<LLVM::GlobalOp>(&op)) {
+      if (!existingGlobal.getSection() ||
+          *existingGlobal.getSection() != "llvm.metadata")
+        continue;
+      if (auto strAttr =
+              dyn_cast_or_null<StringAttr>(existingGlobal.getValueOrNull())) {
+        if (strAttr.getValue() == strRef) {
+          return LLVM::AddressOfOp::create(rewriter, loc, as1PtrTy,
+                                           existingGlobal.getSymName());
+        }
+      }
+    }
+  }
+
+  // Create new global at module start.
+  auto i8Type = rewriter.getI8Type();
+  auto arrayType = LLVM::LLVMArrayType::get(i8Type, strWithNull.size());
+  std::string globalName =
+      llvm::formatv("{0}.{1}", nameHint,
+                    globalNameCounter.fetch_add(1, std::memory_order_relaxed))
+          .str();
+
+  {
+    OpBuilder::InsertionGuard guard(rewriter);
+    rewriter.setInsertionPointToStart(&moduleOp->getRegion(0).front());
+
+    auto globalOp =
+        LLVM::GlobalOp::create(rewriter, loc, arrayType,
+                               /*isConstant=*/true, LLVM::Linkage::Private,
+                               globalName, rewriter.getStringAttr(strRef));
+    globalOp.setSection(StringRef("llvm.metadata"));
+    globalOp.setUnnamedAddr(LLVM::UnnamedAddr::Global);
+    globalOp.setAlignment(1);
+    globalOp.setAddrSpace(1);
+  }
+  // InsertionGuard restores the original insertion point here.
+
+  return LLVM::AddressOfOp::create(rewriter, loc, as1PtrTy, globalName);
+}
+
+/// Annotate a pointer value with cache control metadata by emitting chained
+/// `llvm.intr.ptr.annotation` ops (LLVM::PtrAnnotation).
+///
+/// This is the MLIR-level equivalent of handleDecorationCacheControl() from
+/// the LLVM translation layer. For each cache control attribute, it emits:
+///
+///   %ann = llvm.intr.ptr.annotation %ptr, @".str.cachecontrol.N",
+///              @".str.file.N", 0, null : !llvm.ptr<AS>
+///
+/// Multiple annotations are chained: the result of each annotation op is
+/// fed as the pointer input to the next one.
+///
+/// \param rewriter       The pattern rewriter.
+/// \param loc            Source location for created ops.
+/// \param ptr            The pointer value to annotate.
+/// \param cacheControls  The cache control ArrayAttr (from
+/// getCacheControlMetadata).
+/// \param moduleOp       The enclosing module (for creating globals).
+/// \returns The annotated pointer value (or the original ptr if no
+/// annotations).
+static Value annotatePtrWithCacheControl(ConversionPatternRewriter &rewriter,
+                                         Location loc, Value ptr,
+                                         ArrayAttr cacheControls,
+                                         Operation *moduleOp) {
+  SmallVector<std::string> payloads =
+      buildCacheControlPayloads(cacheControls.getValue());
+  if (payloads.empty())
+    return ptr;
+
+  auto ptrType = cast<LLVM::LLVMPointerType>(ptr.getType());
+  auto as1PtrTy = LLVM::LLVMPointerType::get(rewriter.getContext(), 1);
+  auto i32Ty = rewriter.getI32Type();
+
+  // Create shared constants for all annotations on this pointer.
+  Value fileStr =
+      createMetadataStringPtr(rewriter, moduleOp, loc, "", ".str.file");
+  Value lineVal = LLVM::ConstantOp::create(rewriter, loc, i32Ty, 0);
+  Value nullAS1 = LLVM::ZeroOp::create(rewriter, loc, as1PtrTy);
+
+  // Chain: each annotation takes the result of the previous one as its
+  // pointer operand.
+  Value curPtr = ptr;
+  for (const std::string &payload : payloads) {
+    Value annStr = createMetadataStringPtr(rewriter, moduleOp, loc, payload,
+                                           ".str.cachecontrol");
+    auto annOp = LLVM::PtrAnnotation::create(rewriter, loc, ptrType, curPtr,
+                                             annStr, fileStr, lineVal, nullAS1);
+    curPtr = annOp.getResult();
+  }
+
+  return curPtr;
+}
+
+/// Helper to apply cache control annotation on a pointer operand of a call.
+/// Replaces the pointer argument of the call with an annotated version.
+///
+/// For operations that produce a call (like block load/store/prefetch), the
+/// pointer is typically the first argument. This function:
+/// 1. Builds the annotation chain on the pointer.
+/// 2. Replaces the pointer operand in the provided args list.
+///
+/// \param rewriter     The pattern rewriter.
+/// \param loc          Source location.
+/// \param ptr          The original pointer value (first arg to the call).
+/// \param cacheControls  The cache control metadata.
+/// \param moduleOp     The enclosing module.
+/// \param args         The argument list (modified in place: args[ptrIdx] is
+/// replaced).
+/// \param ptrIdx       Index of the pointer in the args list (default 0).
+template <typename OpType>
+static void
+applyCacheControlAnnotation(ConversionPatternRewriter &rewriter, Location loc,
+                            OpType op, SmallVectorImpl<Value> &args,
+                            Operation *moduleOp, unsigned ptrIdx = 0) {
+  std::optional<ArrayAttr> optCacheControls =
+      getCacheControlMetadata(rewriter, op);
+  if (!optCacheControls)
+    return;
+
+  Value annotatedPtr = annotatePtrWithCacheControl(rewriter, loc, args[ptrIdx],
+                                                   *optCacheControls, moduleOp);
+  args[ptrIdx] = annotatedPtr;
+}
+
+//===----------------------------------------------------------------------===//
+// End cache control annotation utilities
+//===----------------------------------------------------------------------===//
+
 static LLVM::CallOp createDeviceFunctionCall(
     ConversionPatternRewriter &rewriter, StringRef funcName, Type retType,
     ArrayRef<Type> argTypes, ArrayRef<Value> args,
@@ -420,10 +618,17 @@ class PrefetchToOCLPattern : public OpConversionPattern<PrefetchOp> {
   matchAndRewrite(PrefetchOp op, PrefetchOp::Adaptor adaptor,
                   ConversionPatternRewriter &rewriter) const override {
     auto loc = op.getLoc();
+    auto *moduleOp = op->getParentWithTrait<OpTrait::SymbolTable>();
+
     const std::string fnName{"_Z8prefetchPU3AS1Kcm"};
     Value one =
         LLVM::ConstantOp::create(rewriter, loc, rewriter.getI64Type(), 1);
     SmallVector<Value> args{op.getPtr(), one};
+
+    // Annotate pointer with cache control before passing to the call.
+    applyCacheControlAnnotation(rewriter, loc, op, args, moduleOp,
+                                /*ptrIdx=*/0);
+
     SmallVector<Type> argTypes;
     for (auto arg : args)
       argTypes.push_back(arg.getType());
@@ -437,12 +642,9 @@ class PrefetchToOCLPattern : public OpConversionPattern<PrefetchOp> {
         /*targetMem1=*/LLVM::ModRefInfo::NoModRef);
     funcAttr.memEffectsAttr = memAttr;
 
-    LLVM::CallOp call = createDeviceFunctionCall(
-        rewriter, fnName, LLVM::LLVMVoidType::get(rewriter.getContext()),
-        argTypes, args, {}, funcAttr, op.getOperation());
-    if (std::optional<ArrayAttr> optCacheControls =
-            getCacheControlMetadata(rewriter, op))
-      call->setAttr(XeVMDialect::getCacheControlsAttrName(), *optCacheControls);
+    createDeviceFunctionCall(rewriter, fnName,
+                             LLVM::LLVMVoidType::get(rewriter.getContext()),
+                             argTypes, args, {}, funcAttr, op.getOperation());
     rewriter.eraseOp(op);
     return success();
   }
@@ -506,6 +708,7 @@ class LoadStorePrefetchToOCLPattern : public OpConversionPattern<OpType> {
     constexpr bool isPrefetch = std::is_same_v<OpType, BlockPrefetch2dOp>;
 
     auto loc = op.getLoc();
+    auto *moduleOp = op->template getParentWithTrait<OpTrait::SymbolTable>();
     VectorType vecType;
     bool packReg = false;
     bool transpose = false;
@@ -528,6 +731,11 @@ class LoadStorePrefetchToOCLPattern : public OpConversionPattern<OpType> {
         rewriter, loc, VectorType::get(2, i32Type), byteCoord, op.getY(), one);
     SmallVector<Value> args{op.getPtr(), op.getBaseWidth(), op.getBaseHeight(),
                             op.getBasePitch(), byteCoord};
+
+    // Annotate pointer (args[0]) with cache control before the call.
+    applyCacheControlAnnotation(rewriter, loc, op, args, moduleOp,
+                                /*ptrIdx=*/0);
+
     SmallVector<Type> retTypes;
     Value spvLoadDstPtr;
     std::string funcName{"intel_sub_group_2d_block_"};
@@ -599,13 +807,10 @@ class LoadStorePrefetchToOCLPattern : public OpConversionPattern<OpType> {
     for (auto arg : args) {
       argTypes.push_back(arg.getType());
     }
-    LLVM::CallOp call = createDeviceFunctionCall(
+    createDeviceFunctionCall(
         rewriter, funcName, LLVM::LLVMVoidType::get(rewriter.getContext()),
         argTypes, args, paramAttrs, funcAttr, op.getOperation());
-    if (std::optional<ArrayAttr> optCacheControls =
-            getCacheControlMetadata(rewriter, op)) {
-      call->setAttr(XeVMDialect::getCacheControlsAttrName(), *optCacheControls);
-    }
+
     if constexpr (isLoad)
       rewriter.replaceOp(
           op, LLVM::LoadOp::create(rewriter, loc, vecType, spvLoadDstPtr));
@@ -622,6 +827,9 @@ class BlockLoadStore1DToOCLPattern : public OpConversionPattern<OpType> {
   matchAndRewrite(OpType op, typename OpType::Adaptor adaptor,
                   ConversionPatternRewriter &rewriter) const override {
     constexpr bool isStore = std::is_same_v<OpType, xevm::BlockStoreOp>;
+    auto loc = op.getLoc();
+    auto *moduleOp = op->template getParentWithTrait<OpTrait::SymbolTable>();
+
     // Get OpenCL function name
     // https://registry.khronos.org/OpenCL/extensions/
     //         intel/cl_intel_subgroup_local_block_io.html
@@ -653,6 +861,14 @@ class BlockLoadStore1DToOCLPattern : public OpConversionPattern<OpType> {
     args.push_back(op.getPtr());
     argTypes.push_back(op.getPtr().getType());
     isUnsigned.push_back(true);
+
+    // Annotate pointer (args[0]) with cache control.
+    applyCacheControlAnnotation(rewriter, loc, op, args, moduleOp,
+                                /*ptrIdx=*/0);
+    // Update argTypes[0] in case the pointer type changed (it shouldn't
+    // change type, but the value is now the annotated pointer).
+    argTypes[0] = args[0].getType();
+
     Type retType;
     if constexpr (isStore) {
       args.push_back(op.getVal());
@@ -673,10 +889,7 @@ class BlockLoadStore1DToOCLPattern : public OpConversionPattern<OpType> {
     LLVM::CallOp call =
         createDeviceFunctionCall(rewriter, funcName, retType, argTypes, args,
                                  {}, funcAttr, op.getOperation());
-    if (std::optional<ArrayAttr> optCacheControls =
-            getCacheControlMetadata(rewriter, op)) {
-      call->setAttr(XeVMDialect::getCacheControlsAttrName(), *optCacheControls);
-    }
+
     if constexpr (isStore)
       rewriter.eraseOp(op);
     else
@@ -693,9 +906,26 @@ class LLVMLoadStoreToOCLPattern : public OpConversionPattern<OpType> {
                   ConversionPatternRewriter &rewriter) const override {
     if (!op->hasAttr("cache_control"))
       return failure();
+
+    auto *moduleOp = op->template getParentWithTrait<OpTrait::SymbolTable>();
     std::optional<ArrayAttr> optCacheControls =
         getCacheControlMetadata(rewriter, op);
-    op->setAttr(XeVMDialect::getCacheControlsAttrName(), *optCacheControls);
+    if (!optCacheControls) {
+      op->removeAttr("cache_control");
+      return success();
+    }
+
+    // Determine which operand is the pointer.
+    constexpr bool isStore = std::is_same_v<OpType, LLVM::StoreOp>;
+    unsigned ptrIdx = isStore ? 1 : 0;
+    Value ptr = op->getOperand(ptrIdx);
+
+    // Emit annotation intrinsic calls on the pointer.
+    Value annotatedPtr = annotatePtrWithCacheControl(
+        rewriter, op->getLoc(), ptr, *optCacheControls, moduleOp);
+
+    // Replace the pointer operand with the annotated one.
+    op->setOperand(ptrIdx, annotatedPtr);
     op->removeAttr("cache_control");
     return success();
   }
diff --git a/mlir/test/Conversion/XeVMToLLVM/xevm-to-llvm.mlir b/mlir/test/Conversion/XeVMToLLVM/xevm-to-llvm.mlir
index e3fb3af67b3e0..6fe7f8411d059 100644
--- a/mlir/test/Conversion/XeVMToLLVM/xevm-to-llvm.mlir
+++ b/mlir/test/Conversion/XeVMToLLVM/xevm-to-llvm.mlir
@@ -30,10 +30,16 @@ llvm.func @blockload2d(%a: !llvm.ptr<1>, %base_width_a: i32, %base_height_a: i32
 
 // -----
 // CHECK-LABEL: llvm.func spir_funccc @_Z41intel_sub_group_2d_block_read_16b_8r16x1cPU3AS1viiiDv2_iPt(
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6442{{.*}}0,0{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6442{{.*}}1,0{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}("\00") {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK: llvm.func @blockload2d_cache_control(%[[ARG0:.*]]: !llvm.ptr<1>,
+// CHECK-SAME:   %[[ARG1:.*]]: i32, %[[ARG2:.*]]: i32, %[[ARG3:.*]]: i32, %[[ARG4:.*]]: i32, %[[ARG5:.*]]: i32)
 llvm.func @blockload2d_cache_control(%a: !llvm.ptr<1>, %base_width_a: i32, %base_height_a: i32, %base_pitch_a: i32, %x: i32, %y: i32) -> vector<8xi16> {
-  // CHECK: xevm.DecorationCacheControl =
-  // CHECK-SAME: 6442 : i32, 0 : i32, 0 : i32
-  // CHECK-SAME: 6442 : i32, 1 : i32, 0 : i32
+  // CHECK: %[[ANN1:.*]] = "llvm.intr.ptr.annotation"(%[[ARG0]], {{.*}}) : (!llvm.ptr<1>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<1>
+  // CHECK: %[[ANN2:.*]] = "llvm.intr.ptr.annotation"(%[[ANN1]], {{.*}}) : (!llvm.ptr<1>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<1>
+  // CHECK: llvm.call spir_funccc @_Z41intel_sub_group_2d_block_read_16b_8r16x1cPU3AS1viiiDv2_iPt(
+  // CHECK-SAME: %[[ANN2]],
   %loaded_a = xevm.blockload2d %a, %base_width_a, %base_height_a, %base_pitch_a, %x, %y
     <{elem_size_in_bits=16 : i32, tile_width=16 : i32, tile_height=8 : i32, v_blocks=1 : i32, transpose=false,
       pack_register=false, cache_control=#xevm.load_cache_control<L1uc_L2uc_L3uc>}> : (!llvm.ptr<1>, i32, i32, i32, i32, i32) -> vector<8xi16>
@@ -158,10 +164,16 @@ llvm.func @blockstore2d(%c: !llvm.ptr<1>, %base_width_c: i32, %base_height_c: i3
 
 // -----
 // CHECK-LABEL: llvm.func spir_funccc @_Z42intel_sub_group_2d_block_write_32b_8r16x1cPU3AS1viiiDv2_iPj(
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6443{{.*}}0,1{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6443{{.*}}1,2{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}("\00") {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK: llvm.func @blockstore2d_cache_control(%[[ARG0:.*]]: !llvm.ptr<1>,
+// CHECK-SAME: %[[ARG1:.*]]: i32, %[[ARG2:.*]]: i32, %[[ARG3:.*]]: i32, %[[ARG4:.*]]: i32, %[[ARG5:.*]]: i32, %[[ARG6:.*]]: vector<8xi32>) {
 llvm.func @blockstore2d_cache_control(%c: !llvm.ptr<1>, %base_width_c: i32, %base_height_c: i32, %base_pitch_c: i32, %x: i32, %y: i32, %c_result_casted: vector<8xi32>) {
-  // CHECK: xevm.DecorationCacheControl =
-  // CHECK-SAME: 6443 : i32, 0 : i32, 1 : i32
-  // CHECK-SAME: 6443 : i32, 1 : i32, 2 : i32
+  // CHECK: %[[ANN1:.*]] = "llvm.intr.ptr.annotation"(%[[ARG0]], {{.*}}) : (!llvm.ptr<1>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<1>
+  // CHECK: %[[ANN2:.*]] = "llvm.intr.ptr.annotation"(%[[ANN1]], {{.*}}) : (!llvm.ptr<1>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<1>
+  // CHECK: llvm.call spir_funccc @_Z42intel_sub_group_2d_block_write_32b_8r16x1cPU3AS1viiiDv2_iPj(
+  // CHECK-SAME: %[[ANN2]],
   xevm.blockstore2d %c, %base_width_c, %base_height_c, %base_pitch_c, %x, %y, %c_result_casted
     <{elem_size_in_bits=32 : i32, tile_width=16 : i32, tile_height=8 : i32, cache_control = #xevm.store_cache_control<L1wt_L2uc_L3wb>}>
     : (!llvm.ptr<1>, i32, i32, i32, i32, i32, vector<8xi32>)
@@ -172,16 +184,16 @@ llvm.func @blockstore2d_cache_control(%c: !llvm.ptr<1>, %base_width_c: i32, %bas
 // CHECK-LABEL: llvm.func spir_funccc @_Z44intel_sub_group_2d_block_prefetch_8b_8r32x1cPU3AS1viiiDv2_i(
 // CHECK-SAME: !llvm.ptr<1> {llvm.nonnull}, i32, i32, i32, vector<2xi32>) attributes
 // CHECK-SAME: {memory_effects = #llvm.memory_effects<other = none, argMem = read, inaccessibleMem = none, errnoMem = none, targetMem0 = none, targetMem1 = none>, no_unwind}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6442{{.*}}0,0{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6442{{.*}}1,0{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}("\00") {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
 // CHECK: llvm.func @blockprefetch2d(%[[ARG0:.*]]: !llvm.ptr<1>,
 // CHECK-SAME: %[[ARG1:.*]]: i32, %[[ARG2:.*]]: i32, %[[ARG3:.*]]: i32, %[[ARG4:.*]]: i32, %[[ARG5:.*]]: i32) {
 llvm.func @blockprefetch2d(%ptr: !llvm.ptr<1>, %base_width: i32, %base_height: i32, %base_pitch: i32, %x: i32, %y: i32) {
-  // CHECK: %[[VAR0:.*]] = llvm.mlir.undef : vector<2xi32>
-  // CHECK: %[[VAR1:.*]] = llvm.mlir.constant(0 : i32) : i32
-  // CHECK: %[[VAR2:.*]] = llvm.mlir.constant(1 : i32) : i32
-  // CHECK: %[[VAR3:.*]] = llvm.insertelement %[[ARG4]], %[[VAR0]][%[[VAR1]] : i32] : vector<2xi32>
-  // CHECK: %[[VAR4:.*]] = llvm.insertelement %[[ARG5]], %[[VAR3]][%[[VAR2]] : i32] : vector<2xi32>
+  // CHECK: %[[ANN1:.*]] = "llvm.intr.ptr.annotation"(%[[ARG0]], {{.*}}) : (!llvm.ptr<1>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<1>
+  // CHECK: %[[ANN2:.*]] = "llvm.intr.ptr.annotation"(%[[ANN1]], {{.*}}) : (!llvm.ptr<1>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<1>
   // CHECK: llvm.call spir_funccc @_Z44intel_sub_group_2d_block_prefetch_8b_8r32x1cPU3AS1viiiDv2_i(
-  // CHECK-SAME: %[[ARG0]], %[[ARG1]], %[[ARG2]], %[[ARG3]], %[[VAR4]])
+  // CHECK-SAME: %[[ANN2]], %[[ARG1]], %[[ARG2]], %[[ARG3]],
   // CHECK-SAME: {function_type = !llvm.func<void (ptr<1>, i32, i32, i32, vector<2xi32>)>, linkage = #llvm.linkage<external>,
   // CHECK-SAME:   memory_effects = #llvm.memory_effects<other = none, argMem = read, inaccessibleMem = none, errnoMem = none, targetMem0 = none, targetMem1 = none>, no_unwind,
   // CHECK-SAME:   sym_name = "_Z44intel_sub_group_2d_block_prefetch_8b_8r32x1cPU3AS1viiiDv2_i", visibility_ = 0 : i64
@@ -227,10 +239,14 @@ llvm.func @memfence() {
 // -----
 // CHECK-LABEL: llvm.func spir_funccc @_Z8prefetchPU3AS1Kcm(!llvm.ptr<1>, i64) attributes
 // CHECK-SAME: {memory_effects = #llvm.memory_effects<other = none, argMem = read, inaccessibleMem = none, errnoMem = none, targetMem0 = none, targetMem1 = none>, no_unwind}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6442{{.*}}0,0{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6442{{.*}}1,0{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}("\00") {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
 // CHECK: llvm.func @prefetch(%[[ARG0:.*]]: !llvm.ptr<1>) {
 llvm.func @prefetch(%ptr: !llvm.ptr<1>) {
-  // CHECK: %[[VAR0:.*]] = llvm.mlir.constant(1 : i64) : i64
-  // CHECK: llvm.call spir_funccc @_Z8prefetchPU3AS1Kcm(%[[ARG0]], %[[VAR0]])
+  // CHECK: %[[ANN1:.*]] = "llvm.intr.ptr.annotation"(%[[ARG0]], {{.*}}) : (!llvm.ptr<1>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<1>
+  // CHECK: %[[ANN2:.*]] = "llvm.intr.ptr.annotation"(%[[ANN1]], {{.*}}) : (!llvm.ptr<1>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<1>
+  // CHECK: llvm.call spir_funccc @_Z8prefetchPU3AS1Kcm(%[[ANN2]],
   // CHECK-SAME: {function_type = !llvm.func<void (ptr<1>, i64)>, linkage = #llvm.linkage<external>,
   // CHECK-SAME:   memory_effects = #llvm.memory_effects<other = none, argMem = read, inaccessibleMem = none, errnoMem = none, targetMem0 = none, targetMem1 = none>,
   // CHECK-SAME:   no_unwind, sym_name = "_Z8prefetchPU3AS1Kcm", visibility_ = 0 : i64
@@ -239,105 +255,129 @@ llvm.func @prefetch(%ptr: !llvm.ptr<1>) {
 }
 
 // -----
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6442{{.*}}0,0{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6442{{.*}}1,0{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}("\00") {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
 // CHECK-LABEL: llvm.func @llvm.load
 llvm.func @llvm.load(%a: !llvm.ptr<1>) -> i32 {
-  // CHECK: xevm.DecorationCacheControl =
-  // CHECK-SAME: 6442 : i32, 0 : i32, 0 : i32
-  // CHECK-SAME: 6442 : i32, 1 : i32, 0 : i32
+  // CHECK: %[[ANN1:.*]] = "llvm.intr.ptr.annotation"(%{{.*}}, {{.*}}) : (!llvm.ptr<1>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<1>
+  // CHECK: %[[ANN2:.*]] = "llvm.intr.ptr.annotation"(%[[ANN1]], {{.*}}) : (!llvm.ptr<1>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<1>
+  // CHECK: llvm.load %[[ANN2]] : !llvm.ptr<1> -> i32
   %val = llvm.load %a {cache_control=#xevm.load_cache_control<L1uc_L2uc_L3uc>} : !llvm.ptr<1> -> i32
   llvm.return %val : i32
 }
 
 // -----
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6443{{.*}}0,1{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6443{{.*}}1,2{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}("\00") {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
 // CHECK-LABEL: llvm.func @llvm.store
 llvm.func @llvm.store(%a: !llvm.ptr<1>, %val: i32) {
-  // CHECK: xevm.DecorationCacheControl =
-  // CHECK-SAME: 6443 : i32, 0 : i32, 1 : i32
-  // CHECK-SAME: 6443 : i32, 1 : i32, 2 : i32
+  // CHECK: %[[ANN1:.*]] = "llvm.intr.ptr.annotation"(%{{.*}}, {{.*}}) : (!llvm.ptr<1>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<1>
+  // CHECK: %[[ANN2:.*]] = "llvm.intr.ptr.annotation"(%[[ANN1]], {{.*}}) : (!llvm.ptr<1>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<1>
+  // CHECK: llvm.store %{{.*}}, %[[ANN2]] : i32, !llvm.ptr<1>
   llvm.store %val, %a {cache_control=#xevm.store_cache_control<L1wt_L2uc_L3wb>} : i32, !llvm.ptr<1>
   llvm.return
 }
 
 // -----
 // CHECK-LABEL: llvm.func spir_funccc @_Z30intel_sub_group_block_read_us8PU3AS1t
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6442{{.*}}0,0{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6442{{.*}}1,0{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}("\00") {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
 // CHECK: llvm.func @blockload_as1(%[[ARG0:.*]]: !llvm.ptr<1>)
 llvm.func @blockload_as1(%ptr: !llvm.ptr<1>) -> vector<8xi16> {
-  // CHECK: %[[VAR0:.*]] = llvm.call spir_funccc @_Z30intel_sub_group_block_read_us8PU3AS1t(%[[ARG0]])
+  // CHECK: %[[ANN1:.*]] = "llvm.intr.ptr.annotation"(%[[ARG0]], {{.*}}) : (!llvm.ptr<1>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<1>
+  // CHECK: %[[ANN2:.*]] = "llvm.intr.ptr.annotation"(%[[ANN1]], {{.*}}) : (!llvm.ptr<1>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<1>
+  // CHECK: %{{.*}} = llvm.call spir_funccc @_Z30intel_sub_group_block_read_us8PU3AS1t(%[[ANN2]])
   // CHECK-SAME: {function_type = !llvm.func<vector<8xi16> (ptr<1>)>, linkage = #llvm.linkage<external>,
   // CHECK-SAME:  no_unwind, sym_name = "_Z30intel_sub_group_block_read_us8PU3AS1t",
-  // CHECK-SAME:  visibility_ = 0 : i64, will_return, xevm.DecorationCacheControl =
-  // CHECK-SAME:    [6442 : i32, 0 : i32, 0 : i32],
-  // CHECK-SAME:    [6442 : i32, 1 : i32, 0 : i32]
+  // CHECK-SAME:  visibility_ = 0 : i64, will_return}
   %loaded_a = xevm.blockload %ptr <{cache_control=#xevm.load_cache_control<L1uc_L2uc_L3uc>}> : (!llvm.ptr<1>) -> vector<8xi16>
   llvm.return %loaded_a : vector<8xi16>
 }
 
 // -----
 // CHECK-LABEL: llvm.func spir_funccc @_Z31intel_sub_group_block_read_uc16PU3AS3h(!llvm.ptr<3>)
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6442{{.*}}0,0{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6442{{.*}}1,0{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}("\00") {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
 // CHECK: llvm.func @blockload_as3(%[[ARG0:.*]]: !llvm.ptr<3>)
 llvm.func @blockload_as3(%ptr: !llvm.ptr<3>) -> vector<16xi8> {
-  // CHECK: %[[VAR0:.*]] = llvm.call spir_funccc @_Z31intel_sub_group_block_read_uc16PU3AS3h(%[[ARG0]])
+  // CHECK: %[[ANN1:.*]] = "llvm.intr.ptr.annotation"(%[[ARG0]], {{.*}}) : (!llvm.ptr<3>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<3>
+  // CHECK: %[[ANN2:.*]] = "llvm.intr.ptr.annotation"(%[[ANN1]], {{.*}}) : (!llvm.ptr<3>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<3>
+  // CHECK: %{{.*}} = llvm.call spir_funccc @_Z31intel_sub_group_block_read_uc16PU3AS3h(%[[ANN2]])
   // CHECK-SAME: {function_type = !llvm.func<vector<16xi8> (ptr<3>)>, linkage = #llvm.linkage<external>,
   // CHECK-SAME:   no_unwind, sym_name = "_Z31intel_sub_group_block_read_uc16PU3AS3h", visibility_ = 0 : i64,
-  // CHECK-SAME:   will_return, xevm.DecorationCacheControl =
-  // CHECK-SAME:    [6442 : i32, 0 : i32, 0 : i32],
-  // CHECK-SAME:    [6442 : i32, 1 : i32, 0 : i32]
+  // CHECK-SAME:   will_return}
   %loaded_a = xevm.blockload %ptr <{cache_control=#xevm.load_cache_control<L1uc_L2uc_L3uc>}> : (!llvm.ptr<3>) -> vector<16xi8>
   llvm.return %loaded_a : vector<16xi8>
 }
 
 // -----
 // CHECK-LABEL: llvm.func spir_funccc @_Z29intel_sub_group_block_read_ucPU3AS3h(!llvm.ptr<3>)
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6442{{.*}}0,0{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6442{{.*}}1,0{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}("\00") {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
 // CHECK: llvm.func @blockload_scalar(%[[ARG0:.*]]: !llvm.ptr<3>)
 llvm.func @blockload_scalar(%ptr: !llvm.ptr<3>) -> i8 {
-  // CHECK: %[[VAR0:.*]] = llvm.call spir_funccc @_Z29intel_sub_group_block_read_ucPU3AS3h(%[[ARG0]])
+  // CHECK: %[[ANN1:.*]] = "llvm.intr.ptr.annotation"(%[[ARG0]], {{.*}}) : (!llvm.ptr<3>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<3>
+  // CHECK: %[[ANN2:.*]] = "llvm.intr.ptr.annotation"(%[[ANN1]], {{.*}}) : (!llvm.ptr<3>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<3>
+  // CHECK: %{{.*}} = llvm.call spir_funccc @_Z29intel_sub_group_block_read_ucPU3AS3h(%[[ANN2]])
   // CHECK-SAME: {function_type = !llvm.func<i8 (ptr<3>)>, linkage = #llvm.linkage<external>,
   // CHECK-SAME:   no_unwind, sym_name = "_Z29intel_sub_group_block_read_ucPU3AS3h", visibility_ = 0 : i64,
-  // CHECK-SAME:   will_return, xevm.DecorationCacheControl =
-  // CHECK-SAME:    [6442 : i32, 0 : i32, 0 : i32],
-  // CHECK-SAME:    [6442 : i32, 1 : i32, 0 : i32]
+  // CHECK-SAME:   will_return}
   %loaded_a = xevm.blockload %ptr <{cache_control=#xevm.load_cache_control<L1uc_L2uc_L3uc>}> : (!llvm.ptr<3>) -> i8
   llvm.return %loaded_a : i8
 }
 
 // -----
 // CHECK-LABEL: llvm.func spir_funccc @_Z31intel_sub_group_block_write_ui8PU3AS1jDv8_j
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6443{{.*}}0,1{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6443{{.*}}1,2{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}("\00") {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
 // CHECK: llvm.func @blockstore_as1(%[[ARG0:.*]]: !llvm.ptr<1>, %[[ARG1:.*]]: vector<8xi32>) {
 llvm.func @blockstore_as1(%ptr: !llvm.ptr<1>, %data: vector<8xi32>) {
-  // CHECK: llvm.call spir_funccc @_Z31intel_sub_group_block_write_ui8PU3AS1jDv8_j(%[[ARG0]], %[[ARG1]])
+  // CHECK: %[[ANN1:.*]] = "llvm.intr.ptr.annotation"(%[[ARG0]], {{.*}}) : (!llvm.ptr<1>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<1>
+  // CHECK: %[[ANN2:.*]] = "llvm.intr.ptr.annotation"(%[[ANN1]], {{.*}}) : (!llvm.ptr<1>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<1>
+  // CHECK: llvm.call spir_funccc @_Z31intel_sub_group_block_write_ui8PU3AS1jDv8_j(%[[ANN2]], %[[ARG1]])
   // CHECK-SAME: {function_type = !llvm.func<void (ptr<1>, vector<8xi32>)>, linkage = #llvm.linkage<external>,
   // CHECK-SAME:   no_unwind, sym_name = "_Z31intel_sub_group_block_write_ui8PU3AS1jDv8_j", visibility_ = 0 : i64,
-  // CHECK-SAME:   will_return, xevm.DecorationCacheControl =
-  // CHECK-SAME:    [6443 : i32, 0 : i32, 1 : i32],
-  // CHECK-SAME:    [6443 : i32, 1 : i32, 2 : i32]
+  // CHECK-SAME:   will_return}
   xevm.blockstore %ptr, %data <{cache_control=#xevm.store_cache_control<L1wt_L2uc_L3wb>}> : (!llvm.ptr<1>, vector<8xi32>)
   llvm.return
 }
 
 // -----
 // CHECK-LABEL: llvm.func spir_funccc @_Z31intel_sub_group_block_write_ul2PU3AS3mDv2_m
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6443{{.*}}0,1{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6443{{.*}}1,2{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}("\00") {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
 // CHECK: llvm.func @blockstore_as3(%[[ARG0:.*]]: !llvm.ptr<3>, %[[ARG1:.*]]: vector<2xi64>) {
 llvm.func @blockstore_as3(%ptr: !llvm.ptr<3>, %data: vector<2xi64>) {
-  // CHECK: llvm.call spir_funccc @_Z31intel_sub_group_block_write_ul2PU3AS3mDv2_m(%[[ARG0]], %[[ARG1]])
+  // CHECK: %[[ANN1:.*]] = "llvm.intr.ptr.annotation"(%[[ARG0]], {{.*}}) : (!llvm.ptr<3>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<3>
+  // CHECK: %[[ANN2:.*]] = "llvm.intr.ptr.annotation"(%[[ANN1]], {{.*}}) : (!llvm.ptr<3>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<3>
+  // CHECK: llvm.call spir_funccc @_Z31intel_sub_group_block_write_ul2PU3AS3mDv2_m(%[[ANN2]], %[[ARG1]])
   // CHECK-SAME: {function_type = !llvm.func<void (ptr<3>, vector<2xi64>)>, linkage = #llvm.linkage<external>,
   // CHECK-SAME:   no_unwind, sym_name = "_Z31intel_sub_group_block_write_ul2PU3AS3mDv2_m", visibility_ = 0 : i64,
-  // CHECK-SAME:   will_return, xevm.DecorationCacheControl =
-  // CHECK-SAME:    [6443 : i32, 0 : i32, 1 : i32],
-  // CHECK-SAME:    [6443 : i32, 1 : i32, 2 : i32]
+  // CHECK-SAME:   will_return}
   xevm.blockstore %ptr, %data <{cache_control=#xevm.store_cache_control<L1wt_L2uc_L3wb>}> : (!llvm.ptr<3>, vector<2xi64>)
   llvm.return
 }
 
 // -----
 // CHECK-LABEL: llvm.func spir_funccc @_Z30intel_sub_group_block_write_ulPU3AS3mm
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6443{{.*}}0,1{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}({{.*}}6443{{.*}}1,2{{.*}}) {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
+// CHECK-DAG: llvm.mlir.global private unnamed_addr constant @{{.*}}("\00") {addr_space = 1 : i32, alignment = 1 : i64, section = "llvm.metadata"}
 // CHECK: llvm.func @blockstore_scalar(%[[ARG0:.*]]: !llvm.ptr<3>, %[[ARG1:.*]]: i64) {
 llvm.func @blockstore_scalar(%ptr: !llvm.ptr<3>, %data: i64) {
-  // CHECK: llvm.call spir_funccc @_Z30intel_sub_group_block_write_ulPU3AS3mm(%[[ARG0]], %[[ARG1]])
+  // CHECK: %[[ANN1:.*]] = "llvm.intr.ptr.annotation"(%[[ARG0]], {{.*}}) : (!llvm.ptr<3>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<3>
+  // CHECK: %[[ANN2:.*]] = "llvm.intr.ptr.annotation"(%[[ANN1]], {{.*}}) : (!llvm.ptr<3>, !llvm.ptr<1>, !llvm.ptr<1>, i32, !llvm.ptr<1>) -> !llvm.ptr<3>
+  // CHECK: llvm.call spir_funccc @_Z30intel_sub_group_block_write_ulPU3AS3mm(%[[ANN2]], %[[ARG1]])
   // CHECK-SAME: {function_type = !llvm.func<void (ptr<3>, i64)>, linkage = #llvm.linkage<external>,
   // CHECK-SAME:   no_unwind, sym_name = "_Z30intel_sub_group_block_write_ulPU3AS3mm", visibility_ = 0 : i64,
-  // CHECK-SAME:   will_return, xevm.DecorationCacheControl =
-  // CHECK-SAME:    [6443 : i32, 0 : i32, 1 : i32],
-  // CHECK-SAME:    [6443 : i32, 1 : i32, 2 : i32]
+  // CHECK-SAME:   will_return}
   xevm.blockstore %ptr, %data <{cache_control=#xevm.store_cache_control<L1wt_L2uc_L3wb>}> : (!llvm.ptr<3>, i64)
   llvm.return
 }

>From 4108a30656e87316d23d0c1fe8de526866ef9485 Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Sun, 1 Mar 2026 20:36:41 +0000
Subject: [PATCH 8/9] Remove cache_control decoration handling from
 XeVMTranslation phase.

---
 .../Dialect/XeVM/XeVMToLLVMIRTranslation.cpp  | 248 +-----------------
 mlir/test/Target/LLVMIR/xevm.mlir             |  31 +--
 2 files changed, 9 insertions(+), 270 deletions(-)

diff --git a/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
index ecb10f35bcb27..ffa5f1df0fcc3 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
@@ -17,148 +17,19 @@
 #include "mlir/IR/Operation.h"
 #include "mlir/Target/LLVMIR/ModuleTranslation.h"
 
-#include "mlir/Support/LLVM.h"
 #include "llvm/ADT/TypeSwitch.h"
-#include "llvm/IR/ConstantRange.h"
 #include "llvm/IR/Constants.h"
-#include "llvm/IR/DebugInfoMetadata.h"
-#include "llvm/IR/DebugLoc.h"
-#include "llvm/IR/GlobalVariable.h"
-#include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Metadata.h"
-#include "llvm/IR/Module.h"
+
+#include "llvm/IR/ConstantRange.h"
+#include "llvm/IR/IRBuilder.h"
 #include "llvm/Support/raw_ostream.h"
 
 using namespace mlir;
 using namespace mlir::LLVM;
 
 namespace {
-//===----------------------------------------------------------------------===//
-// Utility functions for the translation
-//===----------------------------------------------------------------------===//
-// Extract the source filename from the debug location of \p inst, if available.
-static std::string getSourceFilename(const llvm::Instruction *inst) {
-  if (const llvm::DebugLoc &dbgLoc = inst->getDebugLoc()) {
-    if (auto *loc = dbgLoc.get()) {
-      if (llvm::DIFile *file = loc->getFile()) {
-        if (!file->getDirectory().empty())
-          return (file->getDirectory() + "/" + file->getFilename()).str();
-        return file->getFilename().str();
-      }
-    }
-  }
-  return "";
-}
-
-// Build one cache-control payload string per attribute.
-//
-// Each mlir::Attribute is expected to be an ArrayAttr of (at least) 3
-// IntegerAttr values: [SPIR-V token number of that attribute, value for L1
-// cache, value for L3 cache].
-//
-// A single entry produces a string that appears in LLVM IR as:
-//   {6442:\220,1\22}\00
-static llvm::SmallVector<std::string>
-buildCacheControlPayloads(llvm::ArrayRef<mlir::Attribute> attrs) {
-  llvm::SmallVector<std::string> payloads;
-  llvm::StringMap<bool> seen;
-
-  for (mlir::Attribute a : attrs) {
-    auto arr = mlir::dyn_cast<mlir::ArrayAttr>(a);
-    if (!arr)
-      continue;
-
-    auto vals = arr.getValue();
-    // Assert that the attribute has at most 4 integer values: [SPIR-V token, L1
-    // value, L3 value, optional extra value].
-    assert(vals.size() <= 4 &&
-           "Expected at most 4 integer values in cache control attribute.");
-
-    //  Although the caching value is allowed for 3 levels (L1, L2, L3), current
-    //  Intel GPUs only have L1, and L3. So we only use L1 and L3 values. The L2
-    //  value is ignored.
-    auto firstAttr = mlir::dyn_cast<mlir::IntegerAttr>(vals[0]); // Token number
-    auto secondAttr = mlir::dyn_cast<mlir::IntegerAttr>(vals[1]); // L1 value
-    // L2 value is ignored: vals[2]
-    auto thirdAttr = mlir::dyn_cast<mlir::IntegerAttr>(vals[3]); // L3 value
-
-    if (!firstAttr || !secondAttr || !thirdAttr)
-      continue;
-
-    uint64_t first = firstAttr.getValue().getZExtValue();
-    uint64_t second = secondAttr.getValue().getZExtValue();
-    uint64_t third = thirdAttr.getValue().getZExtValue();
-
-    // Produce: {FIRST:\22SECOND,THIRD\22}
-    // where \22 is the 0x22 byte ("), which LLVM IR prints as \22.
-    // The null terminator (\00) is added by ConstantDataArray::getString.
-    std::string entry;
-    entry.push_back('{');
-    entry += std::to_string(first);
-    entry.push_back(':');
-    entry.push_back('"'); // 0x22 → prints as \22 in LLVM IR
-    entry += std::to_string(second);
-    entry.push_back(',');
-    entry += std::to_string(third);
-    entry.push_back('"'); // 0x22 → prints as \22 in LLVM IR
-    entry.push_back('}');
-
-    // Skip duplicates — identical annotations on the same pointer are
-    // redundant.
-    if (!seen.insert({entry, true}).second)
-      continue;
-
-    payloads.push_back(std::move(entry));
-  }
-
-  return payloads;
-}
-
-// Create (or reuse) an addrspace(1) global string in section "llvm.metadata"
-// and return an i8 addrspace(1)* pointer to its first element.
-//
-// A module-level StringMap caches previously created globals so that
-// identical strings are not duplicated.
-static llvm::Value *getOrCreateAS1MetadataStringPtr(llvm::Module &module,
-                                                    llvm::StringRef str,
-                                                    llvm::StringRef nameHint) {
-  // Per-module cache keyed on the string content.
-  static llvm::DenseMap<llvm::Module *, llvm::StringMap<llvm::Value *>> cache;
-  auto &moduleCache = cache[&module];
-
-  auto it = moduleCache.find(str);
-  if (it != moduleCache.end())
-    return it->second;
-
-  llvm::LLVMContext &ctx = module.getContext();
-
-  llvm::Constant *arr =
-      llvm::ConstantDataArray::getString(ctx, str, /*AddNull=*/true);
-  auto *arrTy = llvm::cast<llvm::ArrayType>(arr->getType());
-
-  auto *gv = new llvm::GlobalVariable(
-      module, arrTy,
-      /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, arr, nameHint,
-      /*InsertBefore=*/nullptr, llvm::GlobalValue::NotThreadLocal,
-      /*AddressSpace=*/1);
-
-  gv->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
-  gv->setSection("llvm.metadata");
-  gv->setAlignment(llvm::Align(1));
-
-  llvm::Constant *zero = llvm::ConstantInt::get(llvm::Type::getInt32Ty(ctx), 0);
-  llvm::Constant *idxs[] = {zero, zero};
-
-  llvm::Constant *gep =
-      llvm::ConstantExpr::getInBoundsGetElementPtr(arrTy, gv, idxs);
-
-  auto *as1PtrTy = llvm::PointerType::get(ctx, /*AddrSpace=*/1);
-  llvm::Value *result = llvm::ConstantExpr::getBitCast(gep, as1PtrTy);
-
-  moduleCache[str] = result;
-  return result;
-}
 /// Implementation of the dialect interface that converts operations belonging
 /// to the XeVM dialect to LLVM IR.
 class XeVMDialectLLVMIRTranslationInterface
@@ -166,124 +37,15 @@ class XeVMDialectLLVMIRTranslationInterface
 public:
   using LLVMTranslationDialectInterface::LLVMTranslationDialectInterface;
 
-  /// Attaches module-level metadata for functions marked as kernels.
+  /// Empty amendOperation implementation, can be extended in the future.
   LogicalResult
   amendOperation(Operation *op, ArrayRef<llvm::Instruction *> instructions,
                  NamedAttribute attribute,
                  LLVM::ModuleTranslation &moduleTranslation) const final {
-    StringRef attrName = attribute.getName().getValue();
-    if (attrName == mlir::xevm::XeVMDialect::getCacheControlsAttrName()) {
-      auto cacheControlsArray = dyn_cast<ArrayAttr>(attribute.getValue());
-      if (cacheControlsArray.size() != 2) {
-        return op->emitOpError(
-            "Expected both L1 and L3 cache control attributes!");
-      }
-      if (instructions.size() != 1) {
-        return op->emitOpError("Expecting a single instruction");
-      }
-      return handleDecorationCacheControl(instructions.front(),
-                                          cacheControlsArray.getValue());
-    }
     return success();
   }
 
 private:
-  // Attach cache-control metadata to the pointer operand of \p inst.
-  //
-  // Each attribute in \p attrs becomes a separate
-  // llvm.ptr.annotation.p<AS>.p1 call, where AS is the address space of the
-  // input pointer.  When there are multiple attributes the calls are chained:
-  // the result of the first annotation is fed as the pointer into the second,
-  // and so on, so that every annotation is associated with the same logical
-  // pointer.
-  //
-  // Example:
-  //   ; For a ptr addrspace(1) operand -> llvm.ptr.annotation.p1.p1
-  //   %a1 = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(
-  //              ptr addrspace(1) %ptr, ... "{6442:\220,1\22}" ...)
-  //   %a2 = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(
-  //              ptr addrspace(1) %a1,  ... "{6442:\221,1\22}" ...)
-  //   ; instruction now uses %a2
-  //
-  //   ; For a ptr addrspace(0) operand -> llvm.ptr.annotation.p0.p1
-  //   %a1 = call ptr @llvm.ptr.annotation.p0.p1(
-  //              ptr %ptr, ... "{6442:\220,1\22}" ...)
-  static mlir::LogicalResult
-  handleDecorationCacheControl(llvm::Instruction *inst,
-                               llvm::ArrayRef<mlir::Attribute> attrs) {
-    llvm::Module *module = inst->getModule();
-    if (!module)
-      return mlir::failure();
-
-    // @TODO: Currently we expect the pointer to be the first operand for all
-    // instructions (which can have cache control attributes) except llvm.store.
-    // but this is not a strict requirement of the protocol; it can be adjusted
-    // if needed.
-    unsigned ptrIdx = 0;
-
-    // Check if the instruction is a LLVM Store instruction, which has the
-    // pointer as the second operand.
-    if (llvm::isa<llvm::StoreInst>(inst))
-      ptrIdx = 1;
-    llvm::Value *ptr = inst->getOperand(ptrIdx);
-
-    if (!ptr || !ptr->getType()->isPointerTy())
-      return mlir::success();
-
-    auto *ptrTy = llvm::cast<llvm::PointerType>(ptr->getType());
-
-    llvm::SmallVector<std::string> payloads = buildCacheControlPayloads(attrs);
-    if (payloads.empty())
-      return mlir::success();
-
-    llvm::LLVMContext &ctx = module->getContext();
-    llvm::IRBuilder<> builder(inst);
-    auto *as1PtrTy = llvm::PointerType::get(ctx, 1);
-
-    // Shared across all annotations for this instruction.
-    std::string fileName = getSourceFilename(inst);
-    llvm::Value *fileStrAS1 = getOrCreateAS1MetadataStringPtr(
-        *module,
-        fileName.empty() ? llvm::StringRef("") : llvm::StringRef(fileName),
-        ".str.file");
-
-    unsigned line = 0;
-    if (llvm::DILocation *loc = inst->getDebugLoc().get())
-      line = loc->getLine();
-    llvm::Value *lineVal =
-        llvm::ConstantInt::get(llvm::Type::getInt32Ty(ctx), line);
-
-    llvm::Value *nullAS1 =
-        llvm::ConstantPointerNull::get(llvm::cast<llvm::PointerType>(as1PtrTy));
-
-    unsigned ptrAS = ptrTy->getAddressSpace();
-
-    llvm::FunctionType *fTy = llvm::FunctionType::get(
-        ptrTy,
-        {ptrTy, as1PtrTy, as1PtrTy, llvm::Type::getInt32Ty(ctx), as1PtrTy},
-        /*isVarArg=*/false);
-
-    // llvm.ptr.annotation.p<ptrAS>.p1
-    std::string intrinsicName =
-        "llvm.ptr.annotation.p" + std::to_string(ptrAS) + ".p1";
-    llvm::FunctionCallee callee =
-        module->getOrInsertFunction(intrinsicName, fTy);
-
-    // Chain: each annotation takes the result of the previous one as its
-    // pointer operand.
-    llvm::Value *curPtr = ptr;
-    for (const std::string &payload : payloads) {
-      llvm::Value *annStrAS1 = getOrCreateAS1MetadataStringPtr(
-          *module, payload, ".str.cachecontrol");
-
-      curPtr = builder.CreateCall(
-          callee, {curPtr, annStrAS1, fileStrAS1, lineVal, nullAS1},
-          "annotated.ptr");
-    }
-
-    inst->setOperand(ptrIdx, curPtr);
-    return mlir::success();
-  }
 };
 } // namespace
 
@@ -298,4 +60,4 @@ void mlir::registerXeVMDialectTranslation(::mlir::MLIRContext &context) {
   DialectRegistry registry;
   registerXeVMDialectTranslation(registry);
   context.appendDialectRegistry(registry);
-}
+}
\ No newline at end of file
diff --git a/mlir/test/Target/LLVMIR/xevm.mlir b/mlir/test/Target/LLVMIR/xevm.mlir
index 0d3562a1d4f17..7847113786789 100644
--- a/mlir/test/Target/LLVMIR/xevm.mlir
+++ b/mlir/test/Target/LLVMIR/xevm.mlir
@@ -5,14 +5,10 @@ module {
 
   llvm.func @prefetch(%arg0: !llvm.ptr<1>) {
     %0 = llvm.mlir.constant(1 : i64) : i64
-    // CHECK: @[[CACHECONTROL:.*]] = private unnamed_addr addrspace(1) constant [13 x i8] c"{6442:\220,0\22}\00", section "llvm.metadata"
-    // CHECK: @[[CACHECONTROL1:.*]] = private unnamed_addr addrspace(1) constant [13 x i8] c"{6442:\221,0\22}\00", section "llvm.metadata"
     // CHECK-LABEL: define void @prefetch
     // CHECK-SAME: ptr addrspace(1) %[[ARG0:.*]])
-    // CHECK: %[[ANNOTATED:.*]] = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(ptr addrspace(1) %[[ARG0]], ptr addrspace(1) @[[CACHECONTROL]],
-    // CHECK: %[[ANNOTATED1:.*]] = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(ptr addrspace(1) %[[ANNOTATED]], ptr addrspace(1) @[[CACHECONTROL1]],
-    // CHECK: call spir_func void @_Z8prefetchPU3AS1Kcm(ptr addrspace(1) %[[ANNOTATED1]], i64 1)
-    llvm.call spir_funccc @_Z8prefetchPU3AS1Kcm(%arg0, %0) {function_type = !llvm.func<void (ptr<1>, i64)>, linkage = #llvm.linkage<external>, no_unwind, sym_name = "_Z8prefetchPU3AS1Kcm", visibility_ = 0 : i64, xevm.DecorationCacheControl = [[6442 : i32, 0 : i32, 1 : i32, 0 : i32], [6442 : i32, 1 : i32, 1 : i32, 0 : i32]]} : (!llvm.ptr<1>, i64) -> ()
+    // CHECK: call spir_func void @_Z8prefetchPU3AS1Kcm(ptr addrspace(1) %[[ARG0]], i64 1)
+    llvm.call spir_funccc @_Z8prefetchPU3AS1Kcm(%arg0, %0) {function_type = !llvm.func<void (ptr<1>, i64)>, linkage = #llvm.linkage<external>, no_unwind, sym_name = "_Z8prefetchPU3AS1Kcm", visibility_ = 0 : i64} : (!llvm.ptr<1>, i64) -> ()
     llvm.return
   }
 }
@@ -20,31 +16,12 @@ module {
 // -----
 
 module {
-  // CHECK: @[[CACHECONTROL:.*]] = private unnamed_addr addrspace(1) constant [13 x i8] c"{6442:\220,0\22}\00", section "llvm.metadata"
-  // CHECK: @[[CACHECONTROL1:.*]] = private unnamed_addr addrspace(1) constant [13 x i8] c"{6442:\221,0\22}\00", section "llvm.metadata"
   // CHECK-LABEL: define i32 @load
   // CHECK-SAME: ptr addrspace(1) %[[ARG0:.*]])
   llvm.func @load(%arg0: !llvm.ptr<1>) -> i32 {
-    // CHECK: %[[ANNOTATED:.*]] = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(ptr addrspace(1) %[[ARG0]], ptr addrspace(1) @[[CACHECONTROL]],
-    // CHECK: %[[ANNOTATED1:.*]] = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(ptr addrspace(1) %[[ANNOTATED]], ptr addrspace(1) @[[CACHECONTROL1]],
-    // CHECK: load i32, ptr addrspace(1) %[[ANNOTATED1]], align 4
-    %0 = llvm.load %arg0 {xevm.DecorationCacheControl = [[6442 : i32, 0 : i32, 1 : i32, 0 : i32], [6442 : i32, 1 : i32, 1 : i32, 0 : i32]]} : !llvm.ptr<1> -> i32
+    // CHECK: load i32, ptr addrspace(1) %[[ARG0]], align 4
+    %0 = llvm.load %arg0 : !llvm.ptr<1> -> i32
     llvm.return %0 : i32
   }
 }
 
-// -----
-
-module {
-  // CHECK: @[[CACHECONTROL:.*]] = private unnamed_addr addrspace(1) constant [13 x i8] c"{6443:\220,0\22}\00", section "llvm.metadata"
-  // CHECK: @[[CACHECONTROL1:.*]] = private unnamed_addr addrspace(1) constant [13 x i8] c"{6443:\221,0\22}\00", section "llvm.metadata"
-  // CHECK-LABEL: define void @store
-  // CHECK-SAME: ptr addrspace(1) %[[ARG0:.*]], i32 %[[ARG1:.*]])
-  llvm.func @store(%arg0: !llvm.ptr<1>, %arg1: i32) {
-    // CHECK: %[[ANNOTATED:.*]] = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(ptr addrspace(1) %[[ARG0]], ptr addrspace(1) @[[CACHECONTROL]],
-    // CHECK: %[[ANNOTATED1:.*]] = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(ptr addrspace(1) %[[ANNOTATED]], ptr addrspace(1) @[[CACHECONTROL1]],
-    // CHECK: store i32 %[[ARG1]], ptr addrspace(1) %[[ANNOTATED1]], align 4
-    llvm.store %arg1, %arg0 {xevm.DecorationCacheControl = [[6443 : i32, 0 : i32, 2 : i32, 0 : i32], [6443 : i32, 1 : i32, 2 : i32, 0 : i32]]} : i32, !llvm.ptr<1>
-    llvm.return
-  }
-}

>From 0bba4fb9fed3828eba1fbe20734cdf128e6b1617 Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Sun, 1 Mar 2026 20:38:56 +0000
Subject: [PATCH 9/9] Fix an end of line formatting.

---
 mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
index ffa5f1df0fcc3..8603ef9cf32af 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/XeVM/XeVMToLLVMIRTranslation.cpp
@@ -60,4 +60,4 @@ void mlir::registerXeVMDialectTranslation(::mlir::MLIRContext &context) {
   DialectRegistry registry;
   registerXeVMDialectTranslation(registry);
   context.appendDialectRegistry(registry);
-}
\ No newline at end of file
+}



More information about the Mlir-commits mailing list