diff --git a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp --- a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp +++ b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp @@ -195,9 +195,8 @@ // Add a parameter to the function for each element passed in. for (const auto &ArgIndex : ArgIndices) { // not allowed to dereference ->begin() if size() is 0 - Params.push_back(GetElementPtrInst::getIndexedType( - cast(I->getType())->getElementType(), - ArgIndex.second)); + Params.push_back( + GetElementPtrInst::getIndexedType(ArgIndex.first, ArgIndex.second)); ArgAttrVec.push_back(AttributeSet()); assert(Params.back()); } @@ -297,8 +296,8 @@ : Type::getInt64Ty(F->getContext())); Ops.push_back(ConstantInt::get(IdxTy, II)); // Keep track of the type we're currently indexing. - if (auto *ElPTy = dyn_cast(ElTy)) - ElTy = ElPTy->getElementType(); + if (isa(ElTy)) + ElTy = ArgIndex.first; else ElTy = GetElementPtrInst::getTypeAtIndex(ElTy, II); } @@ -559,8 +558,9 @@ /// This method limits promotion of aggregates to only promote up to three /// elements of the aggregate in order to avoid exploding the number of /// arguments passed in. -static bool isSafeToPromoteArgument(Argument *Arg, Type *ByValTy, AAResults &AAR, - unsigned MaxElements) { +static bool isSafeToPromoteArgument(Argument *Arg, Type *ByValTy, + AAResults &AAR, unsigned MaxElements, + bool isSelfRecursive) { using GEPIndicesSet = std::set; // Quick exit for unused arguments @@ -725,6 +725,18 @@ if (Loads.empty()) return true; // No users, this is a dead argument. + // If we're in a recursive function, and the argument is a recursive type + // where we load another pointer, we could end up infinitely peeling the + // function argument. With opaque pointers, we can't actually tell if that + // load is the exact same argument type, so this is a bit pessimistic. + if (isSelfRecursive) { + for (LoadInst *Load : Loads) { + if (Load->getType()->isPointerTy()) { + return false; + } + } + } + // Okay, now we know that the argument is only used by load instructions and // it is safe to unconditionally perform all of them. Use alias analysis to // check to see if the pointer is guaranteed to not be modified from entry of @@ -922,8 +934,6 @@ SmallPtrSet ArgsToPromote; SmallPtrSet ByValArgsToTransform; for (Argument *PtrArg : PointerArgs) { - Type *AgTy = cast(PtrArg->getType())->getElementType(); - // Replace sret attribute with noalias. This reduces register pressure by // avoiding a register copy. if (PtrArg->hasStructRetAttr()) { @@ -943,11 +953,14 @@ // // Only handle arguments with specified alignment; if it's unspecified, the // actual alignment of the argument is target-specific. - bool isSafeToPromote = PtrArg->hasByValAttr() && PtrArg->getParamAlign() && - (ArgumentPromotionPass::isDenselyPacked(AgTy, DL) || - !canPaddingBeAccessed(PtrArg)); + Type *ByValTy = + PtrArg->hasByValAttr() ? PtrArg->getParamByValType() : nullptr; + bool isSafeToPromote = + ByValTy && PtrArg->getParamAlign() && + (ArgumentPromotionPass::isDenselyPacked(ByValTy, DL) || + !canPaddingBeAccessed(PtrArg)); if (isSafeToPromote) { - if (StructType *STy = dyn_cast(AgTy)) { + if (StructType *STy = dyn_cast(ByValTy)) { if (MaxElements > 0 && STy->getNumElements() > MaxElements) { LLVM_DEBUG(dbgs() << "argpromotion disable promoting argument '" << PtrArg->getName() @@ -976,21 +989,9 @@ } } - // If the argument is a recursive type and we're in a recursive - // function, we could end up infinitely peeling the function argument. - if (isSelfRecursive) { - if (StructType *STy = dyn_cast(AgTy)) { - bool RecursiveType = - llvm::is_contained(STy->elements(), PtrArg->getType()); - if (RecursiveType) - continue; - } - } - // Otherwise, see if we can promote the pointer to its value. - Type *ByValTy = - PtrArg->hasByValAttr() ? PtrArg->getParamByValType() : nullptr; - if (isSafeToPromoteArgument(PtrArg, ByValTy, AAR, MaxElements)) + if (isSafeToPromoteArgument(PtrArg, ByValTy, AAR, MaxElements, + isSelfRecursive)) ArgsToPromote.insert(PtrArg); } diff --git a/llvm/test/Transforms/ArgumentPromotion/aggregate-promote-dead-gep.ll b/llvm/test/Transforms/ArgumentPromotion/aggregate-promote-dead-gep.ll --- a/llvm/test/Transforms/ArgumentPromotion/aggregate-promote-dead-gep.ll +++ b/llvm/test/Transforms/ArgumentPromotion/aggregate-promote-dead-gep.ll @@ -1,9 +1,16 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes ; RUN: opt < %s -passes=argpromotion -S | FileCheck %s +; RUN: opt < %s -passes=argpromotion -opaque-pointers -S | FileCheck %s %T = type { i32, i32, i32, i32 } @G = constant %T { i32 0, i32 0, i32 17, i32 25 } +; Define variables for typed or opaque pointers +; CHECK-LABEL: %pointers = type +; CHECK-SAME: { [[I32_P:.*]], [[T_P:.*]] } +%pointers = type { i32*, %T* } +@pointers = global %pointers zeroinitializer + define internal i32 @test(%T* %p) { ; CHECK-LABEL: define {{[^@]+}}@test ; CHECK-SAME: (i32 [[P_0_3_VAL:%.*]]) { @@ -22,8 +29,8 @@ define i32 @caller() { ; CHECK-LABEL: define {{[^@]+}}@caller() { ; CHECK-NEXT: entry: -; CHECK-NEXT: [[G_IDX:%.*]] = getelementptr [[T:%.*]], %T* @G, i64 0, i32 3 -; CHECK-NEXT: [[G_IDX_VAL:%.*]] = load i32, i32* [[G_IDX]], align 4 +; CHECK-NEXT: [[G_IDX:%.*]] = getelementptr [[T:%.*]], [[T_P]] @G, i64 0, i32 3 +; CHECK-NEXT: [[G_IDX_VAL:%.*]] = load i32, [[I32_P]] [[G_IDX]], align 4 ; CHECK-NEXT: [[V:%.*]] = call i32 @test(i32 [[G_IDX_VAL]]) ; CHECK-NEXT: ret i32 [[V]] ; diff --git a/llvm/test/Transforms/ArgumentPromotion/aggregate-promote.ll b/llvm/test/Transforms/ArgumentPromotion/aggregate-promote.ll --- a/llvm/test/Transforms/ArgumentPromotion/aggregate-promote.ll +++ b/llvm/test/Transforms/ArgumentPromotion/aggregate-promote.ll @@ -1,9 +1,16 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes ; RUN: opt < %s -passes=argpromotion -S | FileCheck %s +; RUN: opt < %s -passes=argpromotion -opaque-pointers -S | FileCheck %s %T = type { i32, i32, i32, i32 } @G = constant %T { i32 0, i32 0, i32 17, i32 25 } +; Define variables for typed or opaque pointers +; CHECK-LABEL: %pointers = type +; CHECK-SAME: { [[I32_P:.*]], [[T_P:.*]] } +%pointers = type { i32*, %T* } +@pointers = global %pointers zeroinitializer + define internal i32 @test(%T* %p) { ; CHECK-LABEL: define {{[^@]+}}@test ; CHECK-SAME: (i32 [[P_0_2_VAL:%.*]], i32 [[P_0_3_VAL:%.*]]) @@ -23,10 +30,10 @@ define i32 @caller() { ; CHECK-LABEL: define {{[^@]+}}@caller() ; CHECK-NEXT: entry: -; CHECK-NEXT: [[G_IDX:%.*]] = getelementptr [[T:%.*]], %T* @G, i64 0, i32 2 -; CHECK-NEXT: [[G_IDX_VAL:%.*]] = load i32, i32* [[G_IDX]] -; CHECK-NEXT: [[G_IDX1:%.*]] = getelementptr [[T]], %T* @G, i64 0, i32 3 -; CHECK-NEXT: [[G_IDX1_VAL:%.*]] = load i32, i32* [[G_IDX1]] +; CHECK-NEXT: [[G_IDX:%.*]] = getelementptr [[T:%.*]], [[T_P]] @G, i64 0, i32 2 +; CHECK-NEXT: [[G_IDX_VAL:%.*]] = load i32, [[I32_P]] [[G_IDX]] +; CHECK-NEXT: [[G_IDX1:%.*]] = getelementptr [[T]], [[T_P]] @G, i64 0, i32 3 +; CHECK-NEXT: [[G_IDX1_VAL:%.*]] = load i32, [[I32_P]] [[G_IDX1]] ; CHECK-NEXT: [[V:%.*]] = call i32 @test(i32 [[G_IDX_VAL]], i32 [[G_IDX1_VAL]]) ; CHECK-NEXT: ret i32 [[V]] ; diff --git a/llvm/test/Transforms/ArgumentPromotion/attrs.ll b/llvm/test/Transforms/ArgumentPromotion/attrs.ll --- a/llvm/test/Transforms/ArgumentPromotion/attrs.ll +++ b/llvm/test/Transforms/ArgumentPromotion/attrs.ll @@ -1,23 +1,30 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes ; RUN: opt < %s -passes=argpromotion -S | FileCheck %s +; RUN: opt < %s -passes=argpromotion -opaque-pointers -S | FileCheck %s %struct.ss = type { i32, i64 } +; Define variables for typed or opaque pointers +; CHECK-LABEL: %pointers = type +; CHECK-SAME: { [[I32_P:.*]], [[I64_P:.*]], [[STRUCT_SS_P:.*]] } +%pointers = type { i32*, i64*, %struct.ss* } +@pointers = global %pointers zeroinitializer + ; Don't drop 'byval' on %X here. define internal void @f(%struct.ss* byval(%struct.ss) align 4 %b, i32* byval(i32) align 4 %X, i32 %i) nounwind { ; CHECK-LABEL: define {{[^@]+}}@f -; CHECK-SAME: (i32 [[B_0:%.*]], i64 [[B_1:%.*]], i32* byval(i32) align 4 [[X:%.*]], i32 [[I:%.*]]) [[ATTR0:#.*]] { +; CHECK-SAME: (i32 [[B_0:%.*]], i64 [[B_1:%.*]], [[I32_P]] byval(i32) align 4 [[X:%.*]], i32 [[I:%.*]]) [[ATTR0:#.*]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[B:%.*]] = alloca [[STRUCT_SS:%.*]], align 4 -; CHECK-NEXT: [[DOT0:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[B]], i32 0, i32 0 -; CHECK-NEXT: store i32 [[B_0]], i32* [[DOT0]], align 4 -; CHECK-NEXT: [[DOT1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[B]], i32 0, i32 1 -; CHECK-NEXT: store i64 [[B_1]], i64* [[DOT1]], align 4 -; CHECK-NEXT: [[TMP:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[B]], i32 0, i32 0 -; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[TMP]], align 4 +; CHECK-NEXT: [[DOT0:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[B]], i32 0, i32 0 +; CHECK-NEXT: store i32 [[B_0]], [[I32_P]] [[DOT0]], align 4 +; CHECK-NEXT: [[DOT1:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[B]], i32 0, i32 1 +; CHECK-NEXT: store i64 [[B_1]], [[I64_P]] [[DOT1]], align 4 +; CHECK-NEXT: [[TMP:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[B]], i32 0, i32 0 +; CHECK-NEXT: [[TMP1:%.*]] = load i32, [[I32_P]] [[TMP]], align 4 ; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[TMP1]], 1 -; CHECK-NEXT: store i32 [[TMP2]], i32* [[TMP]], align 4 -; CHECK-NEXT: store i32 0, i32* [[X]], align 4 +; CHECK-NEXT: store i32 [[TMP2]], [[I32_P]] [[TMP]], align 4 +; CHECK-NEXT: store i32 0, [[I32_P]] [[X]], align 4 ; CHECK-NEXT: ret void ; entry: @@ -34,18 +41,18 @@ ; Also make sure we don't drop the call zeroext attribute. define i32 @test(i32* %X) { ; CHECK-LABEL: define {{[^@]+}}@test -; CHECK-SAME: (i32* [[X:%.*]]) { +; CHECK-SAME: ([[I32_P]] [[X:%.*]]) { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SS:%.*]], align 8 -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 0 -; CHECK-NEXT: store i32 1, i32* [[TMP1]], align 8 -; CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1 -; CHECK-NEXT: store i64 2, i64* [[TMP4]], align 4 -; CHECK-NEXT: [[S_0:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 0 -; CHECK-NEXT: [[S_0_VAL:%.*]] = load i32, i32* [[S_0]], align 4 -; CHECK-NEXT: [[S_1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1 -; CHECK-NEXT: [[S_1_VAL:%.*]] = load i64, i64* [[S_1]], align 4 -; CHECK-NEXT: call void @f(i32 [[S_0_VAL]], i64 [[S_1_VAL]], i32* byval(i32) align 4 [[X]], i32 zeroext 0) +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[S]], i32 0, i32 0 +; CHECK-NEXT: store i32 1, [[I32_P]] [[TMP1]], align 8 +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[S]], i32 0, i32 1 +; CHECK-NEXT: store i64 2, [[I64_P]] [[TMP4]], align 4 +; CHECK-NEXT: [[S_0:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[S]], i32 0, i32 0 +; CHECK-NEXT: [[S_0_VAL:%.*]] = load i32, [[I32_P]] [[S_0]], align 4 +; CHECK-NEXT: [[S_1:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[S]], i32 0, i32 1 +; CHECK-NEXT: [[S_1_VAL:%.*]] = load i64, [[I64_P]] [[S_1]], align 4 +; CHECK-NEXT: call void @f(i32 [[S_0_VAL]], i64 [[S_1_VAL]], [[I32_P]] byval(i32) align 4 [[X]], i32 zeroext 0) ; CHECK-NEXT: ret i32 0 ; entry: diff --git a/llvm/test/Transforms/ArgumentPromotion/basictest.ll b/llvm/test/Transforms/ArgumentPromotion/basictest.ll --- a/llvm/test/Transforms/ArgumentPromotion/basictest.ll +++ b/llvm/test/Transforms/ArgumentPromotion/basictest.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes ; RUN: opt < %s -basic-aa -argpromotion -mem2reg -S | FileCheck %s +; RUN: opt < %s -basic-aa -argpromotion -mem2reg -opaque-pointers -S | FileCheck %s target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" define internal i32 @test(i32* %X, i32* %Y) { diff --git a/llvm/test/Transforms/ArgumentPromotion/byval-2.ll b/llvm/test/Transforms/ArgumentPromotion/byval-2.ll --- a/llvm/test/Transforms/ArgumentPromotion/byval-2.ll +++ b/llvm/test/Transforms/ArgumentPromotion/byval-2.ll @@ -1,25 +1,32 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes ; RUN: opt < %s -passes=argpromotion -S | FileCheck %s +; RUN: opt < %s -passes=argpromotion -opaque-pointers -S | FileCheck %s ; Arg promotion eliminates the struct argument. ; FIXME: We should eliminate the i32* argument. %struct.ss = type { i32, i64 } +; Define variables for typed or opaque pointers +; CHECK-LABEL: %pointers = type +; CHECK-SAME: { [[I32_P:.*]], [[I64_P:.*]], [[STRUCT_SS_P:.*]] } +%pointers = type { i32*, i64*, %struct.ss* } +@pointers = global %pointers zeroinitializer + define internal void @f(%struct.ss* byval(%struct.ss) align 8 %b, i32* byval(i32) align 4 %X) nounwind { ; CHECK-LABEL: define {{[^@]+}}@f -; CHECK-SAME: (i32 [[B_0:%.*]], i64 [[B_1:%.*]], i32* byval(i32) align 4 [[X:%.*]]) [[ATTR0:#.*]] { +; CHECK-SAME: (i32 [[B_0:%.*]], i64 [[B_1:%.*]], [[I32_P]] byval(i32) align 4 [[X:%.*]]) [[ATTR0:#.*]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[B:%.*]] = alloca [[STRUCT_SS:%.*]], align 8 -; CHECK-NEXT: [[DOT0:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[B]], i32 0, i32 0 -; CHECK-NEXT: store i32 [[B_0]], i32* [[DOT0]], align 8 -; CHECK-NEXT: [[DOT1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[B]], i32 0, i32 1 -; CHECK-NEXT: store i64 [[B_1]], i64* [[DOT1]], align 4 -; CHECK-NEXT: [[TMP:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[B]], i32 0, i32 0 -; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[TMP]], align 4 +; CHECK-NEXT: [[DOT0:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[B]], i32 0, i32 0 +; CHECK-NEXT: store i32 [[B_0]], [[I32_P]] [[DOT0]], align 8 +; CHECK-NEXT: [[DOT1:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[B]], i32 0, i32 1 +; CHECK-NEXT: store i64 [[B_1]], [[I64_P]] [[DOT1]], align 4 +; CHECK-NEXT: [[TMP:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[B]], i32 0, i32 0 +; CHECK-NEXT: [[TMP1:%.*]] = load i32, [[I32_P]] [[TMP]], align 4 ; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[TMP1]], 1 -; CHECK-NEXT: store i32 [[TMP2]], i32* [[TMP]], align 4 -; CHECK-NEXT: store i32 0, i32* [[X]], align 4 +; CHECK-NEXT: store i32 [[TMP2]], [[I32_P]] [[TMP]], align 4 +; CHECK-NEXT: store i32 0, [[I32_P]] [[X]], align 4 ; CHECK-NEXT: ret void ; entry: @@ -34,18 +41,18 @@ define i32 @test(i32* %X) { ; CHECK-LABEL: define {{[^@]+}}@test -; CHECK-SAME: (i32* [[X:%.*]]) { +; CHECK-SAME: ([[I32_P]] [[X:%.*]]) { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SS:%.*]], align 8 -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 0 -; CHECK-NEXT: store i32 1, i32* [[TMP1]], align 8 -; CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1 -; CHECK-NEXT: store i64 2, i64* [[TMP4]], align 4 -; CHECK-NEXT: [[S_0:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 0 -; CHECK-NEXT: [[S_0_VAL:%.*]] = load i32, i32* [[S_0]], align 8 -; CHECK-NEXT: [[S_1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1 -; CHECK-NEXT: [[S_1_VAL:%.*]] = load i64, i64* [[S_1]], align 4 -; CHECK-NEXT: call void @f(i32 [[S_0_VAL]], i64 [[S_1_VAL]], i32* byval(i32) align 4 [[X]]) +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[S]], i32 0, i32 0 +; CHECK-NEXT: store i32 1, [[I32_P]] [[TMP1]], align 8 +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[S]], i32 0, i32 1 +; CHECK-NEXT: store i64 2, [[I64_P]] [[TMP4]], align 4 +; CHECK-NEXT: [[S_0:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[S]], i32 0, i32 0 +; CHECK-NEXT: [[S_0_VAL:%.*]] = load i32, [[I32_P]] [[S_0]], align 8 +; CHECK-NEXT: [[S_1:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[S]], i32 0, i32 1 +; CHECK-NEXT: [[S_1_VAL:%.*]] = load i64, [[I64_P]] [[S_1]], align 4 +; CHECK-NEXT: call void @f(i32 [[S_0_VAL]], i64 [[S_1_VAL]], [[I32_P]] byval(i32) align 4 [[X]]) ; CHECK-NEXT: ret i32 0 ; entry: diff --git a/llvm/test/Transforms/ArgumentPromotion/byval.ll b/llvm/test/Transforms/ArgumentPromotion/byval.ll --- a/llvm/test/Transforms/ArgumentPromotion/byval.ll +++ b/llvm/test/Transforms/ArgumentPromotion/byval.ll @@ -1,23 +1,30 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes ; RUN: opt < %s -passes=argpromotion -S | FileCheck %s +; RUN: opt < %s -passes=argpromotion -opaque-pointers -S | FileCheck %s target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" %struct.ss = type { i32, i64 } +; Define variables for typed or opaque pointers +; CHECK-LABEL: %pointers = type +; CHECK-SAME: { [[I32_P:.*]], [[I64_P:.*]], [[STRUCT_SS_P:.*]] } +%pointers = type { i32*, i64*, %struct.ss* } +@pointers = global %pointers zeroinitializer + define internal void @f(%struct.ss* byval(%struct.ss) align 4 %b) nounwind { ; CHECK-LABEL: define {{[^@]+}}@f ; CHECK-SAME: (i32 [[B_0:%.*]], i64 [[B_1:%.*]]) [[ATTR0:#.*]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[B:%.*]] = alloca [[STRUCT_SS:%.*]], align 4 -; CHECK-NEXT: [[DOT0:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[B]], i32 0, i32 0 -; CHECK-NEXT: store i32 [[B_0]], i32* [[DOT0]], align 4 -; CHECK-NEXT: [[DOT1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[B]], i32 0, i32 1 -; CHECK-NEXT: store i64 [[B_1]], i64* [[DOT1]], align 4 -; CHECK-NEXT: [[TMP:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[B]], i32 0, i32 0 -; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[TMP]], align 4 +; CHECK-NEXT: [[DOT0:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[B]], i32 0, i32 0 +; CHECK-NEXT: store i32 [[B_0]], [[I32_P]] [[DOT0]], align 4 +; CHECK-NEXT: [[DOT1:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[B]], i32 0, i32 1 +; CHECK-NEXT: store i64 [[B_1]], [[I64_P]] [[DOT1]], align 4 +; CHECK-NEXT: [[TMP:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[B]], i32 0, i32 0 +; CHECK-NEXT: [[TMP1:%.*]] = load i32, [[I32_P]] [[TMP]], align 4 ; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[TMP1]], 1 -; CHECK-NEXT: store i32 [[TMP2]], i32* [[TMP]], align 4 +; CHECK-NEXT: store i32 [[TMP2]], [[I32_P]] [[TMP]], align 4 ; CHECK-NEXT: ret void ; entry: @@ -34,14 +41,14 @@ ; CHECK-SAME: (i32 [[B_0:%.*]], i64 [[B_1:%.*]]) [[ATTR0]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[B:%.*]] = alloca [[STRUCT_SS:%.*]], align 32 -; CHECK-NEXT: [[DOT0:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[B]], i32 0, i32 0 -; CHECK-NEXT: store i32 [[B_0]], i32* [[DOT0]], align 32 -; CHECK-NEXT: [[DOT1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[B]], i32 0, i32 1 -; CHECK-NEXT: store i64 [[B_1]], i64* [[DOT1]], align 4 -; CHECK-NEXT: [[TMP:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[B]], i32 0, i32 0 -; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[TMP]], align 4 +; CHECK-NEXT: [[DOT0:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[B]], i32 0, i32 0 +; CHECK-NEXT: store i32 [[B_0]], [[I32_P]] [[DOT0]], align 32 +; CHECK-NEXT: [[DOT1:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[B]], i32 0, i32 1 +; CHECK-NEXT: store i64 [[B_1]], [[I64_P]] [[DOT1]], align 4 +; CHECK-NEXT: [[TMP:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[B]], i32 0, i32 0 +; CHECK-NEXT: [[TMP1:%.*]] = load i32, [[I32_P]] [[TMP]], align 4 ; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[TMP1]], 1 -; CHECK-NEXT: store i32 [[TMP2]], i32* [[TMP]], align 4 +; CHECK-NEXT: store i32 [[TMP2]], [[I32_P]] [[TMP]], align 4 ; CHECK-NEXT: ret void ; entry: @@ -59,7 +66,7 @@ ; just delete this test.) define internal void @h(%struct.ss* byval(%struct.ss) %b) nounwind { ; CHECK-LABEL: define {{[^@]+}}@h -; CHECK-SAME: (%struct.ss* byval(%struct.ss) %b) +; CHECK-SAME: ([[STRUCT_SS_P]] byval(%struct.ss) %b) ; entry: %tmp = getelementptr %struct.ss, %struct.ss* %b, i32 0, i32 0 @@ -74,21 +81,21 @@ ; CHECK-SAME: () [[ATTR0]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SS:%.*]], align 32 -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 0 -; CHECK-NEXT: store i32 1, i32* [[TMP1]], align 8 -; CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1 -; CHECK-NEXT: store i64 2, i64* [[TMP4]], align 4 -; CHECK-NEXT: [[S_0:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 0 -; CHECK-NEXT: [[S_0_VAL:%.*]] = load i32, i32* [[S_0]], align 4 -; CHECK-NEXT: [[S_1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1 -; CHECK-NEXT: [[S_1_VAL:%.*]] = load i64, i64* [[S_1]], align 4 +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[S]], i32 0, i32 0 +; CHECK-NEXT: store i32 1, [[I32_P]] [[TMP1]], align 8 +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[S]], i32 0, i32 1 +; CHECK-NEXT: store i64 2, [[I64_P]] [[TMP4]], align 4 +; CHECK-NEXT: [[S_0:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[S]], i32 0, i32 0 +; CHECK-NEXT: [[S_0_VAL:%.*]] = load i32, [[I32_P]] [[S_0]], align 4 +; CHECK-NEXT: [[S_1:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[S]], i32 0, i32 1 +; CHECK-NEXT: [[S_1_VAL:%.*]] = load i64, [[I64_P]] [[S_1]], align 4 ; CHECK-NEXT: call void @f(i32 [[S_0_VAL]], i64 [[S_1_VAL]]) -; CHECK-NEXT: [[S_01:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 0 -; CHECK-NEXT: [[S_01_VAL:%.*]] = load i32, i32* [[S_01]], align 32 -; CHECK-NEXT: [[S_12:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1 -; CHECK-NEXT: [[S_12_VAL:%.*]] = load i64, i64* [[S_12]], align 4 +; CHECK-NEXT: [[S_01:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[S]], i32 0, i32 0 +; CHECK-NEXT: [[S_01_VAL:%.*]] = load i32, [[I32_P]] [[S_01]], align 32 +; CHECK-NEXT: [[S_12:%.*]] = getelementptr [[STRUCT_SS]], [[STRUCT_SS_P]] [[S]], i32 0, i32 1 +; CHECK-NEXT: [[S_12_VAL:%.*]] = load i64, [[I64_P]] [[S_12]], align 4 ; CHECK-NEXT: call void @g(i32 [[S_01_VAL]], i64 [[S_12_VAL]]) -; CHECK-NEXT: call void @h(%struct.ss* byval(%struct.ss) %S) +; CHECK-NEXT: call void @h([[STRUCT_SS_P]] byval(%struct.ss) %S) ; CHECK-NEXT: ret i32 0 ; entry: diff --git a/llvm/test/Transforms/ArgumentPromotion/chained.ll b/llvm/test/Transforms/ArgumentPromotion/chained.ll --- a/llvm/test/Transforms/ArgumentPromotion/chained.ll +++ b/llvm/test/Transforms/ArgumentPromotion/chained.ll @@ -1,9 +1,16 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes ; RUN: opt < %s -passes=argpromotion -S | FileCheck %s +; RUN: opt < %s -passes=argpromotion -opaque-pointers -S | FileCheck %s @G1 = constant i32 0 @G2 = constant i32* @G1 +; Define variables for typed or opaque pointers +; CHECK-LABEL: %pointers = type +; CHECK-SAME: { [[I32_P:.*]], [[I32_PP:.*]] } +%pointers = type { i32*, i32** } +@pointers = global %pointers zeroinitializer + define internal i32 @test(i32** %x) { ; CHECK-LABEL: define {{[^@]+}}@test ; CHECK-SAME: (i32 [[X_VAL_VAL:%.*]]) @@ -19,8 +26,8 @@ define i32 @caller() { ; CHECK-LABEL: define {{[^@]+}}@caller() ; CHECK-NEXT: entry: -; CHECK-NEXT: [[G2_VAL:%.*]] = load i32*, i32** @G2 -; CHECK-NEXT: [[G2_VAL_VAL:%.*]] = load i32, i32* [[G2_VAL]] +; CHECK-NEXT: [[G2_VAL:%.*]] = load [[I32_P]], [[I32_PP]] @G2 +; CHECK-NEXT: [[G2_VAL_VAL:%.*]] = load i32, [[I32_P]] [[G2_VAL]] ; CHECK-NEXT: [[X:%.*]] = call i32 @test(i32 [[G2_VAL_VAL]]) ; CHECK-NEXT: ret i32 [[X]] ; diff --git a/llvm/test/Transforms/ArgumentPromotion/control-flow.ll b/llvm/test/Transforms/ArgumentPromotion/control-flow.ll --- a/llvm/test/Transforms/ArgumentPromotion/control-flow.ll +++ b/llvm/test/Transforms/ArgumentPromotion/control-flow.ll @@ -1,16 +1,23 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes ; RUN: opt < %s -passes=argpromotion -S | FileCheck %s +; RUN: opt < %s -passes=argpromotion -opaque-pointers -S | FileCheck %s + +; Define variables for typed or opaque pointers +; CHECK-LABEL: %pointers = type +; CHECK-SAME: { [[I32_P:.*]] } +%pointers = type { i32* } +@pointers = global %pointers zeroinitializer ; Don't promote around control flow. define internal i32 @callee(i1 %C, i32* %P) { ; CHECK-LABEL: define {{[^@]+}}@callee -; CHECK-SAME: (i1 [[C:%.*]], i32* [[P:%.*]]) +; CHECK-SAME: (i1 [[C:%.*]], [[I32_P]] [[P:%.*]]) ; CHECK-NEXT: entry: ; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] ; CHECK: T: ; CHECK-NEXT: ret i32 17 ; CHECK: F: -; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P]] +; CHECK-NEXT: [[X:%.*]] = load i32, [[I32_P]] [[P]] ; CHECK-NEXT: ret i32 [[X]] ; entry: @@ -27,7 +34,7 @@ define i32 @foo() { ; CHECK-LABEL: define {{[^@]+}}@foo() ; CHECK-NEXT: entry: -; CHECK-NEXT: [[X:%.*]] = call i32 @callee(i1 true, i32* null) +; CHECK-NEXT: [[X:%.*]] = call i32 @callee(i1 true, [[I32_P]] null) ; CHECK-NEXT: ret i32 [[X]] ; entry: diff --git a/llvm/test/Transforms/ArgumentPromotion/control-flow2.ll b/llvm/test/Transforms/ArgumentPromotion/control-flow2.ll --- a/llvm/test/Transforms/ArgumentPromotion/control-flow2.ll +++ b/llvm/test/Transforms/ArgumentPromotion/control-flow2.ll @@ -1,8 +1,15 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes ; RUN: opt < %s -passes=argpromotion -S | FileCheck %s +; RUN: opt < %s -passes=argpromotion -opaque-pointers -S | FileCheck %s target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" +; Define variables for typed or opaque pointers +; CHECK-LABEL: %pointers = type +; CHECK-SAME: { [[I32_P:.*]] } +%pointers = type { i32* } +@pointers = global %pointers zeroinitializer + define internal i32 @callee(i1 %C, i32* %P) { ; CHECK-LABEL: define {{[^@]+}}@callee ; CHECK-SAME: (i1 [[C:%.*]], i32 [[P_VAL:%.*]]) @@ -25,8 +32,8 @@ define i32 @foo() { ; CHECK-LABEL: define {{[^@]+}}@foo() ; CHECK-NEXT: [[A:%.*]] = alloca i32 -; CHECK-NEXT: store i32 17, i32* [[A]] -; CHECK-NEXT: [[A_VAL:%.*]] = load i32, i32* [[A]] +; CHECK-NEXT: store i32 17, [[I32_P]] [[A]] +; CHECK-NEXT: [[A_VAL:%.*]] = load i32, [[I32_P]] [[A]] ; CHECK-NEXT: [[X:%.*]] = call i32 @callee(i1 false, i32 [[A_VAL]]) ; CHECK-NEXT: ret i32 [[X]] ; diff --git a/llvm/test/Transforms/ArgumentPromotion/crash.ll b/llvm/test/Transforms/ArgumentPromotion/crash.ll --- a/llvm/test/Transforms/ArgumentPromotion/crash.ll +++ b/llvm/test/Transforms/ArgumentPromotion/crash.ll @@ -1,13 +1,21 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes ; RUN: opt -S < %s -inline -argpromotion | FileCheck %s --check-prefix=ARGPROMOTION +; RUN: opt -S < %s -inline -argpromotion -opaque-pointers | FileCheck %s --check-prefix=ARGPROMOTION ; RUN: opt -S < %s -passes=inline,argpromotion | FileCheck %s --check-prefixes=ARGPROMOTION,ALL_NEWPM +; RUN: opt -S < %s -passes=inline,argpromotion -opaque-pointers | FileCheck %s --check-prefixes=ARGPROMOTION,ALL_NEWPM %S = type { %S* } +; Define variables for typed or opaque pointers +; ARGPROMOTION-LABEL: %pointers = type +; ARGPROMOTION-SAME: { [[I8_P:.*]], [[S_P:.*]], [[S_PP:.*]] } +%pointers = type { i8*, %S*, %S** } +@pointers = global %pointers zeroinitializer + ; Inlining should nuke the invoke (and any inlined calls) here even with ; argument promotion running along with it. define void @zot() personality i32 (...)* @wibble { -; ARGPROMOTION-LABEL: define {{[^@]+}}@zot() personality i32 (...)* @wibble +; ARGPROMOTION-LABEL: define {{[^@]+}}@zot() personality {{.*}} @wibble ; ARGPROMOTION-NEXT: bb: ; ARGPROMOTION-NEXT: unreachable ; ARGPROMOTION: hoge.exit: @@ -15,7 +23,7 @@ ; ARGPROMOTION: bb1: ; ARGPROMOTION-NEXT: unreachable ; ARGPROMOTION: bb2: -; ARGPROMOTION-NEXT: [[TMP:%.*]] = landingpad { i8*, i32 } +; ARGPROMOTION-NEXT: [[TMP:%.*]] = landingpad { [[I8_P]], i32 } ; ARGPROMOTION-NEXT: cleanup ; ARGPROMOTION-NEXT: unreachable ; @@ -65,7 +73,7 @@ ; ARGPROMOTION-NEXT: bb: ; ARGPROMOTION-NEXT: [[TMP:%.*]] = alloca [[S:%.*]] ; ARGPROMOTION-NEXT: [[TMP1:%.*]] = alloca [[S]] -; ARGPROMOTION-NEXT: [[TMP2:%.*]] = call i32 @test_inf_promote_callee(%S* [[TMP]], %S* [[TMP1]]) +; ARGPROMOTION-NEXT: [[TMP2:%.*]] = call i32 @test_inf_promote_callee([[S_P]] [[TMP]], [[S_P]] [[TMP1]]) ; ARGPROMOTION-NEXT: ret i32 0 ; bb: @@ -78,13 +86,14 @@ define internal i32 @test_inf_promote_callee(%S* %arg, %S* %arg1) { ; ARGPROMOTION-LABEL: define {{[^@]+}}@test_inf_promote_callee -; ARGPROMOTION-SAME: (%S* [[ARG:%.*]], %S* [[ARG1:%.*]]) +; ARGPROMOTION-SAME: ([[S_P]] [[ARG:%.*]], +; ARGPROMOTION-SAME: [[S_P]] [[ARG1:%.*]]) ; ARGPROMOTION-NEXT: bb: -; ARGPROMOTION-NEXT: [[TMP:%.*]] = getelementptr [[S:%.*]], %S* [[ARG1]], i32 0, i32 0 -; ARGPROMOTION-NEXT: [[TMP2:%.*]] = load %S*, %S** [[TMP]] -; ARGPROMOTION-NEXT: [[TMP3:%.*]] = getelementptr [[S]], %S* [[ARG]], i32 0, i32 0 -; ARGPROMOTION-NEXT: [[TMP4:%.*]] = load %S*, %S** [[TMP3]] -; ARGPROMOTION-NEXT: [[TMP5:%.*]] = call i32 @test_inf_promote_callee(%S* [[TMP4]], %S* [[TMP2]]) +; ARGPROMOTION-NEXT: [[TMP:%.*]] = getelementptr [[S:%.*]], [[S_P]] [[ARG1]], i32 0, i32 0 +; ARGPROMOTION-NEXT: [[TMP2:%.*]] = load [[S_P]], [[S_PP]] [[TMP]] +; ARGPROMOTION-NEXT: [[TMP3:%.*]] = getelementptr [[S]], [[S_P]] [[ARG]], i32 0, i32 0 +; ARGPROMOTION-NEXT: [[TMP4:%.*]] = load [[S_P]], [[S_PP]] [[TMP3]] +; ARGPROMOTION-NEXT: [[TMP5:%.*]] = call i32 @test_inf_promote_callee([[S_P]] [[TMP4]], [[S_P]] [[TMP2]]) ; ARGPROMOTION-NEXT: ret i32 0 ; bb: diff --git a/llvm/test/Transforms/ArgumentPromotion/dbg.ll b/llvm/test/Transforms/ArgumentPromotion/dbg.ll --- a/llvm/test/Transforms/ArgumentPromotion/dbg.ll +++ b/llvm/test/Transforms/ArgumentPromotion/dbg.ll @@ -1,5 +1,12 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes ; RUN: opt < %s -passes=argpromotion -S | FileCheck %s +; RUN: opt < %s -passes=argpromotion -opaque-pointers -S | FileCheck %s + +; Define variables for typed or opaque pointers +; CHECK-LABEL: %pointers = type +; CHECK-SAME: { [[I32_P:.*]], [[I32_PP:.*]], [[STRUCT_PAIR_P:.*]] } +%pointers = type { i32*, i32**, %struct.pair* } +@pointers = global %pointers zeroinitializer declare void @sink(i32) @@ -21,10 +28,10 @@ ; CHECK-LABEL: define {{[^@]+}}@test_byval ; CHECK-SAME: (i32 [[P_0:%.*]], i32 [[P_1:%.*]]) { ; CHECK-NEXT: [[P:%.*]] = alloca [[STRUCT_PAIR:%.*]], align 4 -; CHECK-NEXT: [[DOT0:%.*]] = getelementptr [[STRUCT_PAIR]], %struct.pair* [[P]], i32 0, i32 0 -; CHECK-NEXT: store i32 [[P_0]], i32* [[DOT0]], align 4 -; CHECK-NEXT: [[DOT1:%.*]] = getelementptr [[STRUCT_PAIR]], %struct.pair* [[P]], i32 0, i32 1 -; CHECK-NEXT: store i32 [[P_1]], i32* [[DOT1]], align 4 +; CHECK-NEXT: [[DOT0:%.*]] = getelementptr [[STRUCT_PAIR]], [[STRUCT_PAIR_P]] [[P]], i32 0, i32 0 +; CHECK-NEXT: store i32 [[P_0]], [[I32_P]] [[DOT0]], align 4 +; CHECK-NEXT: [[DOT1:%.*]] = getelementptr [[STRUCT_PAIR]], [[STRUCT_PAIR_P]] [[P]], i32 0, i32 1 +; CHECK-NEXT: store i32 [[P_1]], [[I32_P]] [[DOT1]], align 4 ; CHECK-NEXT: ret void ; ret void @@ -32,14 +39,14 @@ define void @caller(i32** %Y, %struct.pair* %P) { ; CHECK-LABEL: define {{[^@]+}}@caller -; CHECK-SAME: (i32** [[Y:%.*]], %struct.pair* [[P:%.*]]) { -; CHECK-NEXT: [[Y_VAL:%.*]] = load i32*, i32** [[Y]], align 8, [[DBG4:!dbg !.*]] -; CHECK-NEXT: [[Y_VAL_VAL:%.*]] = load i32, i32* [[Y_VAL]], align 8, [[DBG4]] +; CHECK-SAME: ([[I32_PP]] [[Y:%.*]], [[STRUCT_PAIR_P]] [[P:%.*]]) { +; CHECK-NEXT: [[Y_VAL:%.*]] = load [[I32_P]], [[I32_PP]] [[Y]], align 8, [[DBG4:!dbg !.*]] +; CHECK-NEXT: [[Y_VAL_VAL:%.*]] = load i32, [[I32_P]] [[Y_VAL]], align 8, [[DBG4]] ; CHECK-NEXT: call void @test(i32 [[Y_VAL_VAL]]), [[DBG4]] -; CHECK-NEXT: [[P_0:%.*]] = getelementptr [[STRUCT_PAIR:%.*]], %struct.pair* [[P]], i32 0, i32 0, [[DBG5:!dbg !.*]] -; CHECK-NEXT: [[P_0_VAL:%.*]] = load i32, i32* [[P_0]], align 4, [[DBG5]] -; CHECK-NEXT: [[P_1:%.*]] = getelementptr [[STRUCT_PAIR]], %struct.pair* [[P]], i32 0, i32 1, [[DBG5]] -; CHECK-NEXT: [[P_1_VAL:%.*]] = load i32, i32* [[P_1]], align 4, [[DBG5]] +; CHECK-NEXT: [[P_0:%.*]] = getelementptr [[STRUCT_PAIR:%.*]], [[STRUCT_PAIR_P]] [[P]], i32 0, i32 0, [[DBG5:!dbg !.*]] +; CHECK-NEXT: [[P_0_VAL:%.*]] = load i32, [[I32_P]] [[P_0]], align 4, [[DBG5]] +; CHECK-NEXT: [[P_1:%.*]] = getelementptr [[STRUCT_PAIR]], [[STRUCT_PAIR_P]] [[P]], i32 0, i32 1, [[DBG5]] +; CHECK-NEXT: [[P_1_VAL:%.*]] = load i32, [[I32_P]] [[P_1]], align 4, [[DBG5]] ; CHECK-NEXT: call void @test_byval(i32 [[P_0_VAL]], i32 [[P_1_VAL]]), [[DBG5]] ; CHECK-NEXT: ret void ; diff --git a/llvm/test/Transforms/ArgumentPromotion/dead-gep-no-promotion.ll b/llvm/test/Transforms/ArgumentPromotion/dead-gep-no-promotion.ll --- a/llvm/test/Transforms/ArgumentPromotion/dead-gep-no-promotion.ll +++ b/llvm/test/Transforms/ArgumentPromotion/dead-gep-no-promotion.ll @@ -1,13 +1,20 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=argpromotion -S %s | FileCheck %s +; RUN: opt -passes=argpromotion -opaque-pointers -S %s | FileCheck %s @glob = external global i32* +; Define variables for typed or opaque pointers +; CHECK-LABEL: %pointers = type +; CHECK-SAME: { [[I32_P:.*]], [[I32_PP:.*]] } +%pointers = type { i32*, i32** } +@pointers = global %pointers zeroinitializer + ; No arguments in @callee can be promoted, but it contains a dead GEP. Make ; sure it is not removed, as we do not perform any promotion. define i32 @caller(i32* %ptr) { ; CHECK-LABEL: @caller( -; CHECK-NEXT: call void @callee(i32* [[PTR:%.*]], i32* [[PTR]], i32* [[PTR]]) +; CHECK-NEXT: call void @callee([[I32_P]] [[PTR:%.*]], [[I32_P]] [[PTR]], [[I32_P]] [[PTR]]) ; CHECK-NEXT: ret i32 0 ; call void @callee(i32* %ptr, i32* %ptr, i32* %ptr) @@ -16,9 +23,9 @@ define internal void @callee(i32* %arg, i32* %arg1, i32* %arg2) { ; CHECK-LABEL: define internal void @callee( -; CHECK-NEXT: call void @external_fn(i32* [[ARG:%.*]], i32* [[ARG1:%.*]]) -; CHECK-NEXT: [[DEAD_GEP:%.*]] = getelementptr inbounds i32, i32* [[ARG1]], i32 17 -; CHECK-NEXT: store i32* [[ARG2:%.*]], i32** @glob, align 8 +; CHECK-NEXT: call void @external_fn([[I32_P]] [[ARG:%.*]], [[I32_P]] [[ARG1:%.*]]) +; CHECK-NEXT: [[DEAD_GEP:%.*]] = getelementptr inbounds i32, [[I32_P]] [[ARG1]], i32 17 +; CHECK-NEXT: store [[I32_P]] [[ARG2:%.*]], [[I32_PP]] @glob, align 8 ; CHECK-NEXT: ret void ; call void @external_fn(i32* %arg, i32* %arg1) diff --git a/llvm/test/Transforms/ArgumentPromotion/opaque-recursion.ll b/llvm/test/Transforms/ArgumentPromotion/opaque-recursion.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/ArgumentPromotion/opaque-recursion.ll @@ -0,0 +1,55 @@ +; RUN: opt -S < %s -inline -argpromotion | FileCheck %s +; RUN: opt -S < %s -inline -argpromotion -opaque-pointers | FileCheck %s +; RUN: opt -S < %s -passes=inline,argpromotion | FileCheck %s +; RUN: opt -S < %s -passes=inline,argpromotion -opaque-pointers | FileCheck %s + +%S = type { %S* } +%T = type { %S* } + +; Define variables for typed or opaque pointers +; CHECK-LABEL: %pointers = type +; CHECK-SAME: { [[I8_P:.*]], [[S_P:.*]], [[S_PP:.*]], [[T_P:.*]] } +%pointers = type { i8*, %S*, %S**, %T* } +@pointers = global %pointers zeroinitializer + +define i32 @caller(i32 %arg) { +; CHECK-LABEL: define {{[^@]+}}@caller +; CHECK-SAME: (i32 [[ARG:%.*]]) +; CHECK-NEXT: bb: +; CHECK-NEXT: [[TMP:%.*]] = alloca [[T:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = call i32 @callee([[T_P]] [[TMP]]) +; CHECK-NEXT: ret i32 0 +; +bb: + %tmp = alloca %T + %tmp1 = call i32 @callee(%T* %tmp) + + ret i32 0 +} + +; This function could be arg-promoted once, but with opaque pointers it looks +; like a recursive type promotion that's blocked by our current heuristic, +; because we can't tell the difference between %S* and %T* anymore. +; (The heuristic is now the same with typed pointers for migration consistency.) +define internal i32 @callee(%T* %arg) { +; CHECK-LABEL: define {{[^@]+}}@callee +; CHECK-SAME: ([[T_P]] [[ARG:%.*]]) +; CHECK-NEXT: bb: +; CHECK-NEXT: [[NEW:%.*]] = alloca [[T:%.*]] +; CHECK-NEXT: [[TMP:%.*]] = getelementptr [[T:%.*]], [[T_P]] [[ARG]], i32 0, i32 0 +; CHECK-NEXT: [[TMP1:%.*]] = load [[S_P]], [[S_PP]] [[TMP]] +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr [[T]], [[T_P]] [[NEW]], i32 0, i32 0 +; CHECK-NEXT: store [[S_P]] [[TMP1]], [[S_PP]] [[TMP2]] +; CHECK-NEXT: [[TMP3:%.*]] = call i32 @callee([[T_P]] [[NEW]]) +; CHECK-NEXT: ret i32 0 +; +bb: + %new = alloca %T + %tmp = getelementptr %T, %T* %arg, i32 0, i32 0 + %tmp1 = load %S*, %S** %tmp + %tmp2 = getelementptr %T, %T* %new, i32 0, i32 0 + store %S* %tmp1, %S** %tmp2 + %tmp3 = call i32 @callee(%T* %new) + + ret i32 0 +}