[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:36:40 PDT 2026
https://github.com/OfekShilon updated https://github.com/llvm/llvm-project/pull/226568
>From f64db6f3f290c8858b492b6ce02bb366b03a5c58 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 a0e8ab43b2df2b..6995719a64fbc6 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 2de006bfc032d9..76bffebb5a335f 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"
@@ -8273,15 +8274,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);
@@ -8302,7 +8294,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 82edbe120bac6af25aec1e72f6d2279cd592a8ca 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 c6436617d69e6f..ed07f169c991d0 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"
@@ -273,24 +274,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();
@@ -304,8 +342,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");
}
@@ -318,10 +354,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;
@@ -340,12 +373,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()) {
@@ -358,9 +391,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