diff --git a/llvm/test/Transforms/InstCombine/gep-canonicalize-constant-indices.ll b/llvm/test/Transforms/InstCombine/gep-canonicalize-constant-indices.ll
new file mode 100644
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/gep-canonicalize-constant-indices.ll
@@ -0,0 +1,104 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -opaque-pointers -S | FileCheck %s
+
+; Constant-indexed GEP instructions in a chain of GEP instructions should be
+; swapped to the end whenever such transformation is valid. This allows them to
+; be merged.
+
+; The constant-indexed GEP instruction should be swapped to the end, even
+; without merging.
+; result = (((i32*) p + a) + b) + 1
+define ptr @basic(ptr %p, i64 %a, i64 %b) {
+; CHECK-LABEL: @basic(
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1
+; CHECK-NEXT:    [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[A:%.*]]
+; CHECK-NEXT:    [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 [[B:%.*]]
+; CHECK-NEXT:    ret ptr [[TMP3]]
+;
+  %1 = getelementptr inbounds i32, ptr %p, i64 1
+  %2 = getelementptr inbounds i32, ptr %1, i64 %a
+  %3 = getelementptr inbounds i32, ptr %2, i64 %b
+  ret ptr %3
+}
+
+; Constant-indexed GEP are merged after swawpping.
+; result = ((i32*) p + a) + 3
+define ptr @merge(ptr %p, i64 %a) {
+; CHECK-LABEL: @merge(
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1
+; CHECK-NEXT:    [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[A:%.*]]
+; CHECK-NEXT:    [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 2
+; CHECK-NEXT:    ret ptr [[TMP3]]
+;
+  %1 = getelementptr inbounds i32, ptr %p, i64 1
+  %2 = getelementptr inbounds i32, ptr %1, i64 %a
+  %3 = getelementptr inbounds i32, ptr %2, i64 2
+  ret ptr %3
+}
+
+; Multiple constant-indexed GEP. Note that the first two cannot be merged at
+; first, but after the second and third are merged, the result can be merged
+; with the first one on the next pass.
+; result = (<3 x i32>*) ((i16*) ((i8*) ptr + a) + (a * b)) + 9
+define ptr @nested(ptr %p, i64 %a, i64 %b) {
+; CHECK-LABEL: @nested(
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds <3 x i32>, ptr [[P:%.*]], i64 1
+; CHECK-NEXT:    [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[A:%.*]]
+; CHECK-NEXT:    [[TMP3:%.*]] = mul i64 [[A]], [[B:%.*]]
+; CHECK-NEXT:    [[TMP4:%.*]] = getelementptr inbounds <5 x i32>, ptr [[TMP2]], i64 4
+; CHECK-NEXT:    [[TMP5:%.*]] = getelementptr inbounds i16, ptr [[TMP4]], i64 [[TMP3]]
+; CHECK-NEXT:    [[TMP6:%.*]] = getelementptr inbounds <4 x i32>, ptr [[TMP5]], i64 1
+; CHECK-NEXT:    ret ptr [[TMP6]]
+;
+  %1 = getelementptr inbounds <3 x i32>, ptr %p, i64 1
+  %2 = getelementptr inbounds i8, ptr %1, i64 %a
+  %3 = mul i64 %a, %b
+  %4 = getelementptr inbounds <5 x i32>, ptr %2, i64 4
+  %5 = getelementptr inbounds i16, ptr %4, i64 %3
+  %6 = getelementptr inbounds <4 x i32>, ptr %5, i64 1
+  ret ptr %6
+}
+
+; It is valid to swap if the source operand of the first GEP has multiple uses.
+define ptr @multipleUses1(ptr %p) {
+; CHECK-LABEL: @multipleUses1(
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1
+; CHECK-NEXT:    [[TMP2:%.*]] = ptrtoint ptr [[P]] to i64
+; CHECK-NEXT:    [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[TMP2]]
+; CHECK-NEXT:    ret ptr [[TMP3]]
+;
+  %1 = getelementptr inbounds i32, ptr %p, i64 1
+  %2 = ptrtoint ptr %p to i64
+  %3 = getelementptr inbounds i32, ptr %1, i64 %2
+  ret ptr %3
+}
+
+; It is valid to swap if the second GEP has multiple uses.
+define {ptr, ptr} @multipleUses2(ptr %p, i64 %a) {
+; CHECK-LABEL: @multipleUses2(
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1
+; CHECK-NEXT:    [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[A:%.*]]
+; CHECK-NEXT:    [[TMP3:%.*]] = insertvalue { ptr, ptr } undef, ptr [[TMP2]], 0
+; CHECK-NEXT:    [[TMP4:%.*]] = insertvalue { ptr, ptr } [[TMP3]], ptr [[TMP2]], 1
+; CHECK-NEXT:    ret { ptr, ptr } [[TMP4]]
+;
+  %1 = getelementptr inbounds i32, ptr %p, i64 1
+  %2 = getelementptr inbounds i32, ptr %1, i64 %a
+  %3 = insertvalue {ptr, ptr} undef, ptr %2, 0
+  %4 = insertvalue {ptr, ptr} %3, ptr %2, 1
+  ret {ptr, ptr} %4
+}
+
+; Negative test. It is not valid to swap if the first GEP has multiple uses.
+define ptr @multipleUses3(ptr %p) {
+; CHECK-LABEL: @multipleUses3(
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1
+; CHECK-NEXT:    [[TMP2:%.*]] = ptrtoint ptr [[TMP1]] to i64
+; CHECK-NEXT:    [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[TMP2]]
+; CHECK-NEXT:    ret ptr [[TMP3]]
+;
+  %1 = getelementptr inbounds i32, ptr %p, i64 1
+  %2 = ptrtoint ptr %1 to i64
+  %3 = getelementptr inbounds i32, ptr %1, i64 %2
+  ret ptr %3
+}
diff --git a/llvm/test/Transforms/InstCombine/gep-merge-constant-indices.ll b/llvm/test/Transforms/InstCombine/gep-merge-constant-indices.ll
new file mode 100644
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/gep-merge-constant-indices.ll
@@ -0,0 +1,172 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -opaque-pointers -S | FileCheck %s
+
+; Test merging GEP of GEP with constant indices.
+
+%struct.A = type { [123 x i8], i32 }
+%struct.B = type { i8, [3 x i16], %struct.A, float }
+%struct.C = type { i8, i32, i32 }
+
+; result = (i32*) p + 3
+define ptr @mergeBasic(ptr %p) {
+; CHECK-LABEL: @mergeBasic(
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 3
+; CHECK-NEXT:    ret ptr [[TMP1]]
+;
+  %1 = getelementptr inbounds i32, ptr %p, i64 1
+  %2 = getelementptr inbounds i32, ptr %1, i64 2
+  ret ptr %2
+}
+
+; First GEP is merged into the second.
+; result = (i8*) 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
+}
+
+; Second GEP is merged into the first.
+; 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]]
+;
+  %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 = (i8*) (([20 x i8]*) p)[1] + 17
+define ptr @array1(ptr %p) {
+; CHECK-LABEL: @array1(
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds [20 x i8], ptr [[P:%.*]], i64 1, i64 17
+; 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
+}
+
+; result = (i8*) p + 14
+define ptr @array2(ptr %p, i64 %a) {
+; 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)[1] + 17
+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]]
+;
+  %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
+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]]
+;
+  %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 [[STRUCT_A:%.*]], ptr [[P:%.*]], i64 -1, i32 1
+; 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 = (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*) &p[0].member2.member0 + 7
+define ptr @structStruct(ptr %p) {
+; CHECK-LABEL: @structStruct(
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_B:%.*]], ptr [[P:%.*]], i64 0, i32 2, i32 0, i64 7
+; 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 = (i16*) &p[0].member1 + 2
+define ptr @appendIndex(ptr %p) {
+; CHECK-LABEL: @appendIndex(
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_B:%.*]], ptr [[P:%.*]], i64 0, i32 1, i64 2
+; CHECK-NEXT:    ret ptr [[TMP1]]
+;
+  %1 = getelementptr inbounds %struct.B, ptr %p, i64 0, i32 1
+  %2 = getelementptr inbounds i32, ptr %1, i64 1
+  ret ptr %2
+}
+
+; result = (i16*) &((struct.B*) (p + a))[0].member1 + 4
+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
+}