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 @@ -9,6 +9,8 @@ %struct.B = type { i8, [3 x i16], %struct.A, float } %struct.C = type { i8, i32, i32 } +declare void @use(ptr %p) + ; result = (i32*) p + 3 define ptr @mergeBasic(ptr %p) { ; CHECK-LABEL: @mergeBasic( @@ -220,3 +222,77 @@ %2 = getelementptr inbounds i32, ptr %1, i64 1 ret ptr %2 } + +; If the first and last in a chain of GEP are constant indexed, they can be +; merged, even if the first GEP has multiple uses. +; result = (i8*) ((i32*) p + a + b) + 22 +define ptr @gepChain1(ptr %p, i64 %a, i64 %b) { +; CHECK-LABEL: @gepChain1( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i64, ptr [[P:%.*]], i64 2 +; CHECK-NEXT: call void @use(ptr nonnull [[TMP1]]) +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 [[B:%.*]] +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds i16, ptr [[TMP3]], i64 3 +; CHECK-NEXT: ret ptr [[TMP4]] +; + %1 = getelementptr inbounds i64, ptr %p, i64 2 + call void @use(ptr %1) + %2 = getelementptr inbounds i32, ptr %1, i64 %a + %3 = getelementptr inbounds i32, ptr %2, i64 %b + %4 = getelementptr inbounds i16, ptr %3, i64 3 + ret ptr %4 +} + +; Constant indexed GEP cancel out. +; result = (i32*) p + a + b +define ptr @gepChain2(ptr %p, i64 %a, i64 %b) { +; CHECK-LABEL: @gepChain2( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i64, ptr [[P:%.*]], i64 2 +; CHECK-NEXT: call void @use(ptr nonnull [[TMP1]]) +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 [[B:%.*]] +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds i16, ptr [[TMP3]], i64 -8 +; CHECK-NEXT: ret ptr [[TMP4]] +; + %1 = getelementptr inbounds i64, ptr %p, i64 2 + call void @use(ptr %1) + %2 = getelementptr inbounds i32, ptr %1, i64 %a + %3 = getelementptr inbounds i32, ptr %2, i64 %b + %4 = getelementptr inbounds i16, ptr %3, i64 -8 + ret ptr %4 +} + +; Negative test. If the last GEP offset is zero, do not merge since it is a +; bitcast with no cost. +define ptr @gepChainZeroOffset(ptr %p, i64 %a) { +; CHECK-LABEL: @gepChainZeroOffset( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i64, ptr [[P:%.*]], i64 2 +; CHECK-NEXT: call void @use(ptr nonnull [[TMP1]]) +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: ret ptr [[TMP2]] +; + %1 = getelementptr inbounds i64, ptr %p, i64 2 + call void @use(ptr %1) + %2 = getelementptr inbounds i32, ptr %1, i64 %a + %3 = getelementptr inbounds [4 x i16], ptr %2, i64 1, i64 -4 + ret ptr %3 +} + +; Negative test. Do not merge if a GEP in the middle has multiple use, such that +; the transform is invalid. +define ptr @gepChainMultipleUse(ptr %p, i64 %a) { +; CHECK-LABEL: @gepChainMultipleUse( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i64, ptr [[P:%.*]], i64 2 +; CHECK-NEXT: call void @use(ptr nonnull [[TMP1]]) +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: call void @use(ptr nonnull [[TMP2]]) +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 3 +; CHECK-NEXT: ret ptr [[TMP3]] +; + %1 = getelementptr inbounds i64, ptr %p, i64 2 + call void @use(ptr %1) + %2 = getelementptr inbounds i32, ptr %1, i64 %a + call void @use(ptr %2) + %3 = getelementptr inbounds i32, ptr %2, i64 3 + ret ptr %3 +}