Recommit "[TBAA] Emit distinct TBAA tags for pointers with different depths,types. (#76612)"

This reverts the revert commit bee240367c.

This version includes updates to the tests to use patterns when matching
the pointer argument.

Original commit message:

    This patch extends Clang's TBAA generation code to emit distinct tags
    for incompatible pointer types.

    Pointers with different element types are incompatible if the pointee
    types are also incompatible (modulo sugar/modifiers).

    Express this in TBAA by generating different tags for pointers based on
    the pointer depth and pointee type. To get the TBAA tag for the pointee
    type it uses getTypeInfoHelper on the pointee type.

    (Moved from https://reviews.llvm.org/D122573)

    PR: https://github.com/llvm/llvm-project/pull/76612
This commit is contained in:
Florian Hahn
2024-07-19 13:03:21 +01:00
parent ef47bbb471
commit 123c036bd3
9 changed files with 572 additions and 472 deletions

View File

@@ -435,6 +435,10 @@ Non-comprehensive list of changes in this release
- Added support for ``TypeLoc::dump()`` for easier debugging, and improved
textual and JSON dumping for various ``TypeLoc``-related nodes.
- Clang can now emit distinct type-based alias analysis tags for incompatible
pointers, enabling more powerful alias analysis when accessing pointer types.
The new behavior can be enabled using ``-fpointer-tbaa``.
New Compiler Flags
------------------
- ``-fsanitize=implicit-bitfield-conversion`` checks implicit truncation and
@@ -477,6 +481,9 @@ New Compiler Flags
- For the ARM target, added ``-Warm-interrupt-vfp-clobber`` that will emit a
diagnostic when an interrupt handler is declared and VFP is enabled.
- ``-fpointer-tbaa`` enables emission of distinct type-based alias
analysis tags for incompatible pointers.
Deprecated Compiler Flags
-------------------------

View File

@@ -234,6 +234,7 @@ ENUM_CODEGENOPT(StructReturnConvention, StructReturnConventionKind, 2, SRCK_Defa
CODEGENOPT(RelaxAll , 1, 0) ///< Relax all machine code instructions.
CODEGENOPT(RelaxedAliasing , 1, 0) ///< Set when -fno-strict-aliasing is enabled.
CODEGENOPT(PointerTBAA, 1, 0) ///< Whether or not to use distinct TBAA tags for pointers.
CODEGENOPT(StructPathTBAA , 1, 0) ///< Whether or not to use struct-path TBAA.
CODEGENOPT(NewStructPathTBAA , 1, 0) ///< Whether or not to use enhanced struct-path TBAA.
CODEGENOPT(SaveTempLabels , 1, 0) ///< Save temporary labels.

View File

@@ -3389,6 +3389,7 @@ def fstruct_path_tbaa : Flag<["-"], "fstruct-path-tbaa">, Group<f_Group>;
def fno_struct_path_tbaa : Flag<["-"], "fno-struct-path-tbaa">, Group<f_Group>;
def fno_strict_enums : Flag<["-"], "fno-strict-enums">, Group<f_Group>;
def fno_strict_overflow : Flag<["-"], "fno-strict-overflow">, Group<f_Group>;
def fno_pointer_tbaa : Flag<["-"], "fno-pointer-tbaa">, Group<f_Group>;
def fno_temp_file : Flag<["-"], "fno-temp-file">, Group<f_Group>,
Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>, HelpText<
"Directly create compilation output files. This may lead to incorrect incremental builds if the compiler crashes">,
@@ -3893,6 +3894,7 @@ defm strict_vtable_pointers : BoolFOption<"strict-vtable-pointers",
" overwriting polymorphic C++ objects">,
NegFlag<SetFalse>>;
def fstrict_overflow : Flag<["-"], "fstrict-overflow">, Group<f_Group>;
def fpointer_tbaa : Flag<["-"], "fpointer-tbaa">, Group<f_Group>;
def fdriver_only : Flag<["-"], "fdriver-only">, Flags<[NoXarchOption]>,
Visibility<[ClangOption, CLOption, DXCOption]>,
Group<Action_Group>, HelpText<"Only run the driver.">;
@@ -7173,6 +7175,9 @@ def fuse_register_sized_bitfield_access: Flag<["-"], "fuse-register-sized-bitfie
def relaxed_aliasing : Flag<["-"], "relaxed-aliasing">,
HelpText<"Turn off Type Based Alias Analysis">,
MarshallingInfoFlag<CodeGenOpts<"RelaxedAliasing">>;
def pointer_tbaa: Flag<["-"], "pointer-tbaa">,
HelpText<"Turn on Type Based Alias Analysis for pointer accesses">,
MarshallingInfoFlag<CodeGenOpts<"PointerTBAA">>;
def no_struct_path_tbaa : Flag<["-"], "no-struct-path-tbaa">,
HelpText<"Turn off struct-path aware Type Based Alias Analysis">,
MarshallingInfoNegativeFlag<CodeGenOpts<"StructPathTBAA">>;

View File

@@ -186,10 +186,56 @@ llvm::MDNode *CodeGenTBAA::getTypeInfoHelper(const Type *Ty) {
return getChar();
// Handle pointers and references.
// TODO: Implement C++'s type "similarity" and consider dis-"similar"
// pointers distinct.
if (Ty->isPointerType() || Ty->isReferenceType())
return createScalarTypeNode("any pointer", getChar(), Size);
//
// C has a very strict rule for pointer aliasing. C23 6.7.6.1p2:
// For two pointer types to be compatible, both shall be identically
// qualified and both shall be pointers to compatible types.
//
// This rule is impractically strict; we want to at least ignore CVR
// qualifiers. Distinguishing by CVR qualifiers would make it UB to
// e.g. cast a `char **` to `const char * const *` and dereference it,
// which is too common and useful to invalidate. C++'s similar types
// rule permits qualifier differences in these nested positions; in fact,
// C++ even allows that cast as an implicit conversion.
//
// Other qualifiers could theoretically be distinguished, especially if
// they involve a significant representation difference. We don't
// currently do so, however.
//
// Computing the pointee type string recursively is implicitly more
// forgiving than the standards require. Effectively, we are turning
// the question "are these types compatible/similar" into "are
// accesses to these types allowed to alias". In both C and C++,
// the latter question has special carve-outs for signedness
// mismatches that only apply at the top level. As a result, we are
// allowing e.g. `int *` l-values to access `unsigned *` objects.
if (Ty->isPointerType() || Ty->isReferenceType()) {
llvm::MDNode *AnyPtr = createScalarTypeNode("any pointer", getChar(), Size);
if (!CodeGenOpts.PointerTBAA)
return AnyPtr;
// Compute the depth of the pointer and generate a tag of the form "p<depth>
// <base type tag>".
unsigned PtrDepth = 0;
do {
PtrDepth++;
Ty = Ty->getPointeeType().getTypePtr();
} while (Ty->isPointerType());
// TODO: Implement C++'s type "similarity" and consider dis-"similar"
// pointers distinct for non-builtin types.
if (isa<BuiltinType>(Ty)) {
llvm::MDNode *ScalarMD = getTypeInfoHelper(Ty);
StringRef Name =
cast<llvm::MDString>(
ScalarMD->getOperand(CodeGenOpts.NewStructPathTBAA ? 2 : 0))
->getString();
SmallString<256> OutName("p");
OutName += std::to_string(PtrDepth);
OutName += " ";
OutName += Name;
return createScalarTypeNode(OutName, AnyPtr, Size);
}
return AnyPtr;
}
// Accesses to arrays are accesses to objects of their element types.
if (CodeGenOpts.NewStructPathTBAA && Ty->isArrayType())

View File

@@ -5736,6 +5736,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
if (!Args.hasFlag(options::OPT_fstrict_aliasing, StrictAliasingAliasOption,
options::OPT_fno_strict_aliasing, !IsWindowsMSVC))
CmdArgs.push_back("-relaxed-aliasing");
if (Args.hasFlag(options::OPT_fpointer_tbaa, options::OPT_fno_pointer_tbaa,
false))
CmdArgs.push_back("-pointer-tbaa");
if (!Args.hasFlag(options::OPT_fstruct_path_tbaa,
options::OPT_fno_struct_path_tbaa, true))
CmdArgs.push_back("-no-struct-path-tbaa");

View File

@@ -12,7 +12,7 @@
//.
// CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none)
// CHECK-LABEL: define dso_local void @escape
// CHECK-SAME: (ptr noundef [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] !pcsections !2 {
// CHECK-SAME: (ptr noundef [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] !pcsections [[META2:![0-9]+]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret void
//
@@ -23,13 +23,13 @@ __attribute__((noinline, not_tail_called)) void escape(const volatile void *p) {
// CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none)
// CHECK-LABEL: define dso_local i32 @normal_function
// CHECK-SAME: (ptr noundef [[X:%.*]], ptr nocapture noundef readonly [[Y:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] !pcsections !4 {
// CHECK-SAME: (ptr noundef [[X:%.*]], ptr nocapture noundef readonly [[Y:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] !pcsections [[META4:![0-9]+]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[X_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8, !tbaa [[TBAA6:![0-9]+]]
// CHECK-NEXT: store atomic i32 1, ptr [[X]] monotonic, align 4, !pcsections !10
// CHECK-NEXT: store atomic i32 1, ptr [[X]] monotonic, align 4, !pcsections [[META11:![0-9]+]]
// CHECK-NEXT: notail call void @escape(ptr noundef nonnull [[X_ADDR]])
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[Y]], align 4, !tbaa [[TBAA11:![0-9]+]]
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[Y]], align 4, !tbaa [[TBAA12:![0-9]+]]
// CHECK-NEXT: ret i32 [[TMP0]]
//
int normal_function(int *x, int *y) {
@@ -46,7 +46,7 @@ int normal_function(int *x, int *y) {
// CHECK-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8, !tbaa [[TBAA6]]
// CHECK-NEXT: store atomic i32 1, ptr [[X]] monotonic, align 4
// CHECK-NEXT: notail call void @escape(ptr noundef nonnull [[X_ADDR]])
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[Y]], align 4, !tbaa [[TBAA11]]
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[Y]], align 4, !tbaa [[TBAA12]]
// CHECK-NEXT: ret i32 [[TMP0]]
//
__attribute__((disable_sanitizer_instrumentation)) int test_disable_sanitize_instrumentation(int *x, int *y) {
@@ -57,13 +57,13 @@ __attribute__((disable_sanitizer_instrumentation)) int test_disable_sanitize_ins
// CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none)
// CHECK-LABEL: define dso_local i32 @test_no_sanitize_thread
// CHECK-SAME: (ptr noundef [[X:%.*]], ptr nocapture noundef readonly [[Y:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] !pcsections !13 {
// CHECK-SAME: (ptr noundef [[X:%.*]], ptr nocapture noundef readonly [[Y:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] !pcsections [[META14:![0-9]+]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[X_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8, !tbaa [[TBAA6]]
// CHECK-NEXT: store atomic i32 1, ptr [[X]] monotonic, align 4, !pcsections !10
// CHECK-NEXT: store atomic i32 1, ptr [[X]] monotonic, align 4, !pcsections [[META11]]
// CHECK-NEXT: notail call void @escape(ptr noundef nonnull [[X_ADDR]])
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[Y]], align 4, !tbaa [[TBAA11]]
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[Y]], align 4, !tbaa [[TBAA12]]
// CHECK-NEXT: ret i32 [[TMP0]]
//
__attribute__((no_sanitize("thread"))) int test_no_sanitize_thread(int *x, int *y) {
@@ -74,13 +74,13 @@ __attribute__((no_sanitize("thread"))) int test_no_sanitize_thread(int *x, int *
// CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none)
// CHECK-LABEL: define dso_local i32 @test_no_sanitize_all
// CHECK-SAME: (ptr noundef [[X:%.*]], ptr nocapture noundef readonly [[Y:%.*]]) local_unnamed_addr #[[ATTR3]] !pcsections !13 {
// CHECK-SAME: (ptr noundef [[X:%.*]], ptr nocapture noundef readonly [[Y:%.*]]) local_unnamed_addr #[[ATTR3]] !pcsections [[META14]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[X_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8, !tbaa [[TBAA6]]
// CHECK-NEXT: store atomic i32 1, ptr [[X]] monotonic, align 4, !pcsections !10
// CHECK-NEXT: store atomic i32 1, ptr [[X]] monotonic, align 4, !pcsections [[META11]]
// CHECK-NEXT: notail call void @escape(ptr noundef nonnull [[X_ADDR]])
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[Y]], align 4, !tbaa [[TBAA11]]
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[Y]], align 4, !tbaa [[TBAA12]]
// CHECK-NEXT: ret i32 [[TMP0]]
//
__attribute__((no_sanitize("all"))) int test_no_sanitize_all(int *x, int *y) {
@@ -89,23 +89,9 @@ __attribute__((no_sanitize("all"))) int test_no_sanitize_all(int *x, int *y) {
return *y;
}
//.
// CHECK: attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
// CHECK: attributes #1 = { mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
// CHECK: attributes #2 = { disable_sanitizer_instrumentation mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
// CHECK: attributes #3 = { mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "no_sanitize_thread" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
// CHECK: attributes #4 = { nounwind "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
//.
// CHECK: !2 = !{!"sanmd_covered2!C", !3}
// CHECK: !3 = !{i64 0}
// CHECK: !4 = !{!"sanmd_covered2!C", !5}
// CHECK: !5 = !{i64 3}
// CHECK: !6 = !{!7, !7, i64 0}
// CHECK: !7 = !{!"any pointer", !8, i64 0}
// CHECK: !8 = !{!"omnipotent char", !9, i64 0}
// CHECK: !9 = !{!"Simple C/C++ TBAA"}
// CHECK: !10 = !{!"sanmd_atomics2!C"}
// CHECK: !11 = !{!12, !12, i64 0}
// CHECK: !12 = !{!"int", !8, i64 0}
// CHECK: !13 = !{!"sanmd_covered2!C", !14}
// CHECK: !14 = !{i64 2}
// CHECK: attributes #[[ATTR0]] = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
// CHECK: attributes #[[ATTR1]] = { mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
// CHECK: attributes #[[ATTR2]] = { disable_sanitizer_instrumentation mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
// CHECK: attributes #[[ATTR3]] = { mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "no_sanitize_thread" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
// CHECK: attributes #[[ATTR4:[0-9]+]] = { nounwind "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
//.

View File

@@ -1,80 +1,115 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin -O1 -disable-llvm-passes %s -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin -O1 -disable-llvm-passes %s -emit-llvm -o - | FileCheck --check-prefixes=COMMON,DEFAULT %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin -O1 -disable-llvm-passes -pointer-tbaa %s -emit-llvm -o - | FileCheck --check-prefixes=COMMON,ENABLED %s
// RUN: %clang --target=x86_64-apple-darwin -O1 %s -emit-llvm -S -mllvm -disable-llvm-optzns -o - | FileCheck --check-prefixes=COMMON,DEFAULT %s
// RUN: %clang --target=x86_64-apple-darwin -O1 -fpointer-tbaa %s -emit-llvm -S -mllvm -disable-llvm-optzns -o - | FileCheck --check-prefixes=COMMON,ENABLED %s
void p2unsigned(unsigned **ptr) {
// CHECK-LABEL: define void @p2unsigned(ptr noundef %ptr)
// CHECK-NEXT: entry:
// CHECK-NEXT: %ptr.addr = alloca ptr, align 8
// CHECK-NEXT: store ptr %ptr, ptr %ptr.addr, align 8, !tbaa [[ANY_POINTER_0:!.+]]
// CHECK-NEXT: [[BASE:%.+]] = load ptr, ptr %ptr.addr, align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: store ptr null, ptr [[BASE]], align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: ret void
// COMMON-LABEL: define void @p2unsigned(
// COMMON-SAME: ptr noundef [[PTR:%.+]])
// COMMON-NEXT: entry:
// COMMON-NEXT: [[PTR_ADDR:%.+]] = alloca ptr, align 8
// ENABLED-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[P2INT_0:!.+]]
// ENABLED-NEXT: [[BASE:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[P2INT_0]]
// ENABLED-NEXT: store ptr null, ptr [[BASE]], align 8, !tbaa [[P1INT_0:!.+]]
// DEFAULT-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR:!.+]]
// DEFAULT-NEXT: [[BASE:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: store ptr null, ptr [[BASE]], align 8, !tbaa [[ANYPTR]]
// COMMON-NEXT: ret void
//
*ptr = 0;
}
void p2unsigned_volatile(unsigned *volatile *ptr) {
// CHECK-LABEL: define void @p2unsigned_volatile(ptr noundef %ptr)
// CHECK-NEXT: entry:
// CHECK-NEXT: %ptr.addr = alloca ptr, align 8
// CHECK-NEXT: store ptr %ptr, ptr %ptr.addr, align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: [[BASE:%.+]] = load ptr, ptr %ptr.addr, align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: store volatile ptr null, ptr [[BASE]], align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: ret void
// COMMON-LABEL: define void @p2unsigned_volatile(
// COMMON-SAME: ptr noundef [[PTR:%.+]])
// COMMON-NEXT: entry:
// COMMON-NEXT: [[PTR_ADDR:%.+]] = alloca ptr, align 8
// ENABLED-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[P2INT_0]]
// ENABLED-NEXT: [[BASE:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[P2INT_0]]
// ENABLED-NEXT: store volatile ptr null, ptr [[BASE]], align 8, !tbaa [[P1INT_0]]
// DEFAULT-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: [[BASE:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: store volatile ptr null, ptr [[BASE]], align 8, !tbaa [[ANYPTR]]
// COMMON-NEXT: ret void
//
*ptr = 0;
}
void p3int(int ***ptr) {
// CHECK-LABEL: define void @p3int(ptr noundef %ptr)
// CHECK-NEXT: entry:
// CHECK-NEXT: %ptr.addr = alloca ptr, align 8
// CHECK-NEXT: store ptr %ptr, ptr %ptr.addr, align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: [[BASE_0:%.+]] = load ptr, ptr %ptr.addr, align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: [[BASE_1:%.+]] = load ptr, ptr [[BASE_0]], align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: store ptr null, ptr [[BASE_1]], align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: ret void
// COMMON-LABEL: define void @p3int(
// COMMON-SAME: ptr noundef [[PTR:%.+]])
// COMMON-NEXT: entry:
// COMMON-NEXT: [[PTR_ADDR:%.+]] = alloca ptr, align 8
// ENABLED-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[P3INT_0:!.+]]
// ENABLED-NEXT: [[BASE_0:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[P3INT_0]]
// ENABLED-NEXT: [[BASE_1:%.+]] = load ptr, ptr [[BASE_0]], align 8, !tbaa [[P2INT_0]]
// ENABLED-NEXT: store ptr null, ptr [[BASE_1]], align 8, !tbaa [[P1INT_0]]
// DEFAULT-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: [[BASE_0:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: [[BASE_1:%.+]] = load ptr, ptr [[BASE_0]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: store ptr null, ptr [[BASE_1]], align 8, !tbaa [[ANYPTR]]
// COMMON-NEXT: ret void
//
**ptr = 0;
}
void p4char(char ****ptr) {
// CHECK-LABEL: define void @p4char(ptr noundef %ptr)
// CHECK-NEXT: entry:
// CHECK-NEXT: %ptr.addr = alloca ptr, align 8
// CHECK-NEXT: store ptr %ptr, ptr %ptr.addr, align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: [[BASE_0:%.+]] = load ptr, ptr %ptr.addr, align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: [[BASE_1:%.+]] = load ptr, ptr [[BASE_0]], align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: [[BASE_2:%.+]] = load ptr, ptr [[BASE_1]], align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: store ptr null, ptr [[BASE_2]], align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: ret void
// COMMON-LABEL: define void @p4char(
// COMMON-SAME: ptr noundef [[PTR:%.+]])
// COMMON-NEXT: entry:
// COMMON-NEXT: [[PTR_ADDR:%.+]] = alloca ptr, align 8
// ENABLED-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[P4CHAR_0:!.+]]
// ENABLED-NEXT: [[BASE_0:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[P4CHAR_0]]
// ENABLED-NEXT: [[BASE_1:%.+]] = load ptr, ptr [[BASE_0]], align 8, !tbaa [[P3CHAR_0:!.+]]
// ENABLED-NEXT: [[BASE_2:%.+]] = load ptr, ptr [[BASE_1]], align 8, !tbaa [[P2CHAR_0:!.+]]
// ENABLED-NEXT: store ptr null, ptr [[BASE_2]], align 8, !tbaa [[P1CHAR_0:!.+]]
// DEFAULT-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: [[BASE_0:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: [[BASE_1:%.+]] = load ptr, ptr [[BASE_0]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: [[BASE_2:%.+]] = load ptr, ptr [[BASE_1]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: store ptr null, ptr [[BASE_2]], align 8, !tbaa [[ANYPTR]]
// COMMON-NEXT: ret void
//
***ptr = 0;
}
void p4char_const1(const char ****ptr) {
// CHECK-LABEL: define void @p4char_const1(ptr noundef %ptr)
// CHECK-NEXT: entry:
// CHECK-NEXT: %ptr.addr = alloca ptr, align 8
// CHECK-NEXT: store ptr %ptr, ptr %ptr.addr, align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: [[BASE_0:%.+]] = load ptr, ptr %ptr.addr, align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: [[BASE_1:%.+]] = load ptr, ptr [[BASE_0]], align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: [[BASE_2:%.+]] = load ptr, ptr [[BASE_1]], align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: store ptr null, ptr [[BASE_2]], align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: ret void
// COMMON-LABEL: define void @p4char_const1(
// COMMON-SAME: ptr noundef [[PTR:%.+]])
// COMMON-NEXT: entry:
// COMMON-NEXT: [[PTR_ADDR:%.+]] = alloca ptr, align 8
// ENABLED-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[P4CHAR_0]]
// ENABLED-NEXT: [[BASE_0:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[P4CHAR_0]]
// ENABLED-NEXT: [[BASE_1:%.+]] = load ptr, ptr [[BASE_0]], align 8, !tbaa [[P3CHAR_0]]
// ENABLED-NEXT: [[BASE_2:%.+]] = load ptr, ptr [[BASE_1]], align 8, !tbaa [[P2CHAR_0]]
// ENABLED-NEXT: store ptr null, ptr [[BASE_2]], align 8, !tbaa [[P1CHAR_0]]
// DEFAULT-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: [[BASE_0:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: [[BASE_1:%.+]] = load ptr, ptr [[BASE_0]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: [[BASE_2:%.+]] = load ptr, ptr [[BASE_1]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: store ptr null, ptr [[BASE_2]], align 8, !tbaa [[ANYPTR]]
// COMMON-NEXT: ret void
//
***ptr = 0;
}
void p4char_const2(const char **const **ptr) {
// CHECK-LABEL: define void @p4char_const2(ptr noundef %ptr)
// CHECK-NEXT: entry:
// CHECK-NEXT: %ptr.addr = alloca ptr, align 8
// CHECK-NEXT: store ptr %ptr, ptr %ptr.addr, align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: [[BASE_0:%.+]] = load ptr, ptr %ptr.addr, align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: [[BASE_1:%.+]] = load ptr, ptr [[BASE_0]], align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: [[BASE_2:%.+]] = load ptr, ptr [[BASE_1]], align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: store ptr null, ptr [[BASE_2]], align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: ret void
// COMMON-LABEL: define void @p4char_const2(
// COMMON-SAME: ptr noundef [[PTR:%.+]])
// COMMON-NEXT: entry:
// COMMON-NEXT: [[PTR_ADDR:%.+]] = alloca ptr, align 8
// ENABLED-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[P4CHAR_0]]
// ENABLED-NEXT: [[BASE_0:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[P4CHAR_0]]
// ENABLED-NEXT: [[BASE_1:%.+]] = load ptr, ptr [[BASE_0]], align 8, !tbaa [[P3CHAR_0]]
// ENABLED-NEXT: [[BASE_2:%.+]] = load ptr, ptr [[BASE_1]], align 8, !tbaa [[P2CHAR_0]]
// ENABLED-NEXT: store ptr null, ptr [[BASE_2]], align 8, !tbaa [[P1CHAR_0]]
// DEFAULT-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: [[BASE_0:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: [[BASE_1:%.+]] = load ptr, ptr [[BASE_0]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: [[BASE_2:%.+]] = load ptr, ptr [[BASE_1]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: store ptr null, ptr [[BASE_2]], align 8, !tbaa [[ANYPTR]]
// COMMON-NEXT: ret void
//
***ptr = 0;
}
@@ -85,19 +120,36 @@ struct S1 {
};
void p2struct(struct S1 **ptr) {
// CHECK-LABEL: define void @p2struct(ptr noundef %ptr)
// CHECK-NEXT: entry:
// CHECK-NEXT: %ptr.addr = alloca ptr, align 8
// CHECK-NEXT: store ptr %ptr, ptr %ptr.addr, align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: [[BASE:%.+]] = load ptr, ptr %ptr.addr, align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: store ptr null, ptr [[BASE]], align 8, !tbaa [[ANY_POINTER_0]]
// CHECK-NEXT: ret void
// COMMON-LABEL: define void @p2struct(
// COMMON-SAME: ptr noundef [[PTR:%.+]])
// COMMON-NEXT: entry:
// COMMON-NEXT: [[PTR_ADDR:%.+]] = alloca ptr, align 8
// ENABLED-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[P2S1_0:!.+]]
// ENABLED-NEXT: [[BASE:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[P2S1_0]]
// ENABLED-NEXT: store ptr null, ptr [[BASE]], align 8, !tbaa [[P1S1_:!.+]]
// DEFAULT-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: [[BASE:%.+]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[ANYPTR]]
// DEFAULT-NEXT: store ptr null, ptr [[BASE]], align 8, !tbaa [[ANYPTR]]
// COMMON-NEXT: ret void
//
*ptr = 0;
}
// CHECK: [[ANY_POINTER_0]] = !{[[ANY_POINTER:!.+]], [[ANY_POINTER]], i64 0}
// CHECK: [[ANY_POINTER]] = !{!"any pointer", [[CHAR:!.+]], i64 0}
// CHECK: [[CHAR]] = !{!"omnipotent char", [[TBAA_ROOT:!.+]], i64 0}
// CHECK: [[TBAA_ROOT]] = !{!"Simple C/C++ TBAA"}
//
// ENABLED: [[P2INT_0]] = !{[[P2INT:!.+]], [[P2INT]], i64 0}
// ENABLED: [[P2INT]] = !{!"p2 int", [[ANY_POINTER:!.+]], i64 0}
// DEFAULT: [[ANYPTR]] = !{[[ANY_POINTER:!.+]], [[ANY_POINTER]], i64 0}
// COMMON: [[ANY_POINTER]] = !{!"any pointer", [[CHAR:!.+]], i64 0}
// COMMON: [[CHAR]] = !{!"omnipotent char", [[TBAA_ROOT:!.+]], i64 0}
// COMMON: [[TBAA_ROOT]] = !{!"Simple C/C++ TBAA"}
// ENABLED: [[P1INT_0]] = !{[[P1INT:!.+]], [[P1INT]], i64 0}
// ENABLED: [[P1INT]] = !{!"p1 int", [[ANY_POINTER]], i64 0}
// ENABLED: [[P3INT_0]] = !{[[P3INT:!.+]], [[P3INT]], i64 0}
// ENABLED: [[P3INT]] = !{!"p3 int", [[ANY_POINTER]], i64 0}
// ENABLED: [[P4CHAR_0]] = !{[[P4CHAR:!.+]], [[P4CHAR]], i64 0}
// ENABLED: [[P4CHAR]] = !{!"p4 omnipotent char", [[ANY_POINTER]], i64 0}
// ENABLED: [[P3CHAR_0]] = !{[[P3CHAR:!.+]], [[P3CHAR]], i64 0}
// ENABLED: [[P3CHAR]] = !{!"p3 omnipotent char", [[ANY_POINTER]], i64 0}
// ENABLED: [[P2CHAR_0]] = !{[[P2CHAR:!.+]], [[P2CHAR]], i64 0}
// ENABLED: [[P2CHAR]] = !{!"p2 omnipotent char", [[ANY_POINTER]], i64 0}
// ENABLED: [[P1CHAR_0]] = !{[[P1CHAR:!.+]], [[P1CHAR]], i64 0}
// ENABLED: [[P1CHAR]] = !{!"p1 omnipotent char", [[ANY_POINTER]], i64 0}

View File

@@ -68,23 +68,23 @@ void w_branch_elided(unsigned e){
// CHECK-NEXT: [[E_ADDR:%.*]] = alloca i32, align 4
// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4
// CHECK-NEXT: store i32 [[E:%.*]], ptr [[E_ADDR]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr [[I]]) #[[ATTR4:[0-9]+]]
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr [[I]]) #[[ATTR3:[0-9]+]]
// CHECK-NEXT: store i32 0, ptr [[I]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: br label [[FOR_COND:%.*]]
// CHECK: for.cond:
// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[I]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[E_ADDR]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[TMP1]], [[TMP2]]
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[I]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[E_ADDR]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[TMP0]], [[TMP1]]
// CHECK-NEXT: [[CMP_EXPVAL:%.*]] = call i1 @llvm.expect.i1(i1 [[CMP]], i1 true)
// CHECK-NEXT: br i1 [[CMP_EXPVAL]], label [[FOR_BODY:%.*]], label [[FOR_COND_CLEANUP:%.*]]
// CHECK: for.cond.cleanup:
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr [[I]]) #[[ATTR4]]
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr [[I]]) #[[ATTR3]]
// CHECK-NEXT: br label [[FOR_END:%.*]]
// CHECK: for.body:
// CHECK-NEXT: br label [[FOR_INC:%.*]]
// CHECK: for.inc:
// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[E_ADDR]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: [[INC:%.*]] = add i32 [[TMP4]], 1
// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[E_ADDR]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: [[INC:%.*]] = add i32 [[TMP2]], 1
// CHECK-NEXT: store i32 [[INC]], ptr [[E_ADDR]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: br label [[FOR_COND]], !llvm.loop [[LOOP11:![0-9]+]]
// CHECK: for.end:
@@ -100,23 +100,23 @@ void fl(unsigned e)
// CHECK-NEXT: [[E_ADDR:%.*]] = alloca i32, align 4
// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4
// CHECK-NEXT: store i32 [[E:%.*]], ptr [[E_ADDR]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr [[I]]) #[[ATTR4]]
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr [[I]]) #[[ATTR3]]
// CHECK-NEXT: store i32 0, ptr [[I]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: br label [[FOR_COND:%.*]]
// CHECK: for.cond:
// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[I]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[E_ADDR]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[TMP1]], [[TMP2]]
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[I]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[E_ADDR]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[TMP0]], [[TMP1]]
// CHECK-NEXT: [[CMP_EXPVAL:%.*]] = call i1 @llvm.expect.i1(i1 [[CMP]], i1 false)
// CHECK-NEXT: br i1 [[CMP_EXPVAL]], label [[FOR_BODY:%.*]], label [[FOR_COND_CLEANUP:%.*]]
// CHECK: for.cond.cleanup:
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr [[I]]) #[[ATTR4]]
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr [[I]]) #[[ATTR3]]
// CHECK-NEXT: br label [[FOR_END:%.*]]
// CHECK: for.body:
// CHECK-NEXT: br label [[FOR_INC:%.*]]
// CHECK: for.inc:
// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[E_ADDR]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP4]], 1
// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[E_ADDR]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP2]], 1
// CHECK-NEXT: store i32 [[INC]], ptr [[E_ADDR]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: br label [[FOR_COND]], !llvm.loop [[LOOP12:![0-9]+]]
// CHECK: for.end:
@@ -146,42 +146,42 @@ void f_branch_elided()
// CHECK-NEXT: [[__END1:%.*]] = alloca ptr, align 8
// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4
// CHECK-NEXT: store ptr [[E:%.*]], ptr [[E_ADDR]], align 8, !tbaa [[TBAA14:![0-9]+]]
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[__RANGE1]]) #[[ATTR4]]
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[E_ADDR]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: store ptr [[TMP1]], ptr [[__RANGE1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[__BEGIN1]]) #[[ATTR4]]
// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[__RANGE1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [4 x i32], ptr [[TMP3]], i64 0, i64 0
// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[__BEGIN1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[__END1]]) #[[ATTR4]]
// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[__RANGE1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: [[ARRAYDECAY1:%.*]] = getelementptr inbounds [4 x i32], ptr [[TMP5]], i64 0, i64 0
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[__RANGE1]]) #[[ATTR3]]
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[E_ADDR]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: store ptr [[TMP0]], ptr [[__RANGE1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[__BEGIN1]]) #[[ATTR3]]
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[__RANGE1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [4 x i32], ptr [[TMP1]], i64 0, i64 0
// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[__BEGIN1]], align 8, !tbaa [[TBAA16:![0-9]+]]
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[__END1]]) #[[ATTR3]]
// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[__RANGE1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: [[ARRAYDECAY1:%.*]] = getelementptr inbounds [4 x i32], ptr [[TMP2]], i64 0, i64 0
// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY1]], i64 4
// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[__END1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[__END1]], align 8, !tbaa [[TBAA16]]
// CHECK-NEXT: br label [[FOR_COND:%.*]]
// CHECK: for.cond:
// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[__BEGIN1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[__END1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[TMP6]], [[TMP7]]
// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[__BEGIN1]], align 8, !tbaa [[TBAA16]]
// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[__END1]], align 8, !tbaa [[TBAA16]]
// CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[TMP3]], [[TMP4]]
// CHECK-NEXT: [[CMP_EXPVAL:%.*]] = call i1 @llvm.expect.i1(i1 [[CMP]], i1 true)
// CHECK-NEXT: br i1 [[CMP_EXPVAL]], label [[FOR_BODY:%.*]], label [[FOR_COND_CLEANUP:%.*]]
// CHECK: for.cond.cleanup:
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[__END1]]) #[[ATTR4]]
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[__BEGIN1]]) #[[ATTR4]]
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[__RANGE1]]) #[[ATTR4]]
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[__END1]]) #[[ATTR3]]
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[__BEGIN1]]) #[[ATTR3]]
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[__RANGE1]]) #[[ATTR3]]
// CHECK-NEXT: br label [[FOR_END:%.*]]
// CHECK: for.body:
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr [[I]]) #[[ATTR4]]
// CHECK-NEXT: [[TMP12:%.*]] = load ptr, ptr [[__BEGIN1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: [[TMP13:%.*]] = load i32, ptr [[TMP12]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: store i32 [[TMP13]], ptr [[I]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr [[I]]) #[[ATTR4]]
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr [[I]]) #[[ATTR3]]
// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[__BEGIN1]], align 8, !tbaa [[TBAA16]]
// CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[TMP5]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: store i32 [[TMP6]], ptr [[I]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr [[I]]) #[[ATTR3]]
// CHECK-NEXT: br label [[FOR_INC:%.*]]
// CHECK: for.inc:
// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[__BEGIN1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP15]], i32 1
// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[__BEGIN1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: br label [[FOR_COND]], !llvm.loop [[LOOP16:![0-9]+]]
// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[__BEGIN1]], align 8, !tbaa [[TBAA16]]
// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP7]], i32 1
// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[__BEGIN1]], align 8, !tbaa [[TBAA16]]
// CHECK-NEXT: br label [[FOR_COND]], !llvm.loop [[LOOP18:![0-9]+]]
// CHECK: for.end:
// CHECK-NEXT: ret void
//
@@ -198,42 +198,42 @@ void frl(int (&&e) [4])
// CHECK-NEXT: [[__END1:%.*]] = alloca ptr, align 8
// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4
// CHECK-NEXT: store ptr [[E:%.*]], ptr [[E_ADDR]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[__RANGE1]]) #[[ATTR4]]
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[E_ADDR]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: store ptr [[TMP1]], ptr [[__RANGE1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[__BEGIN1]]) #[[ATTR4]]
// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[__RANGE1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [4 x i32], ptr [[TMP3]], i64 0, i64 0
// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[__BEGIN1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[__END1]]) #[[ATTR4]]
// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[__RANGE1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: [[ARRAYDECAY1:%.*]] = getelementptr inbounds [4 x i32], ptr [[TMP5]], i64 0, i64 0
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[__RANGE1]]) #[[ATTR3]]
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[E_ADDR]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: store ptr [[TMP0]], ptr [[__RANGE1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[__BEGIN1]]) #[[ATTR3]]
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[__RANGE1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [4 x i32], ptr [[TMP1]], i64 0, i64 0
// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[__BEGIN1]], align 8, !tbaa [[TBAA16]]
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[__END1]]) #[[ATTR3]]
// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[__RANGE1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: [[ARRAYDECAY1:%.*]] = getelementptr inbounds [4 x i32], ptr [[TMP2]], i64 0, i64 0
// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY1]], i64 4
// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[__END1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[__END1]], align 8, !tbaa [[TBAA16]]
// CHECK-NEXT: br label [[FOR_COND:%.*]]
// CHECK: for.cond:
// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[__BEGIN1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[__END1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[TMP6]], [[TMP7]]
// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[__BEGIN1]], align 8, !tbaa [[TBAA16]]
// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[__END1]], align 8, !tbaa [[TBAA16]]
// CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[TMP3]], [[TMP4]]
// CHECK-NEXT: [[CMP_EXPVAL:%.*]] = call i1 @llvm.expect.i1(i1 [[CMP]], i1 false)
// CHECK-NEXT: br i1 [[CMP_EXPVAL]], label [[FOR_BODY:%.*]], label [[FOR_COND_CLEANUP:%.*]]
// CHECK: for.cond.cleanup:
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[__END1]]) #[[ATTR4]]
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[__BEGIN1]]) #[[ATTR4]]
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[__RANGE1]]) #[[ATTR4]]
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[__END1]]) #[[ATTR3]]
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[__BEGIN1]]) #[[ATTR3]]
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[__RANGE1]]) #[[ATTR3]]
// CHECK-NEXT: br label [[FOR_END:%.*]]
// CHECK: for.body:
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr [[I]]) #[[ATTR4]]
// CHECK-NEXT: [[TMP12:%.*]] = load ptr, ptr [[__BEGIN1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: [[TMP13:%.*]] = load i32, ptr [[TMP12]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: store i32 [[TMP13]], ptr [[I]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr [[I]]) #[[ATTR4]]
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr [[I]]) #[[ATTR3]]
// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[__BEGIN1]], align 8, !tbaa [[TBAA16]]
// CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[TMP5]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: store i32 [[TMP6]], ptr [[I]], align 4, !tbaa [[TBAA2]]
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr [[I]]) #[[ATTR3]]
// CHECK-NEXT: br label [[FOR_INC:%.*]]
// CHECK: for.inc:
// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[__BEGIN1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP15]], i32 1
// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[__BEGIN1]], align 8, !tbaa [[TBAA14]]
// CHECK-NEXT: br label [[FOR_COND]], !llvm.loop [[LOOP17:![0-9]+]]
// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[__BEGIN1]], align 8, !tbaa [[TBAA16]]
// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP7]], i32 1
// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[__BEGIN1]], align 8, !tbaa [[TBAA16]]
// CHECK-NEXT: br label [[FOR_COND]], !llvm.loop [[LOOP19:![0-9]+]]
// CHECK: for.end:
// CHECK-NEXT: ret void
//