[llvm] [TySan] Support the new size-aware TBAA metadata format (PR #226568)

via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 26 01:34:39 PDT 2026


https://github.com/OfekShilon updated https://github.com/llvm/llvm-project/pull/226568

>From c942f34e5d07e036be63c435939d1570811cf187 Mon Sep 17 00:00:00 2001
From: Ofek Shilon <ofekshilon at gmail.com>
Date: Fri, 25 Sep 2026 17:05:57 +0300
Subject: [PATCH 1/2] [IR][NFC] Share the TBAA metadata node readers

The !tbaa operand layout is open-coded in four places: the node wrappers
in TypeBasedAliasAnalysis.cpp, the structural checks in Verifier.cpp,
createMutableTBAAAccessTag() in MDBuilder.cpp, and TypeSanitizer.cpp.
Three of those are in llvm/lib/IR, which cannot depend on llvm/Analysis,
so the existing wrappers were unreachable from most of the duplication.

Move TBAANodeImpl, TBAAStructTagNodeImpl, TBAAStructTypeNode and
isNewFormatTypeNode out of the anonymous namespace in
TypeBasedAliasAnalysis.cpp into a new header, llvm/IR/TBAAMetadata.h,
next to the MDBuilder methods that create these nodes, and use them from
Verifier.cpp and MDBuilder.cpp.

  - isNewFormatTypeNode() now tolerates a null node and a null first
    operand, making it a superset of the copy it replaces in Verifier.cpp.
  - TBAAStructTypeNode::getFieldOffset() is new; field offsets were
    previously only reachable through getField()'s offset-walking logic.
  - Verifier.cpp keeps its own field-list walk. Its job is to reject
    malformed metadata, so it cannot use accessors that assume the
    invariants it is there to check; only the format predicate is shared.

createMutableTBAAAccessTag() had no test coverage, and the immutability
flag it looks up is at a different operand in each format, so a unit test
for the four tag shapes is added alongside.

No functional change intended.
---
 llvm/include/llvm/IR/TBAAMetadata.h          | 275 +++++++++++++++++++
 llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp | 234 +---------------
 llvm/lib/IR/MDBuilder.cpp                    |  28 +-
 llvm/lib/IR/Verifier.cpp                     |  12 +-
 llvm/unittests/IR/MDBuilderTest.cpp          |  34 +++
 5 files changed, 322 insertions(+), 261 deletions(-)
 create mode 100644 llvm/include/llvm/IR/TBAAMetadata.h

diff --git a/llvm/include/llvm/IR/TBAAMetadata.h b/llvm/include/llvm/IR/TBAAMetadata.h
new file mode 100644
index 00000000000000..3f9a144151d65f
--- /dev/null
+++ b/llvm/include/llvm/IR/TBAAMetadata.h
@@ -0,0 +1,275 @@
+//===- TBAAMetadata.h - Type-Based Alias Analysis metadata ------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// Readers for the !tbaa metadata format described in LangRef.
+///
+/// TBAA metadata exists in two formats, and the operand layout differs between
+/// them:
+///
+///   old scalar:     { name, parent, offset }
+///   old struct:     { name, field, offset, field, offset, ... }
+///   old access tag: { base type, access type, offset }
+///
+///   new scalar:     { parent, size, name }
+///   new struct:     { parent, size, name, field, offset, size, ... }
+///   new access tag: { base type, access type, offset, size }
+///
+/// These wrappers hide that difference so that producers and consumers of TBAA
+/// metadata do not each have to open-code the operand indices. The nodes
+/// themselves are created by the createTBAA*() methods of MDBuilder.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_IR_TBAAMETADATA_H
+#define LLVM_IR_TBAAMETADATA_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/Support/Casting.h"
+#include <cstdint>
+
+namespace llvm {
+
+/// isNewFormatTypeNode - Return true iff the given type node is in the new
+/// size-aware format.
+inline bool isNewFormatTypeNode(const MDNode *N) {
+  if (!N || N->getNumOperands() < 3)
+    return false;
+  // In the new format type nodes have a reference to the parent type as their
+  // first operand; in the old format the first operand is the name string.
+  return isa_and_nonnull<MDNode>(N->getOperand(0));
+}
+
+/// This is a simple wrapper around an MDNode which provides a higher-level
+/// interface by hiding the details of how alias analysis information is encoded
+/// in its operands.
+template <typename MDNodeTy> class TBAANodeImpl {
+  MDNodeTy *Node = nullptr;
+
+public:
+  TBAANodeImpl() = default;
+  explicit TBAANodeImpl(MDNodeTy *N) : Node(N) {}
+
+  /// getNode - Get the MDNode for this TBAANode.
+  MDNodeTy *getNode() const { return Node; }
+
+  /// isNewFormat - Return true iff the wrapped type node is in the new
+  /// size-aware format.
+  bool isNewFormat() const { return isNewFormatTypeNode(Node); }
+
+  /// getParent - Get this TBAANode's Alias tree parent.
+  TBAANodeImpl<MDNodeTy> getParent() const {
+    if (isNewFormat())
+      return TBAANodeImpl(cast<MDNodeTy>(Node->getOperand(0)));
+
+    if (Node->getNumOperands() < 2)
+      return TBAANodeImpl<MDNodeTy>();
+    MDNodeTy *P = dyn_cast_or_null<MDNodeTy>(Node->getOperand(1));
+    if (!P)
+      return TBAANodeImpl<MDNodeTy>();
+    // Ok, this node has a valid parent. Return it.
+    return TBAANodeImpl<MDNodeTy>(P);
+  }
+
+  /// Test if this TBAANode represents a type for objects which are
+  /// not modified (by any means) in the context where this
+  /// AliasAnalysis is relevant.
+  bool isTypeImmutable() const {
+    if (Node->getNumOperands() < 3)
+      return false;
+    ConstantInt *CI = mdconst::dyn_extract<ConstantInt>(Node->getOperand(2));
+    if (!CI)
+      return false;
+    return CI->getValue()[0];
+  }
+};
+
+/// \name Specializations of \c TBAANodeImpl for const and non const qualified
+/// \c MDNode.
+/// @{
+using TBAANode = TBAANodeImpl<const MDNode>;
+using MutableTBAANode = TBAANodeImpl<MDNode>;
+/// @}
+
+/// This is a simple wrapper around an MDNode which provides a
+/// higher-level interface by hiding the details of how alias analysis
+/// information is encoded in its operands.
+template <typename MDNodeTy> class TBAAStructTagNodeImpl {
+  /// This node should be created with createTBAAAccessTag().
+  MDNodeTy *Node;
+
+public:
+  explicit TBAAStructTagNodeImpl(MDNodeTy *N) : Node(N) {}
+
+  /// Get the MDNode for this TBAAStructTagNode.
+  MDNodeTy *getNode() const { return Node; }
+
+  /// isNewFormat - Return true iff the wrapped access tag is in the new
+  /// size-aware format.
+  bool isNewFormat() const {
+    if (Node->getNumOperands() < 4)
+      return false;
+    if (MDNodeTy *AccessType = getAccessType())
+      if (!TBAANodeImpl<MDNodeTy>(AccessType).isNewFormat())
+        return false;
+    return true;
+  }
+
+  MDNodeTy *getBaseType() const {
+    return dyn_cast_or_null<MDNode>(Node->getOperand(0));
+  }
+
+  MDNodeTy *getAccessType() const {
+    return dyn_cast_or_null<MDNode>(Node->getOperand(1));
+  }
+
+  uint64_t getOffset() const {
+    return mdconst::extract<ConstantInt>(Node->getOperand(2))->getZExtValue();
+  }
+
+  uint64_t getSize() const {
+    if (!isNewFormat())
+      return UINT64_MAX;
+    return mdconst::extract<ConstantInt>(Node->getOperand(3))->getZExtValue();
+  }
+
+  /// Test if this TBAAStructTagNode represents a type for objects
+  /// which are not modified (by any means) in the context where this
+  /// AliasAnalysis is relevant.
+  bool isTypeImmutable() const {
+    unsigned OpNo = isNewFormat() ? 4 : 3;
+    if (Node->getNumOperands() < OpNo + 1)
+      return false;
+    ConstantInt *CI = mdconst::dyn_extract<ConstantInt>(Node->getOperand(OpNo));
+    if (!CI)
+      return false;
+    return CI->getValue()[0];
+  }
+};
+
+/// \name Specializations of \c TBAAStructTagNodeImpl for const and non const
+/// qualified \c MDNods.
+/// @{
+using TBAAStructTagNode = TBAAStructTagNodeImpl<const MDNode>;
+using MutableTBAAStructTagNode = TBAAStructTagNodeImpl<MDNode>;
+/// @}
+
+/// This is a simple wrapper around an MDNode which provides a
+/// higher-level interface by hiding the details of how alias analysis
+/// information is encoded in its operands.
+class TBAAStructTypeNode {
+  /// This node should be created with createTBAATypeNode().
+  const MDNode *Node = nullptr;
+
+public:
+  TBAAStructTypeNode() = default;
+  explicit TBAAStructTypeNode(const MDNode *N) : Node(N) {}
+
+  /// Get the MDNode for this TBAAStructTypeNode.
+  const MDNode *getNode() const { return Node; }
+
+  /// isNewFormat - Return true iff the wrapped type node is in the new
+  /// size-aware format.
+  bool isNewFormat() const { return isNewFormatTypeNode(Node); }
+
+  bool operator==(const TBAAStructTypeNode &Other) const {
+    return getNode() == Other.getNode();
+  }
+
+  /// getId - Return type identifier.
+  Metadata *getId() const { return Node->getOperand(isNewFormat() ? 2 : 0); }
+
+  unsigned getNumFields() const {
+    unsigned FirstFieldOpNo = isNewFormat() ? 3 : 1;
+    unsigned NumOpsPerField = isNewFormat() ? 3 : 2;
+    return (getNode()->getNumOperands() - FirstFieldOpNo) / NumOpsPerField;
+  }
+
+  TBAAStructTypeNode getFieldType(unsigned FieldIndex) const {
+    unsigned FirstFieldOpNo = isNewFormat() ? 3 : 1;
+    unsigned NumOpsPerField = isNewFormat() ? 3 : 2;
+    unsigned OpIndex = FirstFieldOpNo + FieldIndex * NumOpsPerField;
+    auto *TypeNode = cast<MDNode>(getNode()->getOperand(OpIndex));
+    return TBAAStructTypeNode(TypeNode);
+  }
+
+  /// Get the offset of the field at \p FieldIndex within this type.
+  uint64_t getFieldOffset(unsigned FieldIndex) const {
+    unsigned FirstFieldOpNo = isNewFormat() ? 3 : 1;
+    unsigned NumOpsPerField = isNewFormat() ? 3 : 2;
+    unsigned OpIndex = FirstFieldOpNo + FieldIndex * NumOpsPerField;
+    return mdconst::extract<ConstantInt>(getNode()->getOperand(OpIndex + 1))
+        ->getZExtValue();
+  }
+
+  /// Get this TBAAStructTypeNode's field in the type DAG with
+  /// given offset. Update the offset to be relative to the field type.
+  TBAAStructTypeNode getField(uint64_t &Offset) const {
+    bool NewFormat = isNewFormat();
+    const ArrayRef<MDOperand> Operands = Node->operands();
+    const unsigned NumOperands = Operands.size();
+
+    if (NewFormat) {
+      // New-format root and scalar type nodes have no fields.
+      if (NumOperands < 6)
+        return TBAAStructTypeNode();
+    } else {
+      // Parent can be omitted for the root node.
+      if (NumOperands < 2)
+        return TBAAStructTypeNode();
+
+      // Fast path for a scalar type node and a struct type node with a single
+      // field.
+      if (NumOperands <= 3) {
+        uint64_t Cur =
+            NumOperands == 2
+                ? 0
+                : mdconst::extract<ConstantInt>(Operands[2])->getZExtValue();
+        Offset -= Cur;
+        MDNode *P = dyn_cast_or_null<MDNode>(Operands[1]);
+        if (!P)
+          return TBAAStructTypeNode();
+        return TBAAStructTypeNode(P);
+      }
+    }
+
+    // Assume the offsets are in order. We return the previous field if
+    // the current offset is bigger than the given offset.
+    unsigned FirstFieldOpNo = NewFormat ? 3 : 1;
+    unsigned NumOpsPerField = NewFormat ? 3 : 2;
+    unsigned TheIdx = 0;
+
+    for (unsigned Idx = FirstFieldOpNo; Idx < NumOperands;
+         Idx += NumOpsPerField) {
+      uint64_t Cur =
+          mdconst::extract<ConstantInt>(Operands[Idx + 1])->getZExtValue();
+      if (Cur > Offset) {
+        assert(Idx >= FirstFieldOpNo + NumOpsPerField &&
+               "TBAAStructTypeNode::getField should have an offset match!");
+        TheIdx = Idx - NumOpsPerField;
+        break;
+      }
+    }
+    // Move along the last field.
+    if (TheIdx == 0)
+      TheIdx = NumOperands - NumOpsPerField;
+    uint64_t Cur =
+        mdconst::extract<ConstantInt>(Operands[TheIdx + 1])->getZExtValue();
+    Offset -= Cur;
+    MDNode *P = dyn_cast_or_null<MDNode>(Operands[TheIdx]);
+    if (!P)
+      return TBAAStructTypeNode();
+    return TBAAStructTypeNode(P);
+  }
+};
+
+} // end namespace llvm
+
+#endif // LLVM_IR_TBAAMETADATA_H
diff --git a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
index dbe4ccac7801da..75b12d0325b0eb 100644
--- a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
@@ -110,6 +110,7 @@
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Metadata.h"
 #include "llvm/IR/Module.h"
+#include "llvm/IR/TBAAMetadata.h"
 #include "llvm/InitializePasses.h"
 #include "llvm/Pass.h"
 #include "llvm/Support/Casting.h"
@@ -125,239 +126,6 @@ using namespace llvm;
 // more convenient.
 static cl::opt<bool> EnableTBAA("enable-tbaa", cl::init(true), cl::Hidden);
 
-namespace {
-
-/// isNewFormatTypeNode - Return true iff the given type node is in the new
-/// size-aware format.
-static bool isNewFormatTypeNode(const MDNode *N) {
-  if (N->getNumOperands() < 3)
-    return false;
-  // In the old format the first operand is a string.
-  if (!isa<MDNode>(N->getOperand(0)))
-    return false;
-  return true;
-}
-
-/// This is a simple wrapper around an MDNode which provides a higher-level
-/// interface by hiding the details of how alias analysis information is encoded
-/// in its operands.
-template<typename MDNodeTy>
-class TBAANodeImpl {
-  MDNodeTy *Node = nullptr;
-
-public:
-  TBAANodeImpl() = default;
-  explicit TBAANodeImpl(MDNodeTy *N) : Node(N) {}
-
-  /// getNode - Get the MDNode for this TBAANode.
-  MDNodeTy *getNode() const { return Node; }
-
-  /// isNewFormat - Return true iff the wrapped type node is in the new
-  /// size-aware format.
-  bool isNewFormat() const { return isNewFormatTypeNode(Node); }
-
-  /// getParent - Get this TBAANode's Alias tree parent.
-  TBAANodeImpl<MDNodeTy> getParent() const {
-    if (isNewFormat())
-      return TBAANodeImpl(cast<MDNodeTy>(Node->getOperand(0)));
-
-    if (Node->getNumOperands() < 2)
-      return TBAANodeImpl<MDNodeTy>();
-    MDNodeTy *P = dyn_cast_or_null<MDNodeTy>(Node->getOperand(1));
-    if (!P)
-      return TBAANodeImpl<MDNodeTy>();
-    // Ok, this node has a valid parent. Return it.
-    return TBAANodeImpl<MDNodeTy>(P);
-  }
-
-  /// Test if this TBAANode represents a type for objects which are
-  /// not modified (by any means) in the context where this
-  /// AliasAnalysis is relevant.
-  bool isTypeImmutable() const {
-    if (Node->getNumOperands() < 3)
-      return false;
-    ConstantInt *CI = mdconst::dyn_extract<ConstantInt>(Node->getOperand(2));
-    if (!CI)
-      return false;
-    return CI->getValue()[0];
-  }
-};
-
-/// \name Specializations of \c TBAANodeImpl for const and non const qualified
-/// \c MDNode.
-/// @{
-using TBAANode = TBAANodeImpl<const MDNode>;
-using MutableTBAANode = TBAANodeImpl<MDNode>;
-/// @}
-
-/// This is a simple wrapper around an MDNode which provides a
-/// higher-level interface by hiding the details of how alias analysis
-/// information is encoded in its operands.
-template<typename MDNodeTy>
-class TBAAStructTagNodeImpl {
-  /// This node should be created with createTBAAAccessTag().
-  MDNodeTy *Node;
-
-public:
-  explicit TBAAStructTagNodeImpl(MDNodeTy *N) : Node(N) {}
-
-  /// Get the MDNode for this TBAAStructTagNode.
-  MDNodeTy *getNode() const { return Node; }
-
-  /// isNewFormat - Return true iff the wrapped access tag is in the new
-  /// size-aware format.
-  bool isNewFormat() const {
-    if (Node->getNumOperands() < 4)
-      return false;
-    if (MDNodeTy *AccessType = getAccessType())
-      if (!TBAANodeImpl<MDNodeTy>(AccessType).isNewFormat())
-        return false;
-    return true;
-  }
-
-  MDNodeTy *getBaseType() const {
-    return dyn_cast_or_null<MDNode>(Node->getOperand(0));
-  }
-
-  MDNodeTy *getAccessType() const {
-    return dyn_cast_or_null<MDNode>(Node->getOperand(1));
-  }
-
-  uint64_t getOffset() const {
-    return mdconst::extract<ConstantInt>(Node->getOperand(2))->getZExtValue();
-  }
-
-  uint64_t getSize() const {
-    if (!isNewFormat())
-      return UINT64_MAX;
-    return mdconst::extract<ConstantInt>(Node->getOperand(3))->getZExtValue();
-  }
-
-  /// Test if this TBAAStructTagNode represents a type for objects
-  /// which are not modified (by any means) in the context where this
-  /// AliasAnalysis is relevant.
-  bool isTypeImmutable() const {
-    unsigned OpNo = isNewFormat() ? 4 : 3;
-    if (Node->getNumOperands() < OpNo + 1)
-      return false;
-    ConstantInt *CI = mdconst::dyn_extract<ConstantInt>(Node->getOperand(OpNo));
-    if (!CI)
-      return false;
-    return CI->getValue()[0];
-  }
-};
-
-/// \name Specializations of \c TBAAStructTagNodeImpl for const and non const
-/// qualified \c MDNods.
-/// @{
-using TBAAStructTagNode = TBAAStructTagNodeImpl<const MDNode>;
-using MutableTBAAStructTagNode = TBAAStructTagNodeImpl<MDNode>;
-/// @}
-
-/// This is a simple wrapper around an MDNode which provides a
-/// higher-level interface by hiding the details of how alias analysis
-/// information is encoded in its operands.
-class TBAAStructTypeNode {
-  /// This node should be created with createTBAATypeNode().
-  const MDNode *Node = nullptr;
-
-public:
-  TBAAStructTypeNode() = default;
-  explicit TBAAStructTypeNode(const MDNode *N) : Node(N) {}
-
-  /// Get the MDNode for this TBAAStructTypeNode.
-  const MDNode *getNode() const { return Node; }
-
-  /// isNewFormat - Return true iff the wrapped type node is in the new
-  /// size-aware format.
-  bool isNewFormat() const { return isNewFormatTypeNode(Node); }
-
-  bool operator==(const TBAAStructTypeNode &Other) const {
-    return getNode() == Other.getNode();
-  }
-
-  /// getId - Return type identifier.
-  Metadata *getId() const {
-    return Node->getOperand(isNewFormat() ? 2 : 0);
-  }
-
-  unsigned getNumFields() const {
-    unsigned FirstFieldOpNo = isNewFormat() ? 3 : 1;
-    unsigned NumOpsPerField = isNewFormat() ? 3 : 2;
-    return (getNode()->getNumOperands() - FirstFieldOpNo) / NumOpsPerField;
-  }
-
-  TBAAStructTypeNode getFieldType(unsigned FieldIndex) const {
-    unsigned FirstFieldOpNo = isNewFormat() ? 3 : 1;
-    unsigned NumOpsPerField = isNewFormat() ? 3 : 2;
-    unsigned OpIndex = FirstFieldOpNo + FieldIndex * NumOpsPerField;
-    auto *TypeNode = cast<MDNode>(getNode()->getOperand(OpIndex));
-    return TBAAStructTypeNode(TypeNode);
-  }
-
-  /// Get this TBAAStructTypeNode's field in the type DAG with
-  /// given offset. Update the offset to be relative to the field type.
-  TBAAStructTypeNode getField(uint64_t &Offset) const {
-    bool NewFormat = isNewFormat();
-    const ArrayRef<MDOperand> Operands = Node->operands();
-    const unsigned NumOperands = Operands.size();
-
-    if (NewFormat) {
-      // New-format root and scalar type nodes have no fields.
-      if (NumOperands < 6)
-        return TBAAStructTypeNode();
-    } else {
-      // Parent can be omitted for the root node.
-      if (NumOperands < 2)
-        return TBAAStructTypeNode();
-
-      // Fast path for a scalar type node and a struct type node with a single
-      // field.
-      if (NumOperands <= 3) {
-        uint64_t Cur =
-            NumOperands == 2
-                ? 0
-                : mdconst::extract<ConstantInt>(Operands[2])->getZExtValue();
-        Offset -= Cur;
-        MDNode *P = dyn_cast_or_null<MDNode>(Operands[1]);
-        if (!P)
-          return TBAAStructTypeNode();
-        return TBAAStructTypeNode(P);
-      }
-    }
-
-    // Assume the offsets are in order. We return the previous field if
-    // the current offset is bigger than the given offset.
-    unsigned FirstFieldOpNo = NewFormat ? 3 : 1;
-    unsigned NumOpsPerField = NewFormat ? 3 : 2;
-    unsigned TheIdx = 0;
-
-    for (unsigned Idx = FirstFieldOpNo; Idx < NumOperands;
-         Idx += NumOpsPerField) {
-      uint64_t Cur =
-          mdconst::extract<ConstantInt>(Operands[Idx + 1])->getZExtValue();
-      if (Cur > Offset) {
-        assert(Idx >= FirstFieldOpNo + NumOpsPerField &&
-               "TBAAStructTypeNode::getField should have an offset match!");
-        TheIdx = Idx - NumOpsPerField;
-        break;
-      }
-    }
-    // Move along the last field.
-    if (TheIdx == 0)
-      TheIdx = NumOperands - NumOpsPerField;
-    uint64_t Cur =
-        mdconst::extract<ConstantInt>(Operands[TheIdx + 1])->getZExtValue();
-    Offset -= Cur;
-    MDNode *P = dyn_cast_or_null<MDNode>(Operands[TheIdx]);
-    if (!P)
-      return TBAAStructTypeNode();
-    return TBAAStructTypeNode(P);
-  }
-};
-
-} // end anonymous namespace
-
 AliasResult TypeBasedAAResult::alias(const MemoryLocation &LocA,
                                      const MemoryLocation &LocB,
                                      AAQueryInfo &AAQI, const Instruction *) {
diff --git a/llvm/lib/IR/MDBuilder.cpp b/llvm/lib/IR/MDBuilder.cpp
index 9a8e417b43ba79..def60615f0469c 100644
--- a/llvm/lib/IR/MDBuilder.cpp
+++ b/llvm/lib/IR/MDBuilder.cpp
@@ -16,6 +16,7 @@
 #include "llvm/IR/Function.h"
 #include "llvm/IR/Metadata.h"
 #include "llvm/IR/ProfDataUtils.h"
+#include "llvm/IR/TBAAMetadata.h"
 using namespace llvm;
 
 MDString *MDBuilder::createString(StringRef Str) {
@@ -314,30 +315,21 @@ MDNode *MDBuilder::createTBAAAccessTag(MDNode *BaseType, MDNode *AccessType,
 }
 
 MDNode *MDBuilder::createMutableTBAAAccessTag(MDNode *Tag) {
-  MDNode *BaseType = cast<MDNode>(Tag->getOperand(0));
-  MDNode *AccessType = cast<MDNode>(Tag->getOperand(1));
-  Metadata *OffsetNode = Tag->getOperand(2);
-  uint64_t Offset = mdconst::extract<ConstantInt>(OffsetNode)->getZExtValue();
-
-  bool NewFormat = isa<MDNode>(AccessType->getOperand(0));
-
-  // See if the tag is already mutable.
-  unsigned ImmutabilityFlagOp = NewFormat ? 4 : 3;
-  if (Tag->getNumOperands() <= ImmutabilityFlagOp)
-    return Tag;
-
-  // If Tag is already mutable then return it.
-  Metadata *ImmutabilityFlagNode = Tag->getOperand(ImmutabilityFlagOp);
-  if (!mdconst::extract<ConstantInt>(ImmutabilityFlagNode)->getValue())
+  MutableTBAAStructTagNode TagNode(Tag);
+  MDNode *BaseType = TagNode.getBaseType();
+  MDNode *AccessType = TagNode.getAccessType();
+  uint64_t Offset = TagNode.getOffset();
+  bool NewFormat = isNewFormatTypeNode(AccessType);
+
+  // If the tag has no immutability flag, or is already mutable, return it.
+  if (!TagNode.isTypeImmutable())
     return Tag;
 
   // Otherwise, create another node.
   if (!NewFormat)
     return createTBAAStructTagNode(BaseType, AccessType, Offset);
 
-  Metadata *SizeNode = Tag->getOperand(3);
-  uint64_t Size = mdconst::extract<ConstantInt>(SizeNode)->getZExtValue();
-  return createTBAAAccessTag(BaseType, AccessType, Offset, Size);
+  return createTBAAAccessTag(BaseType, AccessType, Offset, TagNode.getSize());
 }
 
 MDNode *MDBuilder::createIrrLoopHeaderWeight(uint64_t Weight) {
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 1440b95896474f..765749100dc5f0 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -109,6 +109,7 @@
 #include "llvm/IR/PassManager.h"
 #include "llvm/IR/ProfDataUtils.h"
 #include "llvm/IR/Statepoint.h"
+#include "llvm/IR/TBAAMetadata.h"
 #include "llvm/IR/Type.h"
 #include "llvm/IR/Use.h"
 #include "llvm/IR/User.h"
@@ -8230,15 +8231,6 @@ MDNode *TBAAVerifier::getFieldNodeFromTBAABaseNode(const Instruction *I,
   return cast<MDNode>(BaseNode->getOperand(LastIdx));
 }
 
-static bool isNewFormatTBAATypeNode(llvm::MDNode *Type) {
-  if (!Type || Type->getNumOperands() < 3)
-    return false;
-
-  // In the new format type nodes shall have a reference to the parent type as
-  // its first operand.
-  return isa_and_nonnull<MDNode>(Type->getOperand(0));
-}
-
 bool TBAAVerifier::visitTBAAMetadata(const Instruction *I, const MDNode *MD) {
   CheckTBAA(MD->getNumOperands() > 0, "TBAA metadata cannot have 0 operands", I,
             MD);
@@ -8259,7 +8251,7 @@ bool TBAAVerifier::visitTBAAMetadata(const Instruction *I, const MDNode *MD) {
   auto *BaseNode = dyn_cast_or_null<MDNode>(MD->getOperand(0));
   auto *AccessType = dyn_cast_or_null<MDNode>(MD->getOperand(1));
 
-  bool IsNewFormat = isNewFormatTBAATypeNode(AccessType);
+  bool IsNewFormat = isNewFormatTypeNode(AccessType);
 
   if (IsNewFormat) {
     CheckTBAA(MD->getNumOperands() == 4 || MD->getNumOperands() == 5,
diff --git a/llvm/unittests/IR/MDBuilderTest.cpp b/llvm/unittests/IR/MDBuilderTest.cpp
index a923418a05d644..78ceb6db9ae064 100644
--- a/llvm/unittests/IR/MDBuilderTest.cpp
+++ b/llvm/unittests/IR/MDBuilderTest.cpp
@@ -105,6 +105,40 @@ TEST_F(MDBuilderTest, createTBAANode) {
   EXPECT_EQ(mdconst::extract<ConstantInt>(N2->getOperand(2))->getZExtValue(),
             1U);
 }
+TEST_F(MDBuilderTest, createMutableTBAAAccessTag) {
+  MDBuilder MDHelper(Context);
+  MDNode *Root = MDHelper.createTBAARoot("Root");
+
+  // Old-format scalar type node: { name, parent, offset }.
+  MDNode *OldTy = MDHelper.createTBAANode("Scalar", Root);
+  // New-format scalar type node: { parent, size, name }.
+  MDNode *NewTy =
+      MDHelper.createTBAATypeNode(Root, 4, MDHelper.createString("Scalar"));
+
+  // A tag with no immutability flag is already mutable and is returned as is.
+  MDNode *OldMutable = MDHelper.createTBAAStructTagNode(OldTy, OldTy, 0);
+  EXPECT_EQ(MDHelper.createMutableTBAAAccessTag(OldMutable), OldMutable);
+  MDNode *NewMutable = MDHelper.createTBAAAccessTag(NewTy, NewTy, 0, 4);
+  EXPECT_EQ(MDHelper.createMutableTBAAAccessTag(NewMutable), NewMutable);
+
+  // An immutable tag is rebuilt as the corresponding mutable one, in the same
+  // format. The immutability flag sits at operand 3 in the old format and at
+  // operand 4 in the new one, so this is where the two layouts diverge.
+  MDNode *OldImmutable =
+      MDHelper.createTBAAStructTagNode(OldTy, OldTy, 0, /*IsConstant=*/true);
+  ASSERT_EQ(OldImmutable->getNumOperands(), 4U);
+  MDNode *OldResult = MDHelper.createMutableTBAAAccessTag(OldImmutable);
+  EXPECT_NE(OldResult, OldImmutable);
+  EXPECT_EQ(OldResult, OldMutable);
+
+  MDNode *NewImmutable =
+      MDHelper.createTBAAAccessTag(NewTy, NewTy, 0, 4, /*IsImmutable=*/true);
+  ASSERT_EQ(NewImmutable->getNumOperands(), 5U);
+  MDNode *NewResult = MDHelper.createMutableTBAAAccessTag(NewImmutable);
+  EXPECT_NE(NewResult, NewImmutable);
+  EXPECT_EQ(NewResult, NewMutable);
+}
+
 TEST_F(MDBuilderTest, createPCSections) {
   MDBuilder MDHelper(Context);
   ConstantInt *C1 = ConstantInt::get(Context, APInt(8, 1));

>From a14ca1483c10ae58506029e2939740d6867cb112 Mon Sep 17 00:00:00 2001
From: Ofek Shilon <ofekshilon at gmail.com>
Date: Fri, 25 Sep 2026 17:06:09 +0300
Subject: [PATCH 2/2] [TySan] Support the new size-aware TBAA metadata format

TypeSanitizer only understood the old TBAA type-node layout, in which the
type name is the first operand. Under -new-struct-path-tbaa the first
operand is the parent type node and the name moves to operand 2, so
generateBaseTypeDescriptor() failed to find a name and returned false. Its
caller then abandoned instrumentation for the entire function:

    if (!generateTypeDescriptor(MD, TypeDescriptors, TypeNames, M))
      return Res; // Giving up.

As a result, -fsanitize=type combined with -new-struct-path-tbaa silently
produced no instrumentation at all -- no __tysan_check calls and no
__tysan_v1_* type descriptors -- with no diagnostic, so every
type-aliasing violation went undetected. This affected plain scalar
accesses as well as structs, leaving TySan entirely non-functional in that
mode.

Read the metadata through the shared TBAAStructTypeNode accessors instead
of open-coding the old operand layout.

Note that in the old format a scalar type node's parent occupies the first
field slot, so scalars and structs are both covered by the field list. The
new format keeps the parent in a separate operand and gives a scalar no
fields at all, so it has to be added explicitly. The runtime depends on
this: getRootTD() follows Members[0] to find the root of the TBAA tree.

The emitted descriptor layout is unchanged, so no compiler-rt change is
needed: for a given type hierarchy both metadata formats now produce
byte-identical __tysan_v1_* descriptors. The new test asserts exactly the
same descriptor globals as basic.ll, which encodes the same hierarchy in
the old format.

Fixes #226169
---
 .../Instrumentation/TypeSanitizer.cpp         |  72 +++--
 .../TypeSanitizer/new-struct-path-tbaa.ll     | 265 ++++++++++++++++++
 2 files changed, 316 insertions(+), 21 deletions(-)
 create mode 100644 llvm/test/Instrumentation/TypeSanitizer/new-struct-path-tbaa.ll

diff --git a/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
index cea6e9e316c1d6..2c35c0d04fba5b 100644
--- a/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
@@ -29,6 +29,7 @@
 #include "llvm/IR/MDBuilder.h"
 #include "llvm/IR/Metadata.h"
 #include "llvm/IR/Module.h"
+#include "llvm/IR/TBAAMetadata.h"
 #include "llvm/IR/Type.h"
 #include "llvm/ProfileData/InstrProf.h"
 #include "llvm/Support/CommandLine.h"
@@ -260,24 +261,61 @@ static std::string encodeName(StringRef Name) {
   return Output;
 }
 
+using TBAAMemberList = SmallVectorImpl<std::pair<const MDNode *, uint64_t>>;
+
+/// Return the MDString naming the type described by \p N, or nullptr if \p N
+/// is not a well-formed type node.
+static MDString *getTypeNameNode(const MDNode *N) {
+  if (!N->getNumOperands())
+    return nullptr;
+  return dyn_cast<MDString>(TBAAStructTypeNode(N).getId());
+}
+
+/// Collect the (member type node, offset) pairs that \p N contributes to its
+/// TySan type descriptor, appending them to \p Members.
+///
+/// In the old format a scalar type node's parent occupies the first field
+/// slot, so scalars and structs are both covered by the field list. The new
+/// format keeps the parent in a separate operand and gives a scalar no fields
+/// at all, so it is added explicitly here. The runtime depends on this:
+/// getRootTD() follows Members[0] to find the root of the TBAA tree.
+static bool collectTypeMembers(const MDNode *N, TBAAMemberList &Members) {
+  TBAAStructTypeNode TypeNode(N);
+  unsigned NumFields = TypeNode.getNumFields();
+
+  if (TypeNode.isNewFormat() && NumFields == 0) {
+    // A scalar, or a struct with no fields. Either way the parent is the only
+    // edge towards the root.
+    const auto *Parent = dyn_cast<MDNode>(N->getOperand(0));
+    if (!Parent)
+      return false;
+    Members.emplace_back(Parent, 0);
+    return true;
+  }
+
+  for (unsigned I = 0; I != NumFields; ++I)
+    Members.emplace_back(TypeNode.getFieldType(I).getNode(),
+                         TypeNode.getFieldOffset(I));
+
+  return true;
+}
+
 std::string
 TypeSanitizer::getAnonymousStructIdentifier(const MDNode *MD,
                                             TypeNameMapTy &TypeNames) {
   MD5 Hash;
 
-  for (int i = 1, e = MD->getNumOperands(); i < e; i += 2) {
-    const MDNode *MemberNode = dyn_cast<MDNode>(MD->getOperand(i));
-    if (!MemberNode)
-      return "";
+  SmallVector<std::pair<const MDNode *, uint64_t>> Members;
+  if (!collectTypeMembers(MD, Members))
+    return "";
 
+  for (const auto &[MemberNode, Offset] : Members) {
     auto TNI = TypeNames.find(MemberNode);
     std::string MemberName;
     if (TNI != TypeNames.end()) {
       MemberName = TNI->second;
     } else {
-      if (MemberNode->getNumOperands() < 1)
-        return "";
-      MDString *MemberNameNode = dyn_cast<MDString>(MemberNode->getOperand(0));
+      MDString *MemberNameNode = getTypeNameNode(MemberNode);
       if (!MemberNameNode)
         return "";
       MemberName = MemberNameNode->getString().str();
@@ -291,8 +329,6 @@ TypeSanitizer::getAnonymousStructIdentifier(const MDNode *MD,
     Hash.update(MemberName);
     Hash.update("\0");
 
-    uint64_t Offset =
-        mdconst::extract<ConstantInt>(MD->getOperand(i + 1))->getZExtValue();
     Hash.update(utostr(Offset));
     Hash.update("\0");
   }
@@ -305,10 +341,7 @@ TypeSanitizer::getAnonymousStructIdentifier(const MDNode *MD,
 bool TypeSanitizer::generateBaseTypeDescriptor(
     const MDNode *MD, TypeDescriptorsMapTy &TypeDescriptors,
     TypeNameMapTy &TypeNames, Module &M) {
-  if (MD->getNumOperands() < 1)
-    return false;
-
-  MDString *NameNode = dyn_cast<MDString>(MD->getOperand(0));
+  MDString *NameNode = getTypeNameNode(MD);
   if (!NameNode)
     return false;
 
@@ -327,12 +360,12 @@ bool TypeSanitizer::generateBaseTypeDescriptor(
     return true;
   }
 
-  SmallVector<std::pair<Constant *, uint64_t>> Members;
-  for (int i = 1, e = MD->getNumOperands(); i < e; i += 2) {
-    const MDNode *MemberNode = dyn_cast<MDNode>(MD->getOperand(i));
-    if (!MemberNode)
-      return false;
+  SmallVector<std::pair<const MDNode *, uint64_t>> MemberNodes;
+  if (!collectTypeMembers(MD, MemberNodes))
+    return false;
 
+  SmallVector<std::pair<Constant *, uint64_t>> Members;
+  for (const auto &[MemberNode, Offset] : MemberNodes) {
     Constant *Member;
     auto TDI = TypeDescriptors.find(MemberNode);
     if (TDI != TypeDescriptors.end()) {
@@ -345,9 +378,6 @@ bool TypeSanitizer::generateBaseTypeDescriptor(
       Member = TypeDescriptors[MemberNode];
     }
 
-    uint64_t Offset =
-        mdconst::extract<ConstantInt>(MD->getOperand(i + 1))->getZExtValue();
-
     Members.push_back(std::make_pair(Member, Offset));
   }
 
diff --git a/llvm/test/Instrumentation/TypeSanitizer/new-struct-path-tbaa.ll b/llvm/test/Instrumentation/TypeSanitizer/new-struct-path-tbaa.ll
new file mode 100644
index 00000000000000..011e76bb8989f3
--- /dev/null
+++ b/llvm/test/Instrumentation/TypeSanitizer/new-struct-path-tbaa.ll
@@ -0,0 +1,265 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 6
+; Test that type sanitizer instrumentation is also generated for TBAA metadata
+; in the new, size-aware format (clang's -new-struct-path-tbaa).
+;
+; This mirrors basic.ll, with the same type hierarchy expressed in the new
+; format:
+;   old scalar: { name, parent, offset }   new scalar: { parent, size, name }
+;   old struct: { name, field, offset, ... }
+;   new struct: { parent, size, name, field, offset, size, ... }
+;
+; RUN: opt -passes='tysan' -tysan-outline-instrumentation=false -S %s | FileCheck %s --check-prefix=CHECK-INLINE
+; RUN: opt -passes='tysan' -S %s | FileCheck %s --check-prefix=CHECK-OUTLINE
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
+;.
+; CHECK-INLINE: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @tysan.module_ctor, ptr null }]
+; CHECK-INLINE: @__tysan_v1_Simple_20C_2b_2b_20TBAA = linkonce_odr constant { i64, i64, [16 x i8] } { i64 2, i64 0, [16 x i8] c"Simple C++ TBAA\00" }, comdat
+; CHECK-INLINE: @__tysan_v1_omnipotent_20char = linkonce_odr constant { i64, i64, ptr, i64, [16 x i8] } { i64 2, i64 1, ptr @__tysan_v1_Simple_20C_2b_2b_20TBAA, i64 0, [16 x i8] c"omnipotent char\00" }, comdat
+; CHECK-INLINE: @__tysan_v1_int = linkonce_odr constant { i64, i64, ptr, i64, [4 x i8] } { i64 2, i64 1, ptr @__tysan_v1_omnipotent_20char, i64 0, [4 x i8] c"int\00" }, comdat
+; CHECK-INLINE: @__tysan_v1_int_o_0 = linkonce_odr constant { i64, ptr, ptr, i64 } { i64 1, ptr @__tysan_v1_int, ptr @__tysan_v1_int, i64 0 }, comdat
+; CHECK-INLINE: @__tysan_shadow_memory_address = external global i64
+; CHECK-INLINE: @__tysan_app_memory_mask = external global i64
+; CHECK-INLINE: @__tysan_v1___ZTS1x = linkonce_odr constant { i64, i64, ptr, i64, ptr, i64, [7 x i8] } { i64 2, i64 2, ptr @__tysan_v1_int, i64 0, ptr @__tysan_v1_int, i64 4, [7 x i8] c"_ZTS1x\00" }, comdat
+; CHECK-INLINE: @__tysan_v1___ZTS1v = linkonce_odr constant { i64, i64, ptr, i64, ptr, i64, ptr, i64, [7 x i8] } { i64 2, i64 3, ptr @__tysan_v1_int, i64 8, ptr @__tysan_v1_int, i64 12, ptr @__tysan_v1___ZTS1x, i64 16, [7 x i8] c"_ZTS1v\00" }, comdat
+; CHECK-INLINE: @__tysan_v1___ZTS1v_o_12 = linkonce_odr constant { i64, ptr, ptr, i64 } { i64 1, ptr @__tysan_v1___ZTS1v, ptr @__tysan_v1_int, i64 12 }, comdat
+; CHECK-INLINE: @llvm.used = appending global [8 x ptr] [ptr @tysan.module_ctor, ptr @__tysan_v1_Simple_20C_2b_2b_20TBAA, ptr @__tysan_v1_omnipotent_20char, ptr @__tysan_v1_int, ptr @__tysan_v1_int_o_0, ptr @__tysan_v1___ZTS1x, ptr @__tysan_v1___ZTS1v, ptr @__tysan_v1___ZTS1v_o_12], section "llvm.metadata"
+;.
+; CHECK-OUTLINE: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @tysan.module_ctor, ptr null }]
+; CHECK-OUTLINE: @__tysan_v1_Simple_20C_2b_2b_20TBAA = linkonce_odr constant { i64, i64, [16 x i8] } { i64 2, i64 0, [16 x i8] c"Simple C++ TBAA\00" }, comdat
+; CHECK-OUTLINE: @__tysan_v1_omnipotent_20char = linkonce_odr constant { i64, i64, ptr, i64, [16 x i8] } { i64 2, i64 1, ptr @__tysan_v1_Simple_20C_2b_2b_20TBAA, i64 0, [16 x i8] c"omnipotent char\00" }, comdat
+; CHECK-OUTLINE: @__tysan_v1_int = linkonce_odr constant { i64, i64, ptr, i64, [4 x i8] } { i64 2, i64 1, ptr @__tysan_v1_omnipotent_20char, i64 0, [4 x i8] c"int\00" }, comdat
+; CHECK-OUTLINE: @__tysan_v1_int_o_0 = linkonce_odr constant { i64, ptr, ptr, i64 } { i64 1, ptr @__tysan_v1_int, ptr @__tysan_v1_int, i64 0 }, comdat
+; CHECK-OUTLINE: @__tysan_shadow_memory_address = external global i64
+; CHECK-OUTLINE: @__tysan_app_memory_mask = external global i64
+; CHECK-OUTLINE: @__tysan_v1___ZTS1x = linkonce_odr constant { i64, i64, ptr, i64, ptr, i64, [7 x i8] } { i64 2, i64 2, ptr @__tysan_v1_int, i64 0, ptr @__tysan_v1_int, i64 4, [7 x i8] c"_ZTS1x\00" }, comdat
+; CHECK-OUTLINE: @__tysan_v1___ZTS1v = linkonce_odr constant { i64, i64, ptr, i64, ptr, i64, ptr, i64, [7 x i8] } { i64 2, i64 3, ptr @__tysan_v1_int, i64 8, ptr @__tysan_v1_int, i64 12, ptr @__tysan_v1___ZTS1x, i64 16, [7 x i8] c"_ZTS1v\00" }, comdat
+; CHECK-OUTLINE: @__tysan_v1___ZTS1v_o_12 = linkonce_odr constant { i64, ptr, ptr, i64 } { i64 1, ptr @__tysan_v1___ZTS1v, ptr @__tysan_v1_int, i64 12 }, comdat
+; CHECK-OUTLINE: @llvm.used = appending global [8 x ptr] [ptr @tysan.module_ctor, ptr @__tysan_v1_Simple_20C_2b_2b_20TBAA, ptr @__tysan_v1_omnipotent_20char, ptr @__tysan_v1_int, ptr @__tysan_v1_int_o_0, ptr @__tysan_v1___ZTS1x, ptr @__tysan_v1___ZTS1v, ptr @__tysan_v1___ZTS1v_o_12], section "llvm.metadata"
+;.
+define i32 @test_load(ptr %a) sanitize_type {
+; CHECK-INLINE-LABEL: define i32 @test_load(
+; CHECK-INLINE-SAME: ptr [[A:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-INLINE-NEXT:  [[ENTRY:.*:]]
+; CHECK-INLINE-NEXT:    [[APP_MEM_MASK:%.*]] = load i64, ptr @__tysan_app_memory_mask, align 8
+; CHECK-INLINE-NEXT:    [[SHADOW_BASE:%.*]] = load i64, ptr @__tysan_shadow_memory_address, align 8
+; CHECK-INLINE-NEXT:    [[APP_PTR_INT:%.*]] = ptrtoint ptr [[A]] to i64
+; CHECK-INLINE-NEXT:    [[APP_PTR_MASKED:%.*]] = and i64 [[APP_PTR_INT]], [[APP_MEM_MASK]]
+; CHECK-INLINE-NEXT:    [[APP_PTR_SHIFTED:%.*]] = shl i64 [[APP_PTR_MASKED]], 3
+; CHECK-INLINE-NEXT:    [[SHADOW_PTR_INT:%.*]] = add i64 [[APP_PTR_SHIFTED]], [[SHADOW_BASE]]
+; CHECK-INLINE-NEXT:    [[SHADOW_PTR:%.*]] = inttoptr i64 [[SHADOW_PTR_INT]] to ptr
+; CHECK-INLINE-NEXT:    [[SHADOW_DESC:%.*]] = load ptr, ptr [[SHADOW_PTR]], align 8
+; CHECK-INLINE-NEXT:    [[BAD_DESC:%.*]] = icmp ne ptr [[SHADOW_DESC]], @__tysan_v1_int_o_0
+; CHECK-INLINE-NEXT:    br i1 [[BAD_DESC]], label %[[BB0:.*]], label %[[BB22:.*]], !prof [[PROF0:![0-9]+]]
+; CHECK-INLINE:       [[BB0]]:
+; CHECK-INLINE-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[SHADOW_DESC]], null
+; CHECK-INLINE-NEXT:    br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB20:.*]]
+; CHECK-INLINE:       [[BB2]]:
+; CHECK-INLINE-NEXT:    [[TMP3:%.*]] = add i64 [[SHADOW_PTR_INT]], 8
+; CHECK-INLINE-NEXT:    [[TMP4:%.*]] = inttoptr i64 [[TMP3]] to ptr
+; CHECK-INLINE-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8
+; CHECK-INLINE-NEXT:    [[TMP6:%.*]] = icmp ne ptr [[TMP5]], null
+; CHECK-INLINE-NEXT:    [[TMP7:%.*]] = or i1 false, [[TMP6]]
+; CHECK-INLINE-NEXT:    [[TMP8:%.*]] = add i64 [[SHADOW_PTR_INT]], 16
+; CHECK-INLINE-NEXT:    [[TMP9:%.*]] = inttoptr i64 [[TMP8]] to ptr
+; CHECK-INLINE-NEXT:    [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8
+; CHECK-INLINE-NEXT:    [[TMP11:%.*]] = icmp ne ptr [[TMP10]], null
+; CHECK-INLINE-NEXT:    [[TMP12:%.*]] = or i1 [[TMP7]], [[TMP11]]
+; CHECK-INLINE-NEXT:    [[TMP13:%.*]] = add i64 [[SHADOW_PTR_INT]], 24
+; CHECK-INLINE-NEXT:    [[TMP14:%.*]] = inttoptr i64 [[TMP13]] to ptr
+; CHECK-INLINE-NEXT:    [[TMP15:%.*]] = load ptr, ptr [[TMP14]], align 8
+; CHECK-INLINE-NEXT:    [[TMP16:%.*]] = icmp ne ptr [[TMP15]], null
+; CHECK-INLINE-NEXT:    [[TMP17:%.*]] = or i1 [[TMP12]], [[TMP16]]
+; CHECK-INLINE-NEXT:    br i1 [[TMP17]], label %[[BB18:.*]], label %[[BB19:.*]], !prof [[PROF0]]
+; CHECK-INLINE:       [[BB18]]:
+; CHECK-INLINE-NEXT:    call void @__tysan_check(ptr [[A]], i32 4, ptr @__tysan_v1_int_o_0, i32 1)
+; CHECK-INLINE-NEXT:    br label %[[BB19]]
+; CHECK-INLINE:       [[BB19]]:
+; CHECK-INLINE-NEXT:    store ptr @__tysan_v1_int_o_0, ptr [[SHADOW_PTR]], align 8
+; CHECK-INLINE-NEXT:    [[SHADOW_BYTE_1_OFFSET:%.*]] = add i64 [[SHADOW_PTR_INT]], 8
+; CHECK-INLINE-NEXT:    [[SHADOW_BYTE_1_PTR:%.*]] = inttoptr i64 [[SHADOW_BYTE_1_OFFSET]] to ptr
+; CHECK-INLINE-NEXT:    store ptr inttoptr (i64 -1 to ptr), ptr [[SHADOW_BYTE_1_PTR]], align 8
+; CHECK-INLINE-NEXT:    [[SHADOW_BYTE_2_OFFSET:%.*]] = add i64 [[SHADOW_PTR_INT]], 16
+; CHECK-INLINE-NEXT:    [[SHADOW_BYTE_2_PTR:%.*]] = inttoptr i64 [[SHADOW_BYTE_2_OFFSET]] to ptr
+; CHECK-INLINE-NEXT:    store ptr inttoptr (i64 -2 to ptr), ptr [[SHADOW_BYTE_2_PTR]], align 8
+; CHECK-INLINE-NEXT:    [[SHADOW_BYTE_3_OFFSET:%.*]] = add i64 [[SHADOW_PTR_INT]], 24
+; CHECK-INLINE-NEXT:    [[SHADOW_BYTE_3_PTR:%.*]] = inttoptr i64 [[SHADOW_BYTE_3_OFFSET]] to ptr
+; CHECK-INLINE-NEXT:    store ptr inttoptr (i64 -3 to ptr), ptr [[SHADOW_BYTE_3_PTR]], align 8
+; CHECK-INLINE-NEXT:    br label %[[BB21:.*]]
+; CHECK-INLINE:       [[BB20]]:
+; CHECK-INLINE-NEXT:    call void @__tysan_check(ptr [[A]], i32 4, ptr @__tysan_v1_int_o_0, i32 1)
+; CHECK-INLINE-NEXT:    br label %[[BB21]]
+; CHECK-INLINE:       [[BB21]]:
+; CHECK-INLINE-NEXT:    br label %[[BB43:.*]]
+; CHECK-INLINE:       [[BB22]]:
+; CHECK-INLINE-NEXT:    [[TMP23:%.*]] = add i64 [[SHADOW_PTR_INT]], 8
+; CHECK-INLINE-NEXT:    [[TMP24:%.*]] = inttoptr i64 [[TMP23]] to ptr
+; CHECK-INLINE-NEXT:    [[TMP25:%.*]] = load ptr, ptr [[TMP24]], align 8
+; CHECK-INLINE-NEXT:    [[TMP26:%.*]] = ptrtoint ptr [[TMP25]] to i64
+; CHECK-INLINE-NEXT:    [[TMP27:%.*]] = icmp sge i64 [[TMP26]], 0
+; CHECK-INLINE-NEXT:    [[TMP28:%.*]] = or i1 false, [[TMP27]]
+; CHECK-INLINE-NEXT:    [[TMP29:%.*]] = add i64 [[SHADOW_PTR_INT]], 16
+; CHECK-INLINE-NEXT:    [[TMP30:%.*]] = inttoptr i64 [[TMP29]] to ptr
+; CHECK-INLINE-NEXT:    [[TMP31:%.*]] = load ptr, ptr [[TMP30]], align 8
+; CHECK-INLINE-NEXT:    [[TMP32:%.*]] = ptrtoint ptr [[TMP31]] to i64
+; CHECK-INLINE-NEXT:    [[TMP33:%.*]] = icmp sge i64 [[TMP32]], 0
+; CHECK-INLINE-NEXT:    [[TMP34:%.*]] = or i1 [[TMP28]], [[TMP33]]
+; CHECK-INLINE-NEXT:    [[TMP35:%.*]] = add i64 [[SHADOW_PTR_INT]], 24
+; CHECK-INLINE-NEXT:    [[TMP36:%.*]] = inttoptr i64 [[TMP35]] to ptr
+; CHECK-INLINE-NEXT:    [[TMP37:%.*]] = load ptr, ptr [[TMP36]], align 8
+; CHECK-INLINE-NEXT:    [[TMP38:%.*]] = ptrtoint ptr [[TMP37]] to i64
+; CHECK-INLINE-NEXT:    [[TMP39:%.*]] = icmp sge i64 [[TMP38]], 0
+; CHECK-INLINE-NEXT:    [[TMP40:%.*]] = or i1 [[TMP34]], [[TMP39]]
+; CHECK-INLINE-NEXT:    br i1 [[TMP40]], label %[[BB41:.*]], label %[[BB42:.*]], !prof [[PROF0]]
+; CHECK-INLINE:       [[BB41]]:
+; CHECK-INLINE-NEXT:    call void @__tysan_check(ptr [[A]], i32 4, ptr @__tysan_v1_int_o_0, i32 1)
+; CHECK-INLINE-NEXT:    br label %[[BB42]]
+; CHECK-INLINE:       [[BB42]]:
+; CHECK-INLINE-NEXT:    br label %[[BB43]]
+; CHECK-INLINE:       [[BB43]]:
+; CHECK-INLINE-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A]], align 4, !tbaa [[TBAA1:![0-9]+]]
+; CHECK-INLINE-NEXT:    ret i32 [[TMP1]]
+;
+; CHECK-OUTLINE-LABEL: define i32 @test_load(
+; CHECK-OUTLINE-SAME: ptr [[A:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-OUTLINE-NEXT:  [[ENTRY:.*:]]
+; CHECK-OUTLINE-NEXT:    [[APP_MEM_MASK:%.*]] = load i64, ptr @__tysan_app_memory_mask, align 8
+; CHECK-OUTLINE-NEXT:    [[SHADOW_BASE:%.*]] = load i64, ptr @__tysan_shadow_memory_address, align 8
+; CHECK-OUTLINE-NEXT:    call void @__tysan_instrument_with_shadow_update(ptr [[A]], ptr @__tysan_v1_int_o_0, i1 true, i64 4, i32 1)
+; CHECK-OUTLINE-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A]], align 4, !tbaa [[TBAA0:![0-9]+]]
+; CHECK-OUTLINE-NEXT:    ret i32 [[TMP1]]
+;
+entry:
+  %tmp1 = load i32, ptr %a, align 4, !tbaa !3
+  ret i32 %tmp1
+}
+
+define void @test_store(ptr %a) sanitize_type {
+; CHECK-INLINE-LABEL: define void @test_store(
+; CHECK-INLINE-SAME: ptr [[A:%.*]]) #[[ATTR0]] {
+; CHECK-INLINE-NEXT:  [[ENTRY:.*:]]
+; CHECK-INLINE-NEXT:    [[APP_MEM_MASK:%.*]] = load i64, ptr @__tysan_app_memory_mask, align 8
+; CHECK-INLINE-NEXT:    [[SHADOW_BASE:%.*]] = load i64, ptr @__tysan_shadow_memory_address, align 8
+; CHECK-INLINE-NEXT:    [[APP_PTR_INT:%.*]] = ptrtoint ptr [[A]] to i64
+; CHECK-INLINE-NEXT:    [[APP_PTR_MASKED:%.*]] = and i64 [[APP_PTR_INT]], [[APP_MEM_MASK]]
+; CHECK-INLINE-NEXT:    [[APP_PTR_SHIFTED:%.*]] = shl i64 [[APP_PTR_MASKED]], 3
+; CHECK-INLINE-NEXT:    [[SHADOW_PTR_INT:%.*]] = add i64 [[APP_PTR_SHIFTED]], [[SHADOW_BASE]]
+; CHECK-INLINE-NEXT:    [[SHADOW_PTR:%.*]] = inttoptr i64 [[SHADOW_PTR_INT]] to ptr
+; CHECK-INLINE-NEXT:    [[SHADOW_DESC:%.*]] = load ptr, ptr [[SHADOW_PTR]], align 8
+; CHECK-INLINE-NEXT:    [[BAD_DESC:%.*]] = icmp ne ptr [[SHADOW_DESC]], @__tysan_v1___ZTS1v_o_12
+; CHECK-INLINE-NEXT:    br i1 [[BAD_DESC]], label %[[BB0:.*]], label %[[BB22:.*]], !prof [[PROF0]]
+; CHECK-INLINE:       [[BB0]]:
+; CHECK-INLINE-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[SHADOW_DESC]], null
+; CHECK-INLINE-NEXT:    br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB20:.*]]
+; CHECK-INLINE:       [[BB2]]:
+; CHECK-INLINE-NEXT:    [[TMP3:%.*]] = add i64 [[SHADOW_PTR_INT]], 8
+; CHECK-INLINE-NEXT:    [[TMP4:%.*]] = inttoptr i64 [[TMP3]] to ptr
+; CHECK-INLINE-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8
+; CHECK-INLINE-NEXT:    [[TMP6:%.*]] = icmp ne ptr [[TMP5]], null
+; CHECK-INLINE-NEXT:    [[TMP7:%.*]] = or i1 false, [[TMP6]]
+; CHECK-INLINE-NEXT:    [[TMP8:%.*]] = add i64 [[SHADOW_PTR_INT]], 16
+; CHECK-INLINE-NEXT:    [[TMP9:%.*]] = inttoptr i64 [[TMP8]] to ptr
+; CHECK-INLINE-NEXT:    [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8
+; CHECK-INLINE-NEXT:    [[TMP11:%.*]] = icmp ne ptr [[TMP10]], null
+; CHECK-INLINE-NEXT:    [[TMP12:%.*]] = or i1 [[TMP7]], [[TMP11]]
+; CHECK-INLINE-NEXT:    [[TMP13:%.*]] = add i64 [[SHADOW_PTR_INT]], 24
+; CHECK-INLINE-NEXT:    [[TMP14:%.*]] = inttoptr i64 [[TMP13]] to ptr
+; CHECK-INLINE-NEXT:    [[TMP15:%.*]] = load ptr, ptr [[TMP14]], align 8
+; CHECK-INLINE-NEXT:    [[TMP16:%.*]] = icmp ne ptr [[TMP15]], null
+; CHECK-INLINE-NEXT:    [[TMP17:%.*]] = or i1 [[TMP12]], [[TMP16]]
+; CHECK-INLINE-NEXT:    br i1 [[TMP17]], label %[[BB18:.*]], label %[[BB19:.*]], !prof [[PROF0]]
+; CHECK-INLINE:       [[BB18]]:
+; CHECK-INLINE-NEXT:    call void @__tysan_check(ptr [[A]], i32 4, ptr @__tysan_v1___ZTS1v_o_12, i32 2)
+; CHECK-INLINE-NEXT:    br label %[[BB19]]
+; CHECK-INLINE:       [[BB19]]:
+; CHECK-INLINE-NEXT:    store ptr @__tysan_v1___ZTS1v_o_12, ptr [[SHADOW_PTR]], align 8
+; CHECK-INLINE-NEXT:    [[SHADOW_BYTE_1_OFFSET:%.*]] = add i64 [[SHADOW_PTR_INT]], 8
+; CHECK-INLINE-NEXT:    [[SHADOW_BYTE_1_PTR:%.*]] = inttoptr i64 [[SHADOW_BYTE_1_OFFSET]] to ptr
+; CHECK-INLINE-NEXT:    store ptr inttoptr (i64 -1 to ptr), ptr [[SHADOW_BYTE_1_PTR]], align 8
+; CHECK-INLINE-NEXT:    [[SHADOW_BYTE_2_OFFSET:%.*]] = add i64 [[SHADOW_PTR_INT]], 16
+; CHECK-INLINE-NEXT:    [[SHADOW_BYTE_2_PTR:%.*]] = inttoptr i64 [[SHADOW_BYTE_2_OFFSET]] to ptr
+; CHECK-INLINE-NEXT:    store ptr inttoptr (i64 -2 to ptr), ptr [[SHADOW_BYTE_2_PTR]], align 8
+; CHECK-INLINE-NEXT:    [[SHADOW_BYTE_3_OFFSET:%.*]] = add i64 [[SHADOW_PTR_INT]], 24
+; CHECK-INLINE-NEXT:    [[SHADOW_BYTE_3_PTR:%.*]] = inttoptr i64 [[SHADOW_BYTE_3_OFFSET]] to ptr
+; CHECK-INLINE-NEXT:    store ptr inttoptr (i64 -3 to ptr), ptr [[SHADOW_BYTE_3_PTR]], align 8
+; CHECK-INLINE-NEXT:    br label %[[BB21:.*]]
+; CHECK-INLINE:       [[BB20]]:
+; CHECK-INLINE-NEXT:    call void @__tysan_check(ptr [[A]], i32 4, ptr @__tysan_v1___ZTS1v_o_12, i32 2)
+; CHECK-INLINE-NEXT:    br label %[[BB21]]
+; CHECK-INLINE:       [[BB21]]:
+; CHECK-INLINE-NEXT:    br label %[[BB43:.*]]
+; CHECK-INLINE:       [[BB22]]:
+; CHECK-INLINE-NEXT:    [[TMP23:%.*]] = add i64 [[SHADOW_PTR_INT]], 8
+; CHECK-INLINE-NEXT:    [[TMP24:%.*]] = inttoptr i64 [[TMP23]] to ptr
+; CHECK-INLINE-NEXT:    [[TMP25:%.*]] = load ptr, ptr [[TMP24]], align 8
+; CHECK-INLINE-NEXT:    [[TMP26:%.*]] = ptrtoint ptr [[TMP25]] to i64
+; CHECK-INLINE-NEXT:    [[TMP27:%.*]] = icmp sge i64 [[TMP26]], 0
+; CHECK-INLINE-NEXT:    [[TMP28:%.*]] = or i1 false, [[TMP27]]
+; CHECK-INLINE-NEXT:    [[TMP29:%.*]] = add i64 [[SHADOW_PTR_INT]], 16
+; CHECK-INLINE-NEXT:    [[TMP30:%.*]] = inttoptr i64 [[TMP29]] to ptr
+; CHECK-INLINE-NEXT:    [[TMP31:%.*]] = load ptr, ptr [[TMP30]], align 8
+; CHECK-INLINE-NEXT:    [[TMP32:%.*]] = ptrtoint ptr [[TMP31]] to i64
+; CHECK-INLINE-NEXT:    [[TMP33:%.*]] = icmp sge i64 [[TMP32]], 0
+; CHECK-INLINE-NEXT:    [[TMP34:%.*]] = or i1 [[TMP28]], [[TMP33]]
+; CHECK-INLINE-NEXT:    [[TMP35:%.*]] = add i64 [[SHADOW_PTR_INT]], 24
+; CHECK-INLINE-NEXT:    [[TMP36:%.*]] = inttoptr i64 [[TMP35]] to ptr
+; CHECK-INLINE-NEXT:    [[TMP37:%.*]] = load ptr, ptr [[TMP36]], align 8
+; CHECK-INLINE-NEXT:    [[TMP38:%.*]] = ptrtoint ptr [[TMP37]] to i64
+; CHECK-INLINE-NEXT:    [[TMP39:%.*]] = icmp sge i64 [[TMP38]], 0
+; CHECK-INLINE-NEXT:    [[TMP40:%.*]] = or i1 [[TMP34]], [[TMP39]]
+; CHECK-INLINE-NEXT:    br i1 [[TMP40]], label %[[BB41:.*]], label %[[BB42:.*]], !prof [[PROF0]]
+; CHECK-INLINE:       [[BB41]]:
+; CHECK-INLINE-NEXT:    call void @__tysan_check(ptr [[A]], i32 4, ptr @__tysan_v1___ZTS1v_o_12, i32 2)
+; CHECK-INLINE-NEXT:    br label %[[BB42]]
+; CHECK-INLINE:       [[BB42]]:
+; CHECK-INLINE-NEXT:    br label %[[BB43]]
+; CHECK-INLINE:       [[BB43]]:
+; CHECK-INLINE-NEXT:    store i32 42, ptr [[A]], align 4, !tbaa [[TBAA5:![0-9]+]]
+; CHECK-INLINE-NEXT:    ret void
+;
+; CHECK-OUTLINE-LABEL: define void @test_store(
+; CHECK-OUTLINE-SAME: ptr [[A:%.*]]) #[[ATTR0]] {
+; CHECK-OUTLINE-NEXT:  [[ENTRY:.*:]]
+; CHECK-OUTLINE-NEXT:    [[APP_MEM_MASK:%.*]] = load i64, ptr @__tysan_app_memory_mask, align 8
+; CHECK-OUTLINE-NEXT:    [[SHADOW_BASE:%.*]] = load i64, ptr @__tysan_shadow_memory_address, align 8
+; CHECK-OUTLINE-NEXT:    call void @__tysan_instrument_with_shadow_update(ptr [[A]], ptr @__tysan_v1___ZTS1v_o_12, i1 true, i64 4, i32 2)
+; CHECK-OUTLINE-NEXT:    store i32 42, ptr [[A]], align 4, !tbaa [[TBAA4:![0-9]+]]
+; CHECK-OUTLINE-NEXT:    ret void
+;
+entry:
+  store i32 42, ptr %a, align 4, !tbaa !6
+  ret void
+}
+
+!0 = !{!"Simple C++ TBAA"}
+!1 = !{!0, i64 1, !"omnipotent char"}
+!2 = !{!1, i64 4, !"int"}
+!3 = !{!2, !2, i64 0, i64 4}
+!4 = !{!1, i64 8, !"_ZTS1x", !2, i64 0, i64 4, !2, i64 4, i64 4}
+!5 = !{!1, i64 24, !"_ZTS1v", !2, i64 8, i64 4, !2, i64 12, i64 4, !4, i64 16, i64 8}
+!6 = !{!5, !2, i64 12, i64 4}
+;.
+; CHECK-INLINE: attributes #[[ATTR0]] = { sanitize_type }
+; CHECK-INLINE: attributes #[[ATTR1:[0-9]+]] = { nounwind }
+;.
+; CHECK-OUTLINE: attributes #[[ATTR0]] = { sanitize_type }
+; CHECK-OUTLINE: attributes #[[ATTR1:[0-9]+]] = { nounwind }
+;.
+; CHECK-INLINE: [[PROF0]] = !{!"branch_weights", i32 1, i32 100000}
+; CHECK-INLINE: [[TBAA1]] = !{[[META2:![0-9]+]], [[META2]], i64 0, i64 4}
+; CHECK-INLINE: [[META2]] = !{[[META3:![0-9]+]], i64 4, !"int"}
+; CHECK-INLINE: [[META3]] = !{[[META4:![0-9]+]], i64 1, !"omnipotent char"}
+; CHECK-INLINE: [[META4]] = !{!"Simple C++ TBAA"}
+; CHECK-INLINE: [[TBAA5]] = !{[[META6:![0-9]+]], [[META2]], i64 12, i64 4}
+; CHECK-INLINE: [[META6]] = !{[[META3]], i64 24, !"_ZTS1v", [[META2]], i64 8, i64 4, [[META2]], i64 12, i64 4, [[META7:![0-9]+]], i64 16, i64 8}
+; CHECK-INLINE: [[META7]] = !{[[META3]], i64 8, !"_ZTS1x", [[META2]], i64 0, i64 4, [[META2]], i64 4, i64 4}
+;.
+; CHECK-OUTLINE: [[TBAA0]] = !{[[META1:![0-9]+]], [[META1]], i64 0, i64 4}
+; CHECK-OUTLINE: [[META1]] = !{[[META2:![0-9]+]], i64 4, !"int"}
+; CHECK-OUTLINE: [[META2]] = !{[[META3:![0-9]+]], i64 1, !"omnipotent char"}
+; CHECK-OUTLINE: [[META3]] = !{!"Simple C++ TBAA"}
+; CHECK-OUTLINE: [[TBAA4]] = !{[[META5:![0-9]+]], [[META1]], i64 12, i64 4}
+; CHECK-OUTLINE: [[META5]] = !{[[META2]], i64 24, !"_ZTS1v", [[META1]], i64 8, i64 4, [[META1]], i64 12, i64 4, [[META6:![0-9]+]], i64 16, i64 8}
+; CHECK-OUTLINE: [[META6]] = !{[[META2]], i64 8, !"_ZTS1x", [[META1]], i64 0, i64 4, [[META1]], i64 4, i64 4}
+;.



More information about the llvm-commits mailing list