llvm/llvm/test/Transforms/InstCombine/gep-merge-constant-indices.ll

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=instcombine -S | FileCheck %s

; Test merging GEP of GEP with constant indices.

target datalayout = "i24:8:8"

%struct.A = type { [123 x i8], i32 }
%struct.B = type { i8, [3 x i16], %struct.A, float }
%struct.C = type { i8, i32, i32 }

; result = (ptr) p + 3
define ptr @mergeBasic(ptr %p) {
; CHECK-LABEL: @mergeBasic(
; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 12
; CHECK-NEXT:    ret ptr [[TMP1]]
;
  %1 = getelementptr inbounds i32, ptr %p, i64 1
  %2 = getelementptr inbounds i32, ptr %1, i64 2
  ret ptr %2
}

; Converted to ptr and merged.
; result = (ptr) p + 10
define ptr @mergeDifferentTypes(ptr %p) {
; CHECK-LABEL: @mergeDifferentTypes(
; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 10
; CHECK-NEXT:    ret ptr [[TMP1]]
;
  %1 = getelementptr inbounds i8, ptr %p, i64 2
  %2 = getelementptr inbounds i64, ptr %1, i64 1
  ret ptr %2
}

; Converted to ptr and merged.
; result = (ptr) p + 10
define ptr @mergeReverse(ptr %p) {
; CHECK-LABEL: @mergeReverse(
; 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
  ret ptr %2
}

; Offsets of first and last GEP cancel out.
; result = p
define ptr @zeroSum(ptr %p) {
; CHECK-LABEL: @zeroSum(
; CHECK-NEXT:    ret ptr [[P:%.*]]
;
  %1 = getelementptr inbounds i32, ptr %p, i64 1
  %2 = getelementptr inbounds i8, ptr %1, i64 -4
  ret ptr %2
}

; result = (ptr) ((ptr) p + 1) + 17
define ptr @array1(ptr %p) {
; CHECK-LABEL: @array1(
; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 37
; CHECK-NEXT:    ret ptr [[TMP1]]
;
  %1 = getelementptr inbounds [20 x i8], ptr %p, i64 1, i64 1
  %2 = getelementptr inbounds i64, ptr %1, i64 2
  ret ptr %2
}

; Converted to ptr and merged.
; result = (ptr) p + 20
define ptr @array2(ptr %p) {
; CHECK-LABEL: @array2(
; 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
}

; Converted to ptr and merged.
; result = (ptr) p + 36
define ptr @struct1(ptr %p) {
; CHECK-LABEL: @struct1(
; 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
  ret ptr %2
}

; result = &((struct.A*) p - 1).member1
define ptr @struct2(ptr %p) {
; CHECK-LABEL: @struct2(
; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 -4
; CHECK-NEXT:    ret ptr [[TMP1]]
;
  %1 = getelementptr inbounds %struct.A, ptr %p, i64 0, i32 1
  %2 = getelementptr inbounds i8, ptr %1, i64 -128
  ret ptr %2
}

; result = (ptr) &((struct.B) p)[0].member2.member0 + 7
define ptr @structStruct(ptr %p) {
; CHECK-LABEL: @structStruct(
; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 15
; CHECK-NEXT:    ret ptr [[TMP1]]
;
  %1 = getelementptr inbounds %struct.B, ptr %p, i64 0, i32 2, i32 0, i64 3
  %2 = getelementptr inbounds %struct.A, ptr %1, i64 0, i32 0, i64 4
  ret ptr %2
}

; 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 = (ptr) &((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 [[I:%.*]], i32 1, i64 2
; CHECK-NEXT:    ret ptr [[TMP1]]
;
  %1 = getelementptr inbounds %struct.B, ptr %p, i64 %i, i32 1
  %2 = getelementptr inbounds i32, ptr %1, i64 1
  ret ptr %2
}

; Offset of either GEP is not divisible by the other's size, converted to ptr
; and merged.
; Here i24 is 8-bit aligned.
; result = (ptr) p + 7
define ptr @notDivisible(ptr %p) {
; CHECK-LABEL: @notDivisible(
; 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
}

; 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 i8, ptr [[P:%.*]], i64 4
; CHECK-NEXT:    [[TMP2:%.*]] = getelementptr inbounds [4 x i64], ptr [[TMP1]], i64 [[A:%.*]], i64 2
; CHECK-NEXT:    ret ptr [[TMP2]]
;
  %1 = getelementptr inbounds i32, ptr %p, i64 1
  %2 = getelementptr inbounds [4 x i64], ptr %1, i64 %a, i64 2
  ret ptr %2
}

; 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 i8, ptr [[P:%.*]], i64 4
; CHECK-NEXT:    [[TMP2:%.*]] = ptrtoint ptr [[TMP1]] to i64
; CHECK-NEXT:    [[TMP3:%.*]] = getelementptr inbounds [4 x i64], ptr [[TMP1]], i64 [[TMP2]], i64 2
; CHECK-NEXT:    ret ptr [[TMP3]]
;
  %1 = getelementptr inbounds i32, ptr %p, i64 1
  %2 = ptrtoint ptr %1 to i64
  %3 = getelementptr inbounds [4 x i64], ptr %1, i64 %2, i64 2
  ret ptr %3
}

; Two GEP instructions can be merged if one is constant-indexed and the other
; is an aggregate type with a constant last index, and the resulting pointer
; address by adding the constant offset aliases the address of another member.
; result = &((struct.C*) p + a).member2
define ptr @partialConstantMemberAliasing1(ptr %p, i64 %a) {
; CHECK-LABEL: @partialConstantMemberAliasing1(
; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_C:%.*]], ptr [[P:%.*]], i64 [[A:%.*]], i32 2
; CHECK-NEXT:    ret ptr [[TMP1]]
;
  %1 = getelementptr inbounds %struct.C, ptr %p, i64 %a, i32 1
  %2 = getelementptr inbounds i32, ptr %1, i64 1
  ret ptr %2
}

; Negative test. Similar to above, but the new address does not alias the
; address of another member.
define ptr @partialConstantMemberAliasing2(ptr %p, i64 %a) {
; CHECK-LABEL: @partialConstantMemberAliasing2(
; 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 %struct.C, ptr %p, i64 %a, i32 1
  %2 = getelementptr inbounds i8, ptr %1, i64 1
  ret ptr %2
}

; Negative test. Similar to above, but the new address falls outside the address
; 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 [[STRUCT_C:%.*]], ptr [[P:%.*]], i64 [[A:%.*]], i32 2
; CHECK-NEXT:    [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 4
; CHECK-NEXT:    ret ptr [[TMP2]]
;
  %1 = getelementptr inbounds %struct.C, ptr %p, i64 %a, i32 2
  %2 = getelementptr inbounds i32, ptr %1, i64 1
  ret ptr %2
}