Björn Pettersson 092b6e73e6
[InstCombine] Handle "add like" in ADD+GEP->GEP+GEP rewrites (#135156)
Considering that "or disjoint" is the canonical for certain add
operations, then I think we want to support such "add like" operations
when doing ADD+GEP->GEP+GEP rewrites to make things more consistent.

Problem was found when improving ValueTracking, which turned an ADD into
OR, and then suddenly optimizations got worse due to these rewrites no
longer triggering.
2025-04-14 17:11:13 +02:00

337 lines
13 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
define void @test(ptr %ptr, i32 %a, i32 %b) {
; CHECK-LABEL: define void @test(
; CHECK-SAME: ptr [[PTR:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = sext i32 [[A]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[PTR]], i64 [[TMP0]]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[TMP1]], i64 40
; CHECK-NEXT: store i32 [[B]], ptr [[GEP]], align 4
; CHECK-NEXT: ret void
;
entry:
%add = add nsw i32 %a, 10
%idx = sext i32 %add to i64
%gep = getelementptr inbounds i32, ptr %ptr, i64 %idx
store i32 %b, ptr %gep
ret void
}
define i32 @test_add_res_moreoneuse(ptr %ptr, i32 %a, i32 %b) {
; CHECK-LABEL: define i32 @test_add_res_moreoneuse(
; CHECK-SAME: ptr [[PTR:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 5
; CHECK-NEXT: [[IDX:%.*]] = sext i32 [[ADD]] to i64
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i32, ptr [[PTR]], i64 [[IDX]]
; CHECK-NEXT: store i32 [[B]], ptr [[GEP]], align 4
; CHECK-NEXT: ret i32 [[ADD]]
;
entry:
%add = add nsw i32 %a, 5
%idx = sext i32 %add to i64
%gep = getelementptr inbounds i32, ptr %ptr, i64 %idx
store i32 %b, ptr %gep
ret i32 %add
}
define void @test_addop_nonsw_flag(ptr %ptr, i32 %a, i32 %b) {
; CHECK-LABEL: define void @test_addop_nonsw_flag(
; CHECK-SAME: ptr [[PTR:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[A]], 10
; CHECK-NEXT: [[IDX:%.*]] = sext i32 [[ADD]] to i64
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i32, ptr [[PTR]], i64 [[IDX]]
; CHECK-NEXT: store i32 [[B]], ptr [[GEP]], align 4
; CHECK-NEXT: ret void
;
entry:
%add = add i32 %a, 10
%idx = sext i32 %add to i64
%gep = getelementptr inbounds i32, ptr %ptr, i64 %idx
store i32 %b, ptr %gep
ret void
}
define void @test_add_op2_not_constant(ptr %ptr, i32 %a, i32 %b) {
; CHECK-LABEL: define void @test_add_op2_not_constant(
; CHECK-SAME: ptr [[PTR:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[A]], [[B]]
; CHECK-NEXT: [[IDX:%.*]] = sext i32 [[ADD]] to i64
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i32, ptr [[PTR]], i64 [[IDX]]
; CHECK-NEXT: store i32 [[B]], ptr [[GEP]], align 4
; CHECK-NEXT: ret void
;
entry:
%add = add i32 %a, %b
%idx = sext i32 %add to i64
%gep = getelementptr inbounds i32, ptr %ptr, i64 %idx
store i32 %b, ptr %gep
ret void
}
define void @test_zext_nneg(ptr %ptr, i32 %a, i32 %b) {
; CHECK-LABEL: define void @test_zext_nneg(
; CHECK-SAME: ptr [[PTR:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = sext i32 [[A]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[PTR]], i64 [[TMP0]]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[TMP1]], i64 40
; CHECK-NEXT: store i32 [[B]], ptr [[GEP]], align 4
; CHECK-NEXT: ret void
;
entry:
%add = add nsw i32 %a, 10
%idx = zext nneg i32 %add to i64
%gep = getelementptr inbounds i32, ptr %ptr, i64 %idx
store i32 %b, ptr %gep
ret void
}
define void @test_zext_missing_nneg(ptr %ptr, i32 %a, i32 %b) {
; CHECK-LABEL: define void @test_zext_missing_nneg(
; CHECK-SAME: ptr [[PTR:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 10
; CHECK-NEXT: [[IDX:%.*]] = zext i32 [[ADD]] to i64
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds nuw i32, ptr [[PTR]], i64 [[IDX]]
; CHECK-NEXT: store i32 [[B]], ptr [[GEP]], align 4
; CHECK-NEXT: ret void
;
entry:
%add = add nsw i32 %a, 10
%idx = zext i32 %add to i64
%gep = getelementptr inbounds i32, ptr %ptr, i64 %idx
store i32 %b, ptr %gep
ret void
}
define ptr @gep_inbounds_nuwaddlike(ptr %ptr, i64 %a, i64 %b) {
; CHECK-LABEL: define ptr @gep_inbounds_nuwaddlike(
; CHECK-SAME: ptr [[PTR:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i32, ptr [[PTR]], i64 [[A]]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP1]], i64 [[B]]
; CHECK-NEXT: ret ptr [[GEP]]
;
%add = or disjoint i64 %a, %b
%gep = getelementptr inbounds nuw i32, ptr %ptr, i64 %add
ret ptr %gep
}
define ptr @gep_inbounds_add_nuw(ptr %ptr, i64 %a, i64 %b) {
; CHECK-LABEL: define ptr @gep_inbounds_add_nuw(
; CHECK-SAME: ptr [[PTR:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i32, ptr [[PTR]], i64 [[A]]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP1]], i64 [[B]]
; CHECK-NEXT: ret ptr [[GEP]]
;
%add = add nuw i64 %a, %b
%gep = getelementptr inbounds nuw i32, ptr %ptr, i64 %add
ret ptr %gep
}
define ptr @gep_inbounds_add_nusw_nuw(ptr %ptr, i64 %a, i64 %b) {
; CHECK-LABEL: define ptr @gep_inbounds_add_nusw_nuw(
; CHECK-SAME: ptr [[PTR:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr nusw nuw i32, ptr [[PTR]], i64 [[A]]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw nuw i32, ptr [[TMP1]], i64 [[B]]
; CHECK-NEXT: ret ptr [[GEP]]
;
%add = add nuw i64 %a, %b
%gep = getelementptr nusw nuw i32, ptr %ptr, i64 %add
ret ptr %gep
}
define ptr @gep_add_nuw(ptr %ptr, i64 %a, i64 %b) {
; CHECK-LABEL: define ptr @gep_add_nuw(
; CHECK-SAME: ptr [[PTR:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr nuw i32, ptr [[PTR]], i64 [[A]]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i32, ptr [[TMP1]], i64 [[B]]
; CHECK-NEXT: ret ptr [[GEP]]
;
%add = add nuw i64 %a, %b
%gep = getelementptr nuw i32, ptr %ptr, i64 %add
ret ptr %gep
}
define ptr @gep_inbounds_add_nsw_nonneg(ptr %ptr, i64 %a, i64 %b) {
; CHECK-LABEL: define ptr @gep_inbounds_add_nsw_nonneg(
; CHECK-SAME: ptr [[PTR:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
; CHECK-NEXT: [[A_NNEG:%.*]] = icmp sgt i64 [[A]], -1
; CHECK-NEXT: call void @llvm.assume(i1 [[A_NNEG]])
; CHECK-NEXT: [[B_NNEG:%.*]] = icmp sgt i64 [[B]], -1
; CHECK-NEXT: call void @llvm.assume(i1 [[B_NNEG]])
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i32, ptr [[PTR]], i64 [[A]]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP1]], i64 [[B]]
; CHECK-NEXT: ret ptr [[GEP]]
;
%a.nneg = icmp sgt i64 %a, -1
call void @llvm.assume(i1 %a.nneg)
%b.nneg = icmp sgt i64 %b, -1
call void @llvm.assume(i1 %b.nneg)
%add = add nsw i64 %a, %b
%gep = getelementptr inbounds i32, ptr %ptr, i64 %add
ret ptr %gep
}
define ptr @gep_inbounds_add_nsw_not_nonneg1(ptr %ptr, i64 %a, i64 %b) {
; CHECK-LABEL: define ptr @gep_inbounds_add_nsw_not_nonneg1(
; CHECK-SAME: ptr [[PTR:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
; CHECK-NEXT: [[A_NNEG:%.*]] = icmp sgt i64 [[A]], -1
; CHECK-NEXT: call void @llvm.assume(i1 [[A_NNEG]])
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[PTR]], i64 [[A]]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[TMP1]], i64 [[B]]
; CHECK-NEXT: ret ptr [[GEP]]
;
%a.nneg = icmp sgt i64 %a, -1
call void @llvm.assume(i1 %a.nneg)
%add = add nsw i64 %a, %b
%gep = getelementptr inbounds i32, ptr %ptr, i64 %add
ret ptr %gep
}
define ptr @gep_inbounds_add_nsw_not_nonneg2(ptr %ptr, i64 %a, i64 %b) {
; CHECK-LABEL: define ptr @gep_inbounds_add_nsw_not_nonneg2(
; CHECK-SAME: ptr [[PTR:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
; CHECK-NEXT: [[B_NNEG:%.*]] = icmp sgt i64 [[B]], -1
; CHECK-NEXT: call void @llvm.assume(i1 [[B_NNEG]])
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[PTR]], i64 [[A]]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[TMP1]], i64 [[B]]
; CHECK-NEXT: ret ptr [[GEP]]
;
%b.nneg = icmp sgt i64 %b, -1
call void @llvm.assume(i1 %b.nneg)
%add = add nsw i64 %a, %b
%gep = getelementptr inbounds i32, ptr %ptr, i64 %add
ret ptr %gep
}
define ptr @gep_not_inbounds_add_nsw_nonneg(ptr %ptr, i64 %a, i64 %b) {
; CHECK-LABEL: define ptr @gep_not_inbounds_add_nsw_nonneg(
; CHECK-SAME: ptr [[PTR:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
; CHECK-NEXT: [[A_NNEG:%.*]] = icmp sgt i64 [[A]], -1
; CHECK-NEXT: call void @llvm.assume(i1 [[A_NNEG]])
; CHECK-NEXT: [[B_NNEG:%.*]] = icmp sgt i64 [[B]], -1
; CHECK-NEXT: call void @llvm.assume(i1 [[B_NNEG]])
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[PTR]], i64 [[A]]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[TMP1]], i64 [[B]]
; CHECK-NEXT: ret ptr [[GEP]]
;
%a.nneg = icmp sgt i64 %a, -1
call void @llvm.assume(i1 %a.nneg)
%b.nneg = icmp sgt i64 %b, -1
call void @llvm.assume(i1 %b.nneg)
%add = add nsw i64 %a, %b
%gep = getelementptr i32, ptr %ptr, i64 %add
ret ptr %gep
}
define ptr @gep_inbounds_add_not_nsw_nonneg(ptr %ptr, i64 %a, i64 %b) {
; CHECK-LABEL: define ptr @gep_inbounds_add_not_nsw_nonneg(
; CHECK-SAME: ptr [[PTR:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
; CHECK-NEXT: [[A_NNEG:%.*]] = icmp sgt i64 [[A]], -1
; CHECK-NEXT: call void @llvm.assume(i1 [[A_NNEG]])
; CHECK-NEXT: [[B_NNEG:%.*]] = icmp sgt i64 [[B]], -1
; CHECK-NEXT: call void @llvm.assume(i1 [[B_NNEG]])
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[PTR]], i64 [[A]]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[TMP1]], i64 [[B]]
; CHECK-NEXT: ret ptr [[GEP]]
;
%a.nneg = icmp sgt i64 %a, -1
call void @llvm.assume(i1 %a.nneg)
%b.nneg = icmp sgt i64 %b, -1
call void @llvm.assume(i1 %b.nneg)
%add = add i64 %a, %b
%gep = getelementptr inbounds i32, ptr %ptr, i64 %add
ret ptr %gep
}
define ptr @gep_inbounds_sext_add_nonneg(ptr %ptr, i32 %a) {
; CHECK-LABEL: define ptr @gep_inbounds_sext_add_nonneg(
; CHECK-SAME: ptr [[PTR:%.*]], i32 [[A:%.*]]) {
; CHECK-NEXT: [[A_NNEG:%.*]] = icmp sgt i32 [[A]], -1
; CHECK-NEXT: call void @llvm.assume(i1 [[A_NNEG]])
; CHECK-NEXT: [[TMP1:%.*]] = zext nneg i32 [[A]] to i64
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw i32, ptr [[PTR]], i64 [[TMP1]]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP2]], i64 40
; CHECK-NEXT: ret ptr [[GEP]]
;
%a.nneg = icmp sgt i32 %a, -1
call void @llvm.assume(i1 %a.nneg)
%add = add nsw i32 %a, 10
%idx = sext i32 %add to i64
%gep = getelementptr inbounds i32, ptr %ptr, i64 %idx
ret ptr %gep
}
define ptr @gep_inbounds_sext_addlike_nonneg(ptr %ptr, i32 %a) {
; CHECK-LABEL: define ptr @gep_inbounds_sext_addlike_nonneg(
; CHECK-SAME: ptr [[PTR:%.*]], i32 [[A:%.*]]) {
; CHECK-NEXT: [[A_NNEG:%.*]] = icmp sgt i32 [[A]], -1
; CHECK-NEXT: call void @llvm.assume(i1 [[A_NNEG]])
; CHECK-NEXT: [[IDX:%.*]] = zext nneg i32 [[A]] to i64
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds nuw i32, ptr [[PTR]], i64 [[IDX]]
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr inbounds nuw i8, ptr [[GEP]], i64 40
; CHECK-NEXT: ret ptr [[GEP1]]
;
%a.nneg = icmp sgt i32 %a, -1
call void @llvm.assume(i1 %a.nneg)
%add = or disjoint i32 %a, 10
%idx = sext i32 %add to i64
%gep = getelementptr inbounds i32, ptr %ptr, i64 %idx
ret ptr %gep
}
define ptr @gep_inbounds_sext_add_not_nonneg_1(ptr %ptr, i32 %a) {
; CHECK-LABEL: define ptr @gep_inbounds_sext_add_not_nonneg_1(
; CHECK-SAME: ptr [[PTR:%.*]], i32 [[A:%.*]]) {
; CHECK-NEXT: [[A_NNEG:%.*]] = icmp sgt i32 [[A]], -1
; CHECK-NEXT: call void @llvm.assume(i1 [[A_NNEG]])
; CHECK-NEXT: [[TMP1:%.*]] = zext nneg i32 [[A]] to i64
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr i32, ptr [[PTR]], i64 [[TMP1]]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[TMP2]], i64 -40
; CHECK-NEXT: ret ptr [[GEP]]
;
%a.nneg = icmp sgt i32 %a, -1
call void @llvm.assume(i1 %a.nneg)
%add = add nsw i32 %a, -10
%idx = sext i32 %add to i64
%gep = getelementptr inbounds i32, ptr %ptr, i64 %idx
ret ptr %gep
}
define ptr @gep_inbounds_sext_add_not_nonneg_2(ptr %ptr, i32 %a) {
; CHECK-LABEL: define ptr @gep_inbounds_sext_add_not_nonneg_2(
; CHECK-SAME: ptr [[PTR:%.*]], i32 [[A:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[A]] to i64
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr i32, ptr [[PTR]], i64 [[TMP1]]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[TMP2]], i64 40
; CHECK-NEXT: ret ptr [[GEP]]
;
%add = add nsw i32 %a, 10
%idx = sext i32 %add to i64
%gep = getelementptr inbounds i32, ptr %ptr, i64 %idx
ret ptr %gep
}
define ptr @gep_not_inbounds_sext_add_nonneg(ptr %ptr, i32 %a) {
; CHECK-LABEL: define ptr @gep_not_inbounds_sext_add_nonneg(
; CHECK-SAME: ptr [[PTR:%.*]], i32 [[A:%.*]]) {
; CHECK-NEXT: [[A_NNEG:%.*]] = icmp sgt i32 [[A]], -1
; CHECK-NEXT: call void @llvm.assume(i1 [[A_NNEG]])
; CHECK-NEXT: [[TMP1:%.*]] = zext nneg i32 [[A]] to i64
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr i32, ptr [[PTR]], i64 [[TMP1]]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[TMP2]], i64 40
; CHECK-NEXT: ret ptr [[GEP]]
;
%a.nneg = icmp sgt i32 %a, -1
call void @llvm.assume(i1 %a.nneg)
%add = add nsw i32 %a, 10
%idx = sext i32 %add to i64
%gep = getelementptr i32, ptr %ptr, i64 %idx
ret ptr %gep
}