diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index fd2e3aacd0169..b0d11d0450eba 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -6434,9 +6434,10 @@ tuples this way: undefined if ``Offset`` is non-zero. * If ``BaseTy`` is a struct type then ``ImmediateParent(BaseTy, Offset)`` - is ``(NewTy, NewOffset)`` where ``NewTy`` is the type contained in - ``BaseTy`` at offset ``Offset`` and ``NewOffset`` is ``Offset`` adjusted - to be relative within that inner type. + is array of ``(NewTy[N], NewOffset)`` where ``NewTy[N]`` is the Nth type + contained in ``BaseTy`` at offset ``Offset`` and ``NewOffset`` is + ``Offset`` adjusted to be relative within that inner type. Multiple types + occupying same offset allow to describe union-like structures. A memory access with an access tag ``(BaseTy1, AccessTy1, Offset1)`` aliases a memory access with an access tag ``(BaseTy2, AccessTy2, @@ -6447,9 +6448,9 @@ As a concrete example, the type descriptor graph for the following program .. code-block:: c - struct Inner { + union Inner { int i; // offset 0 - float f; // offset 4 + float f; // offset 0 }; struct Outer { @@ -6461,7 +6462,7 @@ As a concrete example, the type descriptor graph for the following program void f(struct Outer* outer, struct Inner* inner, float* f, int* i, char* c) { outer->f = 0; // tag0: (OuterStructTy, FloatScalarTy, 0) outer->inner_a.i = 0; // tag1: (OuterStructTy, IntScalarTy, 12) - outer->inner_a.f = 0.0; // tag2: (OuterStructTy, FloatScalarTy, 16) + outer->inner_a.f = 0.0; // tag2: (OuterStructTy, FloatScalarTy, 12) *f = 0.0; // tag3: (FloatScalarTy, FloatScalarTy, 0) } @@ -6475,13 +6476,13 @@ type): FloatScalarTy = ("float", CharScalarTy, 0) DoubleScalarTy = ("double", CharScalarTy, 0) IntScalarTy = ("int", CharScalarTy, 0) - InnerStructTy = {"Inner" (IntScalarTy, 0), (FloatScalarTy, 4)} + InnerStructTy = {"Inner" (IntScalarTy, 0), (FloatScalarTy, 0)} OuterStructTy = {"Outer", (FloatScalarTy, 0), (DoubleScalarTy, 4), (InnerStructTy, 12)} with (e.g.) ``ImmediateParent(OuterStructTy, 12)`` = ``(InnerStructTy, -0)``, ``ImmediateParent(InnerStructTy, 0)`` = ``(IntScalarTy, 0)``, and +0)``, ``ImmediateParent(InnerStructTy, 0)`` = ``(IntScalarTy, 0), (FloatScalarTy, 0)``, and ``ImmediateParent(IntScalarTy, 0)`` = ``(CharScalarTy, 0)``. .. _tbaa_node_representation: diff --git a/llvm/include/llvm/IR/Verifier.h b/llvm/include/llvm/IR/Verifier.h index b25f8eb77ee38..95db2c4b16eca 100644 --- a/llvm/include/llvm/IR/Verifier.h +++ b/llvm/include/llvm/IR/Verifier.h @@ -59,8 +59,15 @@ class TBAAVerifier { /// \name Helper functions used by \c visitTBAAMetadata. /// @{ - MDNode *getFieldNodeFromTBAABaseNode(Instruction &I, const MDNode *BaseNode, - APInt &Offset, bool IsNewFormat); + std::vector getFieldNodeFromTBAABaseNode(Instruction &I, + const MDNode *BaseNode, + APInt &Offset, + bool IsNewFormat); + bool findAccessTypeNode(Instruction &I, + SmallPtrSetImpl &StructPath, + APInt Offset, bool IsNewFormat, + const MDNode *AccessType, const MDNode *BaseNode, + const MDNode *MD); TBAAVerifier::TBAABaseNodeSummary verifyTBAABaseNode(Instruction &I, const MDNode *BaseNode, bool IsNewFormat); diff --git a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp index d05f42552e81d..12a529cd433a5 100644 --- a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp +++ b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp @@ -121,6 +121,7 @@ #include "llvm/Support/ErrorHandling.h" #include #include +#include using namespace llvm; @@ -299,9 +300,10 @@ class TBAAStructTypeNode { return TBAAStructTypeNode(TypeNode); } - /// Get this TBAAStructTypeNode's field in the type DAG with + /// Get this TBAAStructTypeNode's fields in the type DAG with /// given offset. Update the offset to be relative to the field type. - TBAAStructTypeNode getField(uint64_t &Offset) const { + /// There could be multiple fields with same offset. + std::vector getField(uint64_t &Offset) const { bool NewFormat = isNewFormat(); const ArrayRef Operands = Node->operands(); const unsigned NumOperands = Operands.size(); @@ -309,11 +311,11 @@ class TBAAStructTypeNode { if (NewFormat) { // New-format root and scalar type nodes have no fields. if (NumOperands < 6) - return TBAAStructTypeNode(); + return {TBAAStructTypeNode()}; } else { // Parent can be omitted for the root node. if (NumOperands < 2) - return TBAAStructTypeNode(); + return {TBAAStructTypeNode()}; // Fast path for a scalar type node and a struct type node with a single // field. @@ -325,8 +327,8 @@ class TBAAStructTypeNode { Offset -= Cur; MDNode *P = dyn_cast_or_null(Operands[1]); if (!P) - return TBAAStructTypeNode(); - return TBAAStructTypeNode(P); + return {TBAAStructTypeNode()}; + return {TBAAStructTypeNode(P)}; } } @@ -336,6 +338,8 @@ class TBAAStructTypeNode { unsigned NumOpsPerField = NewFormat ? 3 : 2; unsigned TheIdx = 0; + std::vector Ret; + for (unsigned Idx = FirstFieldOpNo; Idx < NumOperands; Idx += NumOpsPerField) { uint64_t Cur = @@ -353,10 +357,20 @@ class TBAAStructTypeNode { uint64_t Cur = mdconst::extract(Operands[TheIdx + 1])->getZExtValue(); Offset -= Cur; + + // Collect all fields that have right offset. MDNode *P = dyn_cast_or_null(Operands[TheIdx]); - if (!P) - return TBAAStructTypeNode(); - return TBAAStructTypeNode(P); + Ret.emplace_back(P ? TBAAStructTypeNode(P) : TBAAStructTypeNode()); + + while (TheIdx > FirstFieldOpNo) { + TheIdx -= NumOpsPerField; + auto Val = mdconst::extract(Operands[TheIdx + 1]); + if (Cur != Val->getZExtValue()) + break; + MDNode *P = dyn_cast_or_null(Operands[TheIdx]); + P ? Ret.emplace_back(P) : Ret.emplace_back(); + } + return Ret; } }; @@ -572,6 +586,39 @@ static bool hasField(TBAAStructTypeNode BaseType, return false; } +static bool rangeOverlap(std::pair Range1, + std::pair Range2) { + return Range1.first < Range2.first + Range2.second && + Range1.first + Range1.second > Range2.first; +} + +/// Return true if two accessess to given \p BaseType at \p Offset1 and +/// at \p Offset2 may alias. This check does not account for NewStructType +/// parameters such as size and may be more conservative. +static bool mayFieldAccessesAlias(TBAAStructTypeNode BaseType, uint64_t Offset1, + uint64_t Offset2) { + if (!BaseType.getNode()) + return true; + + auto PrevDiff = (long long)(Offset1) - (long long)(Offset2); + auto Fields1 = BaseType.getField(Offset1); + auto Fields2 = BaseType.getField(Offset2); + auto CurrentDiff = (long long)(Offset1) - (long long)(Offset2); + + // If distance between offsets is not same that mean accesses are + // to different fields. + if (PrevDiff != CurrentDiff) + return false; + + // Fields that share same offset may have various internal structure. For + // some of them - same field may be accessed while for others - different + // ones. To be conservative we report MayAlias if any of fields report + // MayAlias. + return llvm::any_of(Fields1, [&](auto &FieldType) { + return mayFieldAccessesAlias(FieldType, Offset1, Offset2); + }); +} + /// Return true if for two given accesses, one of the accessed objects may be a /// subobject of the other. The \p BaseTag and \p SubobjectTag parameters /// describe the accesses to the base object and the subobject respectively. @@ -599,20 +646,38 @@ static bool mayBeAccessToSubobjectOf(TBAAStructTagNode BaseTag, // from the base type, follow the edge with the correct offset in the type DAG // and adjust the offset until we reach the field type or until we reach the // access type. + // If multiple fields have same offset in some base type, then scan each such + // field. bool NewFormat = BaseTag.isNewFormat(); TBAAStructTypeNode BaseType(BaseTag.getBaseType()); uint64_t OffsetInBase = BaseTag.getOffset(); - for (;;) { - // In the old format there is no distinction between fields and parent - // types, so in this case we consider all nodes up to the root. - if (!BaseType.getNode()) { - assert(!NewFormat && "Did not see access type in access path!"); - break; - } + SmallVector, 4> ToCheck; + ToCheck.emplace_back(BaseType, OffsetInBase); + while (!ToCheck.empty()) { + std::tie(BaseType, OffsetInBase) = ToCheck.back(); + ToCheck.pop_back(); + + // In case if root is reached, still check the remaining candidates. + // For new format it is always expected for access type to be found. + // For old format all nodes up to the root are considered from all + // candidates. + if (!BaseType.getNode()) + continue; if (BaseType.getNode() == SubobjectTag.getBaseType()) { - bool SameMemberAccess = OffsetInBase == SubobjectTag.getOffset(); + bool SameMemberAccess; + uint64_t SubobjectOffset = SubobjectTag.getOffset(); + if (NewFormat) + // If size information is available, check if their access locations + // overlap. + SameMemberAccess = rangeOverlap( + std::make_pair(OffsetInBase, BaseTag.getSize()), + std::make_pair(SubobjectOffset, SubobjectTag.getSize())); + else + // Else do a more conservative check. + SameMemberAccess = + mayFieldAccessesAlias(BaseType, OffsetInBase, SubobjectOffset); if (GenericTag) { *GenericTag = SameMemberAccess ? SubobjectTag.getNode() : createAccessTag(CommonType); @@ -627,13 +692,15 @@ static bool mayBeAccessToSubobjectOf(TBAAStructTagNode BaseTag, // Follow the edge with the correct offset. Offset will be adjusted to // be relative to the field type. - BaseType = BaseType.getField(OffsetInBase); + for (auto &&F : BaseType.getField(OffsetInBase)) + ToCheck.emplace_back(F, OffsetInBase); } // If the base object has a direct or indirect field of the subobject's type, // then this may be an access to that field. We need this to check now that // we support aggregates as access types. if (NewFormat) { + assert(BaseType.getNode() && "Did not see access type in access path!"); // TBAAStructTypeNode BaseAccessType(BaseTag.getAccessType()); TBAAStructTypeNode FieldType(SubobjectTag.getBaseType()); if (hasField(BaseType, FieldType)) { diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index b04d39c700a8f..e0d543320059b 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -6969,48 +6969,56 @@ bool TBAAVerifier::isValidScalarTBAANode(const MDNode *MD) { return Result; } -/// Returns the field node at the offset \p Offset in \p BaseNode. Update \p -/// Offset in place to be the offset within the field node returned. +/// Returns one or several field nodes at the offset \p Offset in \p BaseNode. +/// Returns empty vector if \p BaseNode has no fields with specified offset. +/// Update \p Offset in place to be the offset within the field node returned. /// /// We assume we've okayed \p BaseNode via \c verifyTBAABaseNode. -MDNode *TBAAVerifier::getFieldNodeFromTBAABaseNode(Instruction &I, - const MDNode *BaseNode, - APInt &Offset, - bool IsNewFormat) { +std::vector TBAAVerifier::getFieldNodeFromTBAABaseNode( + Instruction &I, const MDNode *BaseNode, APInt &Offset, bool IsNewFormat) { assert(BaseNode->getNumOperands() >= 2 && "Invalid base node!"); // Scalar nodes have only one possible "field" -- their parent in the access // hierarchy. Offset must be zero at this point, but our caller is supposed // to check that. if (BaseNode->getNumOperands() == 2) - return cast(BaseNode->getOperand(1)); + return {cast(BaseNode->getOperand(1))}; unsigned FirstFieldOpNo = IsNewFormat ? 3 : 1; unsigned NumOpsPerField = IsNewFormat ? 3 : 2; + + unsigned LastIdx = BaseNode->getNumOperands() - NumOpsPerField; for (unsigned Idx = FirstFieldOpNo; Idx < BaseNode->getNumOperands(); Idx += NumOpsPerField) { auto *OffsetEntryCI = mdconst::extract(BaseNode->getOperand(Idx + 1)); if (OffsetEntryCI->getValue().ugt(Offset)) { if (Idx == FirstFieldOpNo) { - CheckFailed("Could not find TBAA parent in struct type node", &I, - BaseNode, &Offset); - return nullptr; + return {}; } - unsigned PrevIdx = Idx - NumOpsPerField; - auto *PrevOffsetEntryCI = - mdconst::extract(BaseNode->getOperand(PrevIdx + 1)); - Offset -= PrevOffsetEntryCI->getValue(); - return cast(BaseNode->getOperand(PrevIdx)); + LastIdx = Idx - NumOpsPerField; + break; } } - unsigned LastIdx = BaseNode->getNumOperands() - NumOpsPerField; auto *LastOffsetEntryCI = mdconst::extract( BaseNode->getOperand(LastIdx + 1)); - Offset -= LastOffsetEntryCI->getValue(); - return cast(BaseNode->getOperand(LastIdx)); + auto LastOffsetVal = LastOffsetEntryCI->getValue(); + Offset -= LastOffsetVal; + + std::vector Ret; + Ret.emplace_back(cast(BaseNode->getOperand(LastIdx))); + while (LastIdx > FirstFieldOpNo) { + LastIdx -= NumOpsPerField; + LastOffsetEntryCI = + mdconst::extract(BaseNode->getOperand(LastIdx + 1)); + if (LastOffsetEntryCI->getValue() != LastOffsetVal) + break; + Ret.emplace_back(cast(BaseNode->getOperand(LastIdx))); + } + + return Ret; } static bool isNewFormatTBAATypeNode(llvm::MDNode *Type) { @@ -7087,47 +7095,84 @@ bool TBAAVerifier::visitTBAAMetadata(Instruction &I, const MDNode *MD) { CheckTBAA(OffsetCI, "Offset must be constant integer", &I, MD); APInt Offset = OffsetCI->getValue(); - bool SeenAccessTypeInPath = false; - SmallPtrSet StructPath; + SmallPtrSet StructPath; - for (/* empty */; BaseNode && !IsRootTBAANode(BaseNode); - BaseNode = getFieldNodeFromTBAABaseNode(I, BaseNode, Offset, - IsNewFormat)) { - if (!StructPath.insert(BaseNode).second) { - CheckFailed("Cycle detected in struct path", &I, MD); - return false; - } + auto &&[Invalid, BaseNodeBitWidth] = + verifyTBAABaseNode(I, BaseNode, IsNewFormat); - bool Invalid; - unsigned BaseNodeBitWidth; - std::tie(Invalid, BaseNodeBitWidth) = verifyTBAABaseNode(I, BaseNode, - IsNewFormat); + // If the base node is invalid in itself, then we've already printed all the + // errors we wanted to print. + if (Invalid) + return false; - // If the base node is invalid in itself, then we've already printed all the - // errors we wanted to print. - if (Invalid) - return false; + bool SeenAccessTypeInPath = BaseNode == AccessType; + if (SeenAccessTypeInPath) { + CheckTBAA(Offset == 0, "Offset not zero at the point of scalar access", &I, + MD, &Offset); + if (IsNewFormat) + return true; + } - SeenAccessTypeInPath |= BaseNode == AccessType; + CheckTBAA(findAccessTypeNode(I, StructPath, Offset, IsNewFormat, AccessType, + BaseNode, MD) || + SeenAccessTypeInPath, + "Did not see access type in access path!", &I, MD); + return true; +} - if (isValidScalarTBAANode(BaseNode) || BaseNode == AccessType) - CheckTBAA(Offset == 0, "Offset not zero at the point of scalar access", - &I, MD, &Offset); +bool TBAAVerifier::findAccessTypeNode( + Instruction &I, SmallPtrSetImpl &StructPath, APInt Offset, + bool IsNewFormat, const MDNode *AccessType, const MDNode *BaseNode, + const MDNode *MD) { + if (!BaseNode || IsRootTBAANode(BaseNode)) + return false; - CheckTBAA(BaseNodeBitWidth == Offset.getBitWidth() || - (BaseNodeBitWidth == 0 && Offset == 0) || - (IsNewFormat && BaseNodeBitWidth == ~0u), - "Access bit-width not the same as description bit-width", &I, MD, - BaseNodeBitWidth, Offset.getBitWidth()); + auto &&[Invalid, BaseNodeBitWidth] = + verifyTBAABaseNode(I, BaseNode, IsNewFormat); - if (IsNewFormat && SeenAccessTypeInPath) - break; + // If the base node is invalid in itself, then we've already printed all the + // errors we wanted to print. + if (Invalid) + return false; + + // Offset at point of scalar access must be zero. Skip mismatched nodes. + if ((isValidScalarTBAANode(BaseNode) || BaseNode == AccessType) && + Offset != 0) + return false; + + CheckTBAA(BaseNodeBitWidth == Offset.getBitWidth() || + (BaseNodeBitWidth == 0 && Offset == 0) || + (IsNewFormat && BaseNodeBitWidth == ~0u), + "Access bit-width not the same as description bit-width", &I, MD, + BaseNodeBitWidth, Offset.getBitWidth()); + + bool SeenAccessTypeInPath = (BaseNode == AccessType && Offset == 0); + + if (IsNewFormat && SeenAccessTypeInPath) + return true; + + auto ProbableNodes = + getFieldNodeFromTBAABaseNode(I, BaseNode, Offset, IsNewFormat); + + if (!StructPath.insert(BaseNode).second) { + CheckFailed("Cycle detected in struct path", &I, MD); + return false; } - CheckTBAA(SeenAccessTypeInPath, "Did not see access type in access path!", &I, - MD); - return true; + for (auto *PN : ProbableNodes) { + if (!PN || IsRootTBAANode(PN)) + continue; + + SmallPtrSet StructPathCopy; + StructPathCopy.insert(StructPath.begin(), StructPath.end()); + + if (findAccessTypeNode(I, StructPathCopy, Offset, IsNewFormat, AccessType, + PN, MD)) + return true; + } + + return SeenAccessTypeInPath; } char VerifierLegacyPass::ID = 0; diff --git a/llvm/test/Analysis/TypeBasedAliasAnalysis/aggregates.ll b/llvm/test/Analysis/TypeBasedAliasAnalysis/aggregates.ll index 4049c78049e03..422f8d8040468 100644 --- a/llvm/test/Analysis/TypeBasedAliasAnalysis/aggregates.ll +++ b/llvm/test/Analysis/TypeBasedAliasAnalysis/aggregates.ll @@ -105,6 +105,22 @@ entry: ret i32 %0 } +; C vs. D => MayAlias. +define i32 @f7(ptr %c, ptr %d) { +entry: +; CHECK-LABEL: f7 +; CHECK: MayAlias: store i16 7, {{.*}} <-> store i32 5, +; OPT-LABEL: f7 +; OPT: store i32 5, +; OPT: store i16 7, +; OPT: load i32 +; OPT: ret i32 + store i32 5, ptr %c, align 4, !tbaa !18 ; TAG_Union_int + store i16 7, ptr %d, align 4, !tbaa !17 ; TAG_Union_short + %0 = load i32, ptr %c, align 4, !tbaa !18 ; TAG_Union_int + ret i32 %0 +} + !0 = !{!"root"} !1 = !{!0, i64 1, !"char"} !2 = !{!1, i64 4, !"int"} @@ -128,3 +144,7 @@ entry: !14 = !{!4, i64 2, !"D", !11, i64 0, i64 2} !15 = !{!14, !14, i64 0, i64 2} ; TAG_D + +!16 = !{!1, i64 2, !"Union", !11, i64 0, i64 2, !2, i64 0, i64 4} +!17 = !{!16, !11, i64 0, i64 2} ; TAG_Union_short +!18 = !{!16, !2, i64 0, i64 4} ; TAG_Union_int diff --git a/llvm/test/Analysis/TypeBasedAliasAnalysis/union-path-new.ll b/llvm/test/Analysis/TypeBasedAliasAnalysis/union-path-new.ll new file mode 100644 index 0000000000000..76ee80b259b95 --- /dev/null +++ b/llvm/test/Analysis/TypeBasedAliasAnalysis/union-path-new.ll @@ -0,0 +1,190 @@ +; RUN: opt < %s -aa-pipeline=tbaa -passes=aa-eval -evaluate-aa-metadata \ +; RUN: -print-no-aliases -print-may-aliases -disable-output 2>&1 | \ +; RUN: FileCheck %s +; RUN: opt < %s -aa-pipeline=tbaa -passes=gvn -S | FileCheck %s --check-prefix=OPT +; +; Check various union use cases with old struct path TBAA. + +; IR generated from following C code: +; +; // Case 1: Union with array field. +; +; union A{ +; int a[5]; +; double g; +; }; +; +; // MayAlias. +; double f1(union A* a) { +; a->g = 2.0; +; a->a[1] = 5; +; return a->g; +; } +; +; // Case 2: Union with struct and primitive type. +; +; struct S1{ +; int a; +; float b; +; int c; +; }; +; +; union S2{ +; struct S1 c; +; double d; +; }; +; +; // MayAlias. +; double f2(union S2* u) { +; u->d = 2.0; +; u->c.b = 3.0; +; return u->d; +; } +; // NoAlias. +; // Contrary to old struct path, here tbaa +; // no alias. (see union-path-old.ll) +; double f3(union S2* u) { +; u->d = 2.0; +; u->c.c = 3; +; return u->d; +; } +; +; // Case 3: Union of two structs. +; +; struct FloatS{ +; float a; +; float b; +; }; +; +; struct IntS{ +; short a; +; short b; +; char c; +; }; +; +; union SU { +; struct FloatS a; +; struct IntS b; +; }; +; +; // NoAlias. +; float f4(union SU* u) { +; u->a.a = 3.0; +; u->b.c = 5; +; return u->a.a; +; } +; +; // MayAlias. +; float f5(union SU* u) { +; u->a.a = 3.0; +; u->b.b = 5; +; return u->a.a; +; } + + + +define double @f1(ptr %0) { +entry: +; CHECK-LABEL: f1 +; CHECK: MayAlias: store i32 5, {{.*}} <-> store double 2.0 +; OPT-LABEL: f1 +; OPT: store double 2.0 +; OPT: store i32 5, +; OPT: load double +; OPT: ret double + store double 2.000000e+00, ptr %0, align 8, !tbaa !5 + %2 = getelementptr inbounds i8, ptr %0, i64 4 + store i32 5, ptr %2, align 4, !tbaa !11 + %3 = load double, ptr %0, align 8, !tbaa !5 + ret double %3 +} + +define double @f2(ptr %0) { +entry: +; CHECK-LABEL: f2 +; CHECK: MayAlias: store float 3.0{{.*}} <-> store double 2.0 +; OPT-LABEL: f2 +; OPT: store double 2.0 +; OPT: store float 3.0 +; OPT: load double +; OPT: ret double + store double 2.000000e+00, ptr %0, align 8, !tbaa !12 + %2 = getelementptr inbounds i8, ptr %0, i64 4 + store float 3.000000e+00, ptr %2, align 4, !tbaa !16 + %3 = load double, ptr %0, align 8, !tbaa !12 + ret double %3 +} + +define double @f3(ptr %0) { +entry: +; CHECK-LABEL: f3 +; CHECK: NoAlias: store i32 3,{{.*}} <-> store double 2.0 +; OPT-LABEL: f3 +; OPT: store double 2.0 +; OPT: store i32 3 +; OPT-NOT: load double +; OPT: ret double 2.0 + store double 2.000000e+00, ptr %0, align 8, !tbaa !12 + %2 = getelementptr inbounds i8, ptr %0, i64 8 + store i32 3, ptr %2, align 8, !tbaa !17 + %3 = load double, ptr %0, align 8, !tbaa !12 + ret double %3 +} + +define float @f4(ptr %0) { +; CHECK-LABEL: f4 +; CHECK: NoAlias: store i8 5{{.*}} <-> store float 3.0 +; OPT-LABEL: f4 +; OPT: store float 3.0 +; OPT: store i8 5 +; OPT-NOT: load +; OPT: ret float 3.0 + store float 3.000000e+00, ptr %0, align 4, !tbaa !18 + %2 = getelementptr inbounds i8, ptr %0, i64 4 + store i8 5, ptr %2, align 4, !tbaa !23 + %3 = load float, ptr %0, align 4, !tbaa !18 + ret float %3 +} + +define float @f5(ptr %0) { +entry: +; CHECK-LABEL: f5 +; CHECK: MayAlias: store i16 5, {{.*}} <-> store float 3.0 +; OPT-LABEL: f5 +; OPT: store float 3.0 +; OPT: store i16 5 +; OPT: load float +; OPT: ret float + store float 3.000000e+00, ptr %0, align 4, !tbaa !18 + %2 = getelementptr inbounds i8, ptr %0, i64 2 + store i16 5, ptr %2, align 2, !tbaa !24 + %3 = load float, ptr %0, align 4, !tbaa !18 + ret float %3 +} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 8, !"PIC Level", i32 2} +!2 = !{i32 7, !"PIE Level", i32 2} +!3 = !{i32 7, !"uwtable", i32 2} +!4 = !{!""} +!5 = !{!6, !10, i64 0, i64 8} +!6 = !{!8, i64 24, !"A", !7, i64 0, i64 20, !10, i64 0, i64 8} +!7 = !{!8, i64 4, !"int"} +!8 = !{!9, i64 1, !"omnipotent char"} +!9 = !{!"Simple C/C++ TBAA"} +!10 = !{!8, i64 8, !"double"} +!11 = !{!6, !7, i64 0, i64 4} +!12 = !{!13, !10, i64 0, i64 8} +!13 = !{!8, i64 8, !"S2", !14, i64 0, i64 8, !10, i64 0, i64 8} +!14 = !{!8, i64 8, !"S1", !7, i64 0, i64 4, !15, i64 4, i64 4, !7, i64 8, i64 4} +!15 = !{!8, i64 4, !"float"} +!16 = !{!13, !15, i64 4, i64 4} +!17 = !{!13, !7, i64 8, i64 4} +!18 = !{!19, !15, i64 0, i64 4} +!19 = !{!8, i64 8, !"SU", !20, i64 0, i64 8, !21, i64 0, i64 6} +!20 = !{!8, i64 8, !"FloatS", !15, i64 0, i64 4, !15, i64 4, i64 4} +!21 = !{!8, i64 6, !"IntS", !22, i64 0, i64 2, !22, i64 2, i64 2, !8, i64 4, i64 1} +!22 = !{!8, i64 2, !"short"} +!23 = !{!19, !8, i64 4, i64 1} +!24 = !{!19, !22, i64 2, i64 2} + diff --git a/llvm/test/Analysis/TypeBasedAliasAnalysis/union-path-old.ll b/llvm/test/Analysis/TypeBasedAliasAnalysis/union-path-old.ll new file mode 100644 index 0000000000000..b62e6e2243f7b --- /dev/null +++ b/llvm/test/Analysis/TypeBasedAliasAnalysis/union-path-old.ll @@ -0,0 +1,190 @@ +; RUN: opt < %s -aa-pipeline=tbaa -passes=aa-eval -evaluate-aa-metadata \ +; RUN: -print-no-aliases -print-may-aliases -disable-output 2>&1 | \ +; RUN: FileCheck %s +; RUN: opt < %s -aa-pipeline=tbaa -passes=gvn -S | FileCheck %s --check-prefix=OPT +; +; Check various union use cases with old struct path TBAA. + +; IR generated from following C code: +; +; // Case 1: Union with array field. +; +; union A{ +; int a[5]; +; double g; +; }; +; +; // MayAlias. +; double f1(union A* a) { +; a->g = 2.0; +; a->a[1] = 5; +; return a->g; +; } +; +; // Case 2: Union with struct and primitive type. +; +; struct S1{ +; int a; +; float b; +; int c; +; }; +; +; union S2{ +; struct S1 c; +; double d; +; }; +; +; // MayAlias. +; double f2(union S2* u) { +; u->d = 2.0; +; u->c.b = 3.0; +; return u->d; +; } +; // MayAlias. +; // Old struct path is conservative here. +; // (For reference see union-path-new.ll) +; double f3(union S2* u) { +; u->d = 2.0; +; u->c.c = 3; +; return u->d; +; } +; +; // Case 3: Union of two structs. +; +; struct FloatS{ +; float a; +; float b; +; }; +; +; struct IntS{ +; short a; +; short b; +; char c; +; }; +; +; union SU { +; struct FloatS a; +; struct IntS b; +; }; +; +; // NoAlias. +; float f4(union SU* u) { +; u->a.a = 3.0; +; u->b.c = 5; +; return u->a.a; +; } +; +; // MayAlias. +; float f5(union SU* u) { +; u->a.a = 3.0; +; u->b.b = 5; +; return u->a.a; +; } + + + +define double @f1(ptr %0) { +entry: +; CHECK-LABEL: f1 +; CHECK: MayAlias: store i32 5, {{.*}} <-> store double 2.0 +; OPT-LABEL: f1 +; OPT: store double 2.0 +; OPT: store i32 5, +; OPT: load double +; OPT: ret double + store double 2.000000e+00, ptr %0, align 8, !tbaa !5 + %2 = getelementptr inbounds i8, ptr %0, i64 4 + store i32 5, ptr %2, align 4, !tbaa !11 + %3 = load double, ptr %0, align 8, !tbaa !5 + ret double %3 +} + +define double @f2(ptr %0) { +entry: +; CHECK-LABEL: f2 +; CHECK: MayAlias: store float 3.0{{.*}} <-> store double 2.0 +; OPT-LABEL: f2 +; OPT: store double 2.0 +; OPT: store float 3.0 +; OPT: load double +; OPT: ret double + store double 2.000000e+00, ptr %0, align 8, !tbaa !12 + %2 = getelementptr inbounds i8, ptr %0, i64 4 + store float 3.000000e+00, ptr %2, align 4, !tbaa !16 + %3 = load double, ptr %0, align 8, !tbaa !12 + ret double %3 +} + +define double @f3(ptr %0) { +entry: +; CHECK-LABEL: f3 +; CHECK: MayAlias: store i32 3,{{.*}} <-> store double 2.0 +; OPT-LABEL: f3 +; OPT: store double 2.0 +; OPT: store i32 3 +; OPT: load double +; OPT: ret double + store double 2.000000e+00, ptr %0, align 8, !tbaa !12 + %2 = getelementptr inbounds i8, ptr %0, i64 8 + store i32 3, ptr %2, align 8, !tbaa !17 + %3 = load double, ptr %0, align 8, !tbaa !12 + ret double %3 +} + +define float @f4(ptr %0) { +; CHECK-LABEL: f4 +; CHECK: NoAlias: store i8 5{{.*}} <-> store float 3.0 +; OPT-LABEL: f4 +; OPT: store float 3.0 +; OPT: store i8 5 +; OPT-NOT: load +; OPT: ret float 3.0 + store float 3.000000e+00, ptr %0, align 4, !tbaa !18 + %2 = getelementptr inbounds i8, ptr %0, i64 4 + store i8 5, ptr %2, align 4, !tbaa !23 + %3 = load float, ptr %0, align 4, !tbaa !18 + ret float %3 +} + +define float @f5(ptr %0) { +entry: +; CHECK-LABEL: f5 +; CHECK: MayAlias: store i16 5, {{.*}} <-> store float 3.0 +; OPT-LABEL: f5 +; OPT: store float 3.0 +; OPT: store i16 5 +; OPT: load float +; OPT: ret float + store float 3.000000e+00, ptr %0, align 4, !tbaa !18 + %2 = getelementptr inbounds i8, ptr %0, i64 2 + store i16 5, ptr %2, align 2, !tbaa !24 + %3 = load float, ptr %0, align 4, !tbaa !18 + ret float %3 +} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 8, !"PIC Level", i32 2} +!2 = !{i32 7, !"PIE Level", i32 2} +!3 = !{i32 7, !"uwtable", i32 2} +!4 = !{!""} +!5 = !{!6, !10, i64 0} +!6 = !{!"A", !7, i64 0, !10, i64 0} +!7 = !{!"int", !8, i64 0} +!8 = !{!"omnipotent char", !9, i64 0} +!9 = !{!"Simple C/C++ TBAA"} +!10 = !{!"double", !8, i64 0} +!11 = !{!6, !7, i64 0} +!12 = !{!13, !10, i64 0} +!13 = !{!"S2", !14, i64 0, !10, i64 0} +!14 = !{!"S1", !7, i64 0, !15, i64 4, !7, i64 8} +!15 = !{!"float", !8, i64 0} +!16 = !{!13, !15, i64 4} +!17 = !{!13, !7, i64 8} +!18 = !{!19, !15, i64 0} +!19 = !{!"SU", !20, i64 0, !21, i64 0} +!20 = !{!"FloatS", !15, i64 0, !15, i64 4} +!21 = !{!"IntS", !22, i64 0, !22, i64 2, !8, i64 4} +!22 = !{!"short", !8, i64 0} +!23 = !{!19, !8, i64 4} +!24 = !{!19, !22, i64 2} + diff --git a/llvm/test/Verifier/tbaa.ll b/llvm/test/Verifier/tbaa.ll index abaa415aed749..107192542d55d 100644 --- a/llvm/test/Verifier/tbaa.ll +++ b/llvm/test/Verifier/tbaa.ll @@ -61,15 +61,15 @@ define void @f_1(ptr %ptr) { ; CHECK: Cycle detected in struct path ; CHECK-NEXT: store i32 0, ptr %ptr, align 4, !tbaa !{{[0-9]+}} -; CHECK: Offset not zero at the point of scalar access +; CHECK: Did not see access type in access path +; CHECK-NEXT: store i32 0, ptr %ptr, align 4, !tbaa !{{[0-9]+}} + +; CHECK: Did not see access type in access path ; CHECK-NEXT: store i32 1, ptr %ptr, align 4, !tbaa !{{[0-9]+}} -; CHECK: Offset not zero at the point of scalar access +; CHECK: Did not see access type in access path ; CHECK-NEXT: store i32 2, ptr %ptr, align 4, !tbaa !{{[0-9]+}} -; CHECK: Could not find TBAA parent in struct type node -; CHECK-NEXT: store i32 3, ptr %ptr, align 4, !tbaa !{{[0-9]+}} - ; CHECK: Did not see access type in access path! ; CHECK-NEXT: store i32 3, ptr %ptr, align 4, !tbaa !{{[0-9]+}}