diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp --- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -12204,225 +12204,76 @@ } ChangeStatus updateImpl(Attributor &A) override { - ChangeStatus Changed = ChangeStatus::UNCHANGED; - auto MakeChange = [&](Instruction &I) { - AllocaInst &AI = cast(I); - const IRPosition &IRP = IRPosition::inst(AI); - auto *PI = - A.getOrCreateAAFor(IRP, *this, DepClassTy::OPTIONAL); - - if (!PI) { - LLVM_DEBUG(dbgs() << "[AAAllocationInfo, updateImpl] could not get " - "AAPointerInfo object" - << AI << "\n"); - return false; - } - - LLVM_DEBUG(dbgs() << "[AAAllocationInfo, updateImpl] Was able to create " - "AAPointer Info object " - << *PI << "\n"); - const AAPointerInfo &OtherAA = *PI; - - if (!OtherAA.getState().isValidState()) { - LLVM_DEBUG(dbgs() << "[AAAllocationInfo, updateImpl] AAPointerInfo not " - "in valid state." - << "\n"); - return false; - } - - const auto &OtherAAImpl = static_cast(*PI); - const auto &State = OtherAAImpl.getState(); - - int BinSize = State.size(); + auto *I = this->getIRPosition().getCtxI(); + AllocaInst &AI = cast(*I); + const IRPosition &IRP = IRPosition::inst(AI); + auto *PI = + A.getOrCreateAAFor(IRP, *this, DepClassTy::OPTIONAL); - if (BinSize > 1) { - LLVM_DEBUG(dbgs() << "[AAAllocationInfo, updateImpl] AAPointerInfo Bin " - "Size is greater than one. " - "Not supported yet!" - << "\n"); - return false; - } + if (!PI) { + return indicatePessimisticFixpoint(); + } - const auto &It = State.begin(); + const AAPointerInfo &OtherAA = *PI; - if (It->getFirst().Offset != 0) { - LLVM_DEBUG( - dbgs() << "[AAAllocationInfo, updateImpl] AAPointerInfo Access not " - "starting at 0th byte in Alloca." - "Not supported yet!" - << "\n"); - return false; - } + if (!OtherAA.getState().isValidState()) { + return indicatePessimisticFixpoint(); + } - LLVM_DEBUG(dbgs() << "[AAAllocationInfo, updateImpl] The first offset is " - "the start of the Alloca." - << "\n"); + const auto &OtherAAImpl = static_cast(*PI); + const auto &State = OtherAAImpl.getState(); - int64_t OffsetEnd = It->getFirst().Offset + It->getFirst().Size; - const DataLayout &DL = AI.getModule()->getDataLayout(); - const auto &AllocationSize = AI.getAllocationSize(DL); + int BinSize = State.size(); - if (!AllocationSize) { - LLVM_DEBUG( - dbgs() << "[AAAllocationInfo, updateImpl] Could not get allocation " - "size from Alloca." - << "\n"); - return false; - } + if (BinSize > 1) { + return indicatePessimisticFixpoint(); + } - if (OffsetEnd == AllocationSize) { - LLVM_DEBUG( - dbgs() << "[AAAllocationInfo, updateImpl] Offset size cannot be " - "reduced in Alloca." - << "\n"); - return false; - } + const auto &It = State.begin(); - Type *IntegerSizeOfOffsetEnd = - Type::getIntNTy(AI.getContext(), OffsetEnd * 8); + if (It->getFirst().Offset != 0) { + return indicatePessimisticFixpoint(); + } - LLVM_DEBUG(dbgs() << "[AAAllocationInfo] Successfully reduced allocation " - "size of alloca based on Bin." - << "\n"); + int64_t OffsetEnd = It->getFirst().Offset + It->getFirst().Size; + const DataLayout &DL = AI.getModule()->getDataLayout(); + const auto &AllocationSize = AI.getAllocationSize(DL); - Changed = ChangeStatus::CHANGED; + if (!AllocationSize) { + return indicatePessimisticFixpoint(); + } - changeAllocationSize(IntegerSizeOfOffsetEnd->getPrimitiveSizeInBits()); + if (OffsetEnd == AllocationSize) { + return indicatePessimisticFixpoint(); + } - return true; - }; + Type *IntegerSizeOfOffsetEnd = + Type::getIntNTy(AI.getContext(), OffsetEnd * 8); - auto *I = this->getIRPosition().getCtxI(); - if (!I) { + if (!changeAllocationSize( + IntegerSizeOfOffsetEnd->getPrimitiveSizeInBits())) { return ChangeStatus::UNCHANGED; } - if (!MakeChange(*I)) { - return indicatePessimisticFixpoint(); - } - - return Changed; + return ChangeStatus::CHANGED; } - ChangeStatus Changed = ChangeStatus::UNCHANGED; /// See AbstractAttribute::manifest(...). ChangeStatus manifest(Attributor &A) override { - auto MakeChange = [&](Instruction &I) { - AllocaInst &AI = cast(I); - const IRPosition &IRP = IRPosition::inst(AI); - auto *PI = - A.getOrCreateAAFor(IRP, *this, DepClassTy::OPTIONAL); - - if (!PI) { - LLVM_DEBUG( - dbgs() << "[AAAllocationInfo, manifest] Failed to verify all " - "interfering accesses for Instruction " - << AI << "\n"); - return false; - } - LLVM_DEBUG(dbgs() << "[AAAllocationInfo, manifest] Was able to create " - "AAPointer Info object " - << *PI << "\n"); - - const AAPointerInfo &OtherAA = *PI; - - if (!OtherAA.getState().isValidState()) { - LLVM_DEBUG( - dbgs() - << "[AAAllocationInfo, manifest] AAPointerInfo not in valid state." - << "\n"); - return false; - } - - const auto &OtherAAImpl = static_cast(*PI); - const auto &State = OtherAAImpl.getState(); - - int BinSize = State.size(); - - if (BinSize > 1) { - LLVM_DEBUG(dbgs() << "[AAAllocationInfo, manifest] AAPointerInfo Bin " - "Size is greater than one. " - "Not supported yet!" - << "\n"); - return false; - } - - const auto &It = State.begin(); - - if (It->getFirst().Offset != 0) { - LLVM_DEBUG( - dbgs() << "[AAAllocationInfo, manifest] AAPointerInfo Access not " - "starting at 0th byte in Alloca." - "Not supported yet!" - << "\n"); - return false; - } - - LLVM_DEBUG(dbgs() << "[AAAllocationInfo, manifest] The first offset is " - "the start of the Alloca." - << "\n"); - - int64_t OffsetEnd = It->getFirst().Offset + It->getFirst().Size; - const DataLayout &DL = AI.getModule()->getDataLayout(); - const auto &AllocationSize = AI.getAllocationSize(DL); - - if (!AllocationSize) { - LLVM_DEBUG( - dbgs() << "[AAAllocationInfo, manifest] Could not get allocation " - "size from Alloca." - << "\n"); - return false; - } - - if (OffsetEnd == AllocationSize) { - LLVM_DEBUG( - dbgs() << "[AAAllocationInfo, manifest] Offset size cannot be " - "reduced in Alloca." - << "\n"); - return false; - } - - if (getAllocatedSize() == AllocationSize) { - return false; - } - - // if the Bin Size is greater then current state then update state. - if (getAllocatedSize() < OffsetEnd) { - changeAllocationSize( - std::optional(TypeSize(OffsetEnd, true))); - } - - Type *IntegerSizeOfOffsetEnd = - Type::getIntNTy(AI.getContext(), OffsetEnd); - - AI.setAllocatedType(IntegerSizeOfOffsetEnd); - - LLVM_DEBUG( - dbgs() - << "[AAAllocationInfo, manifest] Successfully reduced allocation " - "size of alloca based on Bin." - << AI << "\n"); - - Changed = ChangeStatus::CHANGED; - return true; - }; + if (!isValidState()) { + return indicatePessimisticFixpoint(); + } auto *I = this->getIRPosition().getCtxI(); - if (!I) { - LLVM_DEBUG( - dbgs() << "[AAAllocationInfo, manifest] Instruction not valid.\n"); - return ChangeStatus::UNCHANGED; - } + AllocaInst &AI = cast(*I); - if (!MakeChange(*I)) { - return ChangeStatus::UNCHANGED; - } + Type *IntegerSizeOfAlloca = + Type::getIntNTy(AI.getContext(), getAllocatedSize()->getFixedValue()); - LLVM_DEBUG(dbgs() << "[AAAllocationInfo, manifest] return from " - "AAAllocation info manifest.\n"); - return Changed; + AI.setAllocatedType(IntegerSizeOfAlloca); + return ChangeStatus::CHANGED; } /// See AbstractAttribute::getAsStr(). diff --git a/llvm/test/Transforms/Attributor/allocator-start-range-only-negative-1.ll b/llvm/test/Transforms/Attributor/allocator-start-range-only-negative-1.ll deleted file mode 100644 --- a/llvm/test/Transforms/Attributor/allocator-start-range-only-negative-1.ll +++ /dev/null @@ -1,51 +0,0 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --version 2 -; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT -; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC - -%struct.Foo = type { i32, i8, ptr } - -; Function Attrs: noinline nounwind uwtable -define dso_local ptr @foo(i32 noundef %val) #0 { -; CHECK-LABEL: define dso_local ptr @foo -; CHECK-SAME: (i32 noundef [[VAL:%.*]]) { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[VAL_ADDR:%.*]] = alloca i32, align 4 -; CHECK-NEXT: [[F:%.*]] = alloca ptr, align 8 -; CHECK-NEXT: store i32 [[VAL]], ptr [[VAL_ADDR]], align 4 -; CHECK-NEXT: [[CALL:%.*]] = call noalias ptr @malloc(i64 noundef 16) -; CHECK-NEXT: store ptr [[CALL]], ptr [[F]], align 8 -; CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F]], align 8 -; CHECK-NEXT: store i32 2, ptr [[TMP0]], align 8 -; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 10, [[VAL]] -; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[F]], align 8 -; CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 8 -; CHECK-NEXT: [[ADD2:%.*]] = add nsw i32 [[TMP2]], [[ADD]] -; CHECK-NEXT: store i32 [[ADD2]], ptr [[TMP1]], align 8 -; CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[F]], align 8 -; CHECK-NEXT: ret ptr [[TMP3]] -; -entry: - %val.addr = alloca i32, align 4 - %f = alloca ptr, align 8 - store i32 %val, ptr %val.addr, align 4 - %call = call noalias ptr @malloc(i64 noundef 16) #2 - store ptr %call, ptr %f, align 8 - %0 = load ptr, ptr %f, align 8 - %field1 = getelementptr inbounds %struct.Foo, ptr %0, i32 0, i32 0 - store i32 2, ptr %field1, align 8 - %1 = load i32, ptr %val.addr, align 4 - %add = add nsw i32 10, %1 - %2 = load ptr, ptr %f, align 8 - %field11 = getelementptr inbounds %struct.Foo, ptr %2, i32 0, i32 0 - %3 = load i32, ptr %field11, align 8 - %add2 = add nsw i32 %3, %add - store i32 %add2, ptr %field11, align 8 - %4 = load ptr, ptr %f, align 8 - ret ptr %4 -} - -; Function Attrs: nounwind allocsize(0) -declare noalias ptr @malloc(i64 noundef) #1 -;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: -; CGSCC: {{.*}} -; TUNIT: {{.*}} diff --git a/llvm/test/Transforms/Attributor/allocator-start-range-only-negative-2.ll b/llvm/test/Transforms/Attributor/allocator-start-range-only-negative-2.ll deleted file mode 100644 --- a/llvm/test/Transforms/Attributor/allocator-start-range-only-negative-2.ll +++ /dev/null @@ -1,38 +0,0 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --version 2 -; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT -; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC - -%struct.Foo = type { i32, i8, ptr } - -; Function Attrs: noinline nounwind uwtable -define dso_local { i64, ptr } @foo(i32 noundef %val) #0 { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define dso_local { i64, ptr } @foo -; CHECK-SAME: (i32 noundef [[VAL:%.*]]) #[[ATTR0:[0-9]+]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 -; CHECK-NEXT: [[VAL_ADDR:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store i32 [[VAL]], ptr [[VAL_ADDR]], align 4 -; CHECK-NEXT: store i32 2, ptr [[RETVAL]], align 8 -; CHECK-NEXT: [[FIELD3:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[RETVAL]], i32 0, i32 2 -; CHECK-NEXT: store ptr [[VAL_ADDR]], ptr [[FIELD3]], align 8 -; CHECK-NEXT: [[TMP0:%.*]] = load { i64, ptr }, ptr [[RETVAL]], align 8 -; CHECK-NEXT: ret { i64, ptr } [[TMP0]] -; -entry: - %retval = alloca %struct.Foo, align 8 - %val.addr = alloca i32, align 4 - store i32 %val, ptr %val.addr, align 4 - %field1 = getelementptr inbounds %struct.Foo, ptr %retval, i32 0, i32 0 - store i32 2, ptr %field1, align 8 - %field3 = getelementptr inbounds %struct.Foo, ptr %retval, i32 0, i32 2 - store ptr %val.addr, ptr %field3, align 8 - %0 = load { i64, ptr }, ptr %retval, align 8 - ret { i64, ptr } %0 -} -;. -; CHECK: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } -;. -;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: -; CGSCC: {{.*}} -; TUNIT: {{.*}} diff --git a/llvm/test/Transforms/Attributor/allocator-start-range-only-positive-1.ll b/llvm/test/Transforms/Attributor/allocator-start-range-only-positive-1.ll deleted file mode 100644 --- a/llvm/test/Transforms/Attributor/allocator-start-range-only-positive-1.ll +++ /dev/null @@ -1,49 +0,0 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --version 2 -; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT -; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC - -%struct.Foo = type { i32, i32, i8 } - -@.str = private unnamed_addr constant [15 x i8] c"Field 1 is %d\0A\00", align 1 - -;. -; CHECK: @[[_STR:[a-zA-Z0-9_$"\\.-]+]] = private unnamed_addr constant [15 x i8] c"Field 1 is %d\0A\00", align 1 -;. -define dso_local void @foo(i32 noundef %val) #0 { -; CHECK-LABEL: define dso_local void @foo -; CHECK-SAME: (i32 noundef [[VAL:%.*]]) { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[VAL_ADDR:%.*]] = alloca i32, align 4 -; CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 -; CHECK-NEXT: store i32 [[VAL]], ptr [[VAL_ADDR]], align 4 -; CHECK-NEXT: store i32 10, ptr [[F]], align 4 -; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[F]], align 4 -; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], 1 -; CHECK-NEXT: store i32 [[ADD]], ptr [[F]], align 4 -; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[F]], align 4 -; CHECK-NEXT: [[ADD3:%.*]] = add nsw i32 [[TMP1]], [[VAL]] -; CHECK-NEXT: [[CALL:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(15) @.str, i32 noundef [[ADD3]]) -; CHECK-NEXT: ret void -; -entry: - %val.addr = alloca i32, align 4 - %f = alloca %struct.Foo, align 4 - store i32 %val, ptr %val.addr, align 4 - %field1 = getelementptr inbounds %struct.Foo, ptr %f, i32 0, i32 0 - store i32 10, ptr %field1, align 4 - %field11 = getelementptr inbounds %struct.Foo, ptr %f, i32 0, i32 0 - %0 = load i32, ptr %field11, align 4 - %add = add nsw i32 %0, 1 - store i32 %add, ptr %field11, align 4 - %field12 = getelementptr inbounds %struct.Foo, ptr %f, i32 0, i32 0 - %1 = load i32, ptr %field12, align 4 - %2 = load i32, ptr %val.addr, align 4 - %add3 = add nsw i32 %1, %2 - %call = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %add3) - ret void -} - -declare i32 @printf(ptr noundef, ...) #1 -;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: -; CGSCC: {{.*}} -; TUNIT: {{.*}} diff --git a/llvm/test/Transforms/Attributor/allocator.ll b/llvm/test/Transforms/Attributor/allocator.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/allocator.ll @@ -0,0 +1,122 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --version 2 +; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT +; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC + +%struct.Foo = type { i32, i32, i8 } + +@.str = private unnamed_addr constant [15 x i8] c"Field 1 is %d\0A\00", align 1 + +;. +; CHECK: @[[_STR:[a-zA-Z0-9_$"\\.-]+]] = private unnamed_addr constant [15 x i8] c"Field 1 is %d\0A\00", align 1 +;. +define dso_local void @foo_positive_test(i32 noundef %val) #0 { +; CHECK-LABEL: define dso_local void @foo_positive_test +; CHECK-SAME: (i32 noundef [[VAL:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[VAL_ADDR:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[F:%.*]] = alloca i32, align 4 +; CHECK-NEXT: store i32 [[VAL]], ptr [[VAL_ADDR]], align 4 +; CHECK-NEXT: store i32 10, ptr [[F]], align 4 +; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[F]], align 4 +; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], 1 +; CHECK-NEXT: store i32 [[ADD]], ptr [[F]], align 4 +; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[F]], align 4 +; CHECK-NEXT: [[ADD3:%.*]] = add nsw i32 [[TMP1]], [[VAL]] +; CHECK-NEXT: [[CALL:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(15) @.str, i32 noundef [[ADD3]]) +; CHECK-NEXT: ret void +; +entry: + %val.addr = alloca i64, align 4 + %f = alloca %struct.Foo, align 4 + store i32 %val, ptr %val.addr, align 4 + %field1 = getelementptr inbounds %struct.Foo, ptr %f, i32 0, i32 0 + store i32 10, ptr %field1, align 4 + %field11 = getelementptr inbounds %struct.Foo, ptr %f, i32 0, i32 0 + %0 = load i32, ptr %field11, align 4 + %add = add nsw i32 %0, 1 + store i32 %add, ptr %field11, align 4 + %field12 = getelementptr inbounds %struct.Foo, ptr %f, i32 0, i32 0 + %1 = load i32, ptr %field12, align 4 + %2 = load i32, ptr %val.addr, align 4 + %add3 = add nsw i32 %1, %2 + %call = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %add3) + ret void +} + +; Function Attrs: noinline nounwind uwtable +define dso_local ptr @foo_negative_test_escaping_pointer(i32 noundef %val) #0 { +; CHECK-LABEL: define dso_local ptr @foo_negative_test_escaping_pointer +; CHECK-SAME: (i32 noundef [[VAL:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[VAL_ADDR:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[F:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: store i32 [[VAL]], ptr [[VAL_ADDR]], align 4 +; CHECK-NEXT: [[CALL:%.*]] = call noalias ptr @malloc(i64 noundef 16) +; CHECK-NEXT: store ptr [[CALL]], ptr [[F]], align 8 +; CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F]], align 8 +; CHECK-NEXT: store i32 2, ptr [[TMP0]], align 8 +; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 10, [[VAL]] +; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[F]], align 8 +; CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 8 +; CHECK-NEXT: [[ADD2:%.*]] = add nsw i32 [[TMP2]], [[ADD]] +; CHECK-NEXT: store i32 [[ADD2]], ptr [[TMP1]], align 8 +; CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[F]], align 8 +; CHECK-NEXT: ret ptr [[TMP3]] +; +entry: + %val.addr = alloca i32, align 4 + %f = alloca ptr, align 8 + store i32 %val, ptr %val.addr, align 4 + %call = call noalias ptr @malloc(i64 noundef 16) #2 + store ptr %call, ptr %f, align 8 + %0 = load ptr, ptr %f, align 8 + %field1 = getelementptr inbounds %struct.Foo, ptr %0, i32 0, i32 0 + store i32 2, ptr %field1, align 8 + %1 = load i32, ptr %val.addr, align 4 + %add = add nsw i32 10, %1 + %2 = load ptr, ptr %f, align 8 + %field11 = getelementptr inbounds %struct.Foo, ptr %2, i32 0, i32 0 + %3 = load i32, ptr %field11, align 8 + %add2 = add nsw i32 %3, %add + store i32 %add2, ptr %field11, align 8 + %4 = load ptr, ptr %f, align 8 + ret ptr %4 +} + +; Function Attrs: noinline nounwind uwtable +define dso_local { i64, ptr } @foo_negative_test_not_a_single_start_offset(i32 noundef %val) #0 { +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +; CHECK-LABEL: define dso_local { i64, ptr } @foo_negative_test_not_a_single_start_offset +; CHECK-SAME: (i32 noundef [[VAL:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +; CHECK-NEXT: [[VAL_ADDR:%.*]] = alloca i32, align 4 +; CHECK-NEXT: store i32 [[VAL]], ptr [[VAL_ADDR]], align 4 +; CHECK-NEXT: store i32 2, ptr [[RETVAL]], align 8 +; CHECK-NEXT: [[FIELD3:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[RETVAL]], i32 0, i32 2 +; CHECK-NEXT: store ptr [[VAL_ADDR]], ptr [[FIELD3]], align 8 +; CHECK-NEXT: [[TMP0:%.*]] = load { i64, ptr }, ptr [[RETVAL]], align 8 +; CHECK-NEXT: ret { i64, ptr } [[TMP0]] +; +entry: + %retval = alloca %struct.Foo, align 8 + %val.addr = alloca i32, align 4 + store i32 %val, ptr %val.addr, align 4 + %field1 = getelementptr inbounds %struct.Foo, ptr %retval, i32 0, i32 0 + store i32 2, ptr %field1, align 8 + %field3 = getelementptr inbounds %struct.Foo, ptr %retval, i32 0, i32 2 + store ptr %val.addr, ptr %field3, align 8 + %0 = load { i64, ptr }, ptr %retval, align 8 + ret { i64, ptr } %0 +} + +declare i32 @printf(ptr noundef, ...) #1 + +; Function Attrs: nounwind allocsize(0) +declare noalias ptr @malloc(i64 noundef) #1 +;. +; CHECK: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } +;. +;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +; CGSCC: {{.*}} +; TUNIT: {{.*}}