diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -2023,11 +2023,23 @@ if (!GEP.accumulateConstantOffset(DL, Offset)) return nullptr; + APInt OffsetOld = Offset; // Convert the total offset back into indices. SmallVector ConstIndices = DL.getGEPIndicesForOffset(BaseType, Offset); - if (!Offset.isZero() || (!IsFirstType && !ConstIndices[0].isZero())) + if (!Offset.isZero() || (!IsFirstType && !ConstIndices[0].isZero())) { + // If both GEP are constant-indexed, and cannot be merged in either way, + // convert them to a GEP of i8. + if (Src->hasAllConstantIndices()) + return isMergedGEPInBounds(*Src, *cast(&GEP)) + ? GetElementPtrInst::CreateInBounds( + Builder.getInt8Ty(), Src->getOperand(0), + Builder.getInt(OffsetOld), GEP.getName()) + : GetElementPtrInst::Create( + Builder.getInt8Ty(), Src->getOperand(0), + Builder.getInt(OffsetOld), GEP.getName()); return nullptr; + } bool IsInBounds = isMergedGEPInBounds(*Src, *cast(&GEP)); SmallVector Indices; diff --git a/llvm/test/Transforms/InstCombine/gep-merge-constant-indices.ll b/llvm/test/Transforms/InstCombine/gep-merge-constant-indices.ll --- a/llvm/test/Transforms/InstCombine/gep-merge-constant-indices.ll +++ b/llvm/test/Transforms/InstCombine/gep-merge-constant-indices.ll @@ -20,7 +20,7 @@ ret ptr %2 } -; First GEP is merged into the second. +; Converted to i8* and merged. ; result = (i8*) p + 10 define ptr @mergeDifferentTypes(ptr %p) { ; CHECK-LABEL: @mergeDifferentTypes( @@ -32,13 +32,12 @@ ret ptr %2 } -; Second GEP is merged into the first. +; Converted to i8* and merged. ; result = (i8*) p + 10 define ptr @mergeReverse(ptr %p) { ; CHECK-LABEL: @mergeReverse( -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i64, ptr [[P:%.*]], i64 1 -; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 2 -; CHECK-NEXT: ret ptr [[TMP2]] +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 10 +; CHECK-NEXT: ret ptr [[TMP1]] ; %1 = getelementptr inbounds i64, ptr %p, i64 1 %2 = getelementptr inbounds i8, ptr %1, i64 2 @@ -67,36 +66,24 @@ ret ptr %2 } -; result = (i8*) p + 14 -define ptr @array2(ptr %p, i64 %a) { +; Converted to i8* and merged. +; result = (i8*) p + 20 +define ptr @array2(ptr %p) { ; CHECK-LABEL: @array2( -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [7 x i32], ptr [[P:%.*]], i64 0, i64 3 -; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 2 -; CHECK-NEXT: ret ptr [[TMP2]] -; - %1 = getelementptr inbounds [7 x i32], ptr %p, i64 0, i64 3 - %2 = getelementptr inbounds i8, ptr %1, i64 2 - ret ptr %2 -} - -; result = (i8*) (([3 x i8]*) p + 6) + 2 -define ptr @array3(ptr %p) { -; CHECK-LABEL: @array3( -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i64, ptr [[P:%.*]], i64 2 -; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [3 x i8], ptr [[TMP1]], i64 1, i64 1 -; CHECK-NEXT: ret ptr [[TMP2]] +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 20 +; CHECK-NEXT: ret ptr [[TMP1]] ; %1 = getelementptr inbounds i64, ptr %p, i64 2 %2 = getelementptr inbounds [3 x i8], ptr %1, i64 1, i64 1 ret ptr %2 } -; result = (struct.C*) p + 3 +; Converted to i8* and merged. +; result = (i8*) p + 36 define ptr @struct1(ptr %p) { ; CHECK-LABEL: @struct1( -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i64, ptr [[P:%.*]], i64 3 -; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_C:%.*]], ptr [[TMP1]], i64 1 -; CHECK-NEXT: ret ptr [[TMP2]] +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 36 +; CHECK-NEXT: ret ptr [[TMP1]] ; %1 = getelementptr inbounds i64, ptr %p, i64 3 %2 = getelementptr inbounds %struct.C, ptr %1, i64 1 @@ -114,28 +101,6 @@ ret ptr %2 } -; result = (i8*) p - 4 -define ptr @struct3(ptr %p, i64 %a) { -; CHECK-LABEL: @struct3( -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 -4 -; CHECK-NEXT: ret ptr [[TMP1]] -; - %1 = getelementptr inbounds i8, ptr %p, i64 -128 - %2 = getelementptr inbounds %struct.A, ptr %1, i64 0, i32 1 - ret ptr %2 -} -; result = ((struct.C*) p + 1).member2 -define ptr @struct4(ptr %p, i64 %a) { -; CHECK-LABEL: @struct4( -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i64, ptr [[P:%.*]], i64 1 -; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_C:%.*]], ptr [[TMP1]], i64 1 -; CHECK-NEXT: ret ptr [[TMP2]] -; - %1 = getelementptr inbounds i64, ptr %p, i64 1 - %2 = getelementptr inbounds %struct.C, ptr %1, i64 1 - ret ptr %2 -} - ; result = (i8*) &((struct.B) p)[0].member2.member0 + 7 define ptr @structStruct(ptr %p) { ; CHECK-LABEL: @structStruct( @@ -150,73 +115,33 @@ ; First GEP offset is not divisible by last GEP's source element size, but first ; GEP points to an array such that the last GEP offset is divisible by the ; array's element size, so the first GEP can be rewritten with an extra index. -; result = (i16*) &((struct.B*) p)[0].member1 + 2 -define ptr @appendIndex(ptr %p) { +; result = (i16*) &((struct.B*) p)[i].member1 + 2 +define ptr @appendIndex(ptr %p, i64 %i) { ; CHECK-LABEL: @appendIndex( -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_B:%.*]], ptr [[P:%.*]], i64 0, i32 1, i64 2 +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_B:%.*]], ptr [[P:%.*]], i64 [[I:%.*]], i32 1, i64 2 ; CHECK-NEXT: ret ptr [[TMP1]] ; - %1 = getelementptr inbounds %struct.B, ptr %p, i64 0, i32 1 + %1 = getelementptr inbounds %struct.B, ptr %p, i64 %i, i32 1 %2 = getelementptr inbounds i32, ptr %1, i64 1 ret ptr %2 } -; result = (i8*) &((struct.A*) &((struct.B*) p)[0].member2).member0 + 2 -define ptr @appendIndexReverse(ptr %p) { -; CHECK-LABEL: @appendIndexReverse( -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i64, ptr [[P:%.*]], i64 1 -; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_B:%.*]], ptr [[TMP1]], i64 0, i32 1 -; CHECK-NEXT: ret ptr [[TMP2]] -; - %1 = getelementptr inbounds i64, ptr %p, i64 1 - %2 = getelementptr inbounds %struct.B, ptr %1, i64 0, i32 1 - ret ptr %2 -} - -; Constant-indexed GEP can be merged if the sum of offsets aliases a member's -; address of one of the GEP instructions. -; result = &((struct.C*) p + 2).member1 -define ptr @structMemberAliasing(ptr %p, i64 %a) { -; CHECK-LABEL: @structMemberAliasing( -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i64, ptr [[P:%.*]], i64 1 -; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_C:%.*]], ptr [[TMP1]], i64 1, i32 2 -; CHECK-NEXT: ret ptr [[TMP2]] -; - %1 = getelementptr inbounds i64, ptr %p, i64 1 - %2 = getelementptr inbounds %struct.C, ptr %1, i64 1, i32 2 - ret ptr %2 -} - -; Negative test. Offset of either GEP is not divisible by the other's size. +; Offset of either GEP is not divisible by the other's size, converted to i8* +; and merged. ; Here i24 is 8-bit aligned. +; result = (i8*) p + 7 define ptr @notDivisible(ptr %p) { ; CHECK-LABEL: @notDivisible( -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i24, ptr [[P:%.*]], i64 1 -; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 1 -; CHECK-NEXT: ret ptr [[TMP2]] +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 7 +; CHECK-NEXT: ret ptr [[TMP1]] ; %1 = getelementptr inbounds i24, ptr %p, i64 1 %2 = getelementptr inbounds i32, ptr %1, i64 1 ret ptr %2 } -; Two GEP instructions can be merged if one is constant-indexed and the other -; is a sequential type with a constant last index, and the constant offset is -; divisible by the sequential type size. -; result = (i32*) (([4 x i32]*) p + a) + 3 -define ptr @partialConstant1(ptr %p, i64 %a) { -; CHECK-LABEL: @partialConstant1( -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1 -; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [4 x i32], ptr [[TMP1]], i64 [[A:%.*]], i64 2 -; CHECK-NEXT: ret ptr [[TMP2]] -; - %1 = getelementptr inbounds i32, ptr %p, i64 1 - %2 = getelementptr inbounds [4 x i32], ptr %1, i64 %a, i64 2 - ret ptr %2 -} - -; Negative test. Similar to above, but two GEP should not be merged if the -; constant offset is not divisible. +; Negative test. Two GEP should not be merged if not both offsets are constant +; or divisible by the other's size. define ptr @partialConstant2(ptr %p, i64 %a) { ; CHECK-LABEL: @partialConstant2( ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1 @@ -228,8 +153,8 @@ ret ptr %2 } -; Negative test. Similar to above, but two GEP should not be merged if there is -; another use of the first GEP by the second GEP. +; Negative test. Two GEP should not be merged if there is another use of the +; first GEP by the second GEP. define ptr @partialConstant3(ptr %p) { ; CHECK-LABEL: @partialConstant3( ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1 @@ -249,12 +174,11 @@ ; result = &((struct.C*) p + a).member2 define ptr @partialConstantMemberAliasing1(ptr %p, i64 %a) { ; CHECK-LABEL: @partialConstantMemberAliasing1( -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1 -; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_C:%.*]], ptr [[TMP1]], i64 [[A:%.*]], i32 1 -; CHECK-NEXT: ret ptr [[TMP2]] +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_C:%.*]], ptr [[P:%.*]], i64 [[A:%.*]], i32 2 +; CHECK-NEXT: ret ptr [[TMP1]] ; - %1 = getelementptr inbounds i32, ptr %p, i64 1 - %2 = getelementptr inbounds %struct.C, ptr %1, i64 %a, i32 1 + %1 = getelementptr inbounds %struct.C, ptr %p, i64 %a, i32 1 + %2 = getelementptr inbounds i32, ptr %1, i64 1 ret ptr %2 } @@ -262,12 +186,12 @@ ; address of another member. define ptr @partialConstantMemberAliasing2(ptr %p, i64 %a) { ; CHECK-LABEL: @partialConstantMemberAliasing2( -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 1 -; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_C:%.*]], ptr [[TMP1]], i64 [[A:%.*]], i32 1 +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_C:%.*]], ptr [[P:%.*]], i64 [[A:%.*]], i32 1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 1 ; CHECK-NEXT: ret ptr [[TMP2]] ; - %1 = getelementptr inbounds i8, ptr %p, i64 1 - %2 = getelementptr inbounds %struct.C, ptr %1, i64 %a, i32 1 + %1 = getelementptr inbounds %struct.C, ptr %p, i64 %a, i32 1 + %2 = getelementptr inbounds i8, ptr %1, i64 1 ret ptr %2 } @@ -275,11 +199,11 @@ ; range of the object currently pointed by the non-constant GEP. define ptr @partialConstantMemberAliasing3(ptr %p, i64 %a) { ; CHECK-LABEL: @partialConstantMemberAliasing3( -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1 -; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_C:%.*]], ptr [[TMP1]], i64 [[A:%.*]], i32 2 +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_C:%.*]], ptr [[P:%.*]], i64 [[A:%.*]], i32 2 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 1 ; CHECK-NEXT: ret ptr [[TMP2]] ; - %1 = getelementptr inbounds i32, ptr %p, i64 1 - %2 = getelementptr inbounds %struct.C, ptr %1, i64 %a, i32 2 + %1 = getelementptr inbounds %struct.C, ptr %p, i64 %a, i32 2 + %2 = getelementptr inbounds i32, ptr %1, i64 1 ret ptr %2 } diff --git a/llvm/test/Transforms/InstCombine/opaque-ptr.ll b/llvm/test/Transforms/InstCombine/opaque-ptr.ll --- a/llvm/test/Transforms/InstCombine/opaque-ptr.ll +++ b/llvm/test/Transforms/InstCombine/opaque-ptr.ll @@ -211,8 +211,7 @@ define ptr @geps_combinable_different_elem_type4(ptr %a) { ; CHECK-LABEL: @geps_combinable_different_elem_type4( -; CHECK-NEXT: [[A2:%.*]] = getelementptr { i32, i32 }, ptr [[A:%.*]], i64 0, i32 1 -; CHECK-NEXT: [[A3:%.*]] = getelementptr i8, ptr [[A2]], i64 10 +; CHECK-NEXT: [[A3:%.*]] = getelementptr i8, ptr [[A:%.*]], i64 14 ; CHECK-NEXT: ret ptr [[A3]] ; %a2 = getelementptr { i32, i32 }, ptr %a, i32 0, i32 1