D92270 updated constant expression folding to fold inbounds GEP to poison if the base is undef. Apply the same logic to SimplifyGEPInst. The justification is that we can choose an out-of-bounds pointer as base pointer. Reviewed By: nikic, lebedev.ri Differential Revision: https://reviews.llvm.org/D117015
361 lines
12 KiB
LLVM
361 lines
12 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
|
; RUN: opt -S -instsimplify < %s | FileCheck %s
|
|
|
|
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
|
|
|
|
%struct.A = type { [7 x i8] }
|
|
|
|
define %struct.A* @test1(%struct.A* %b, %struct.A* %e) {
|
|
; CHECK-LABEL: @test1(
|
|
; CHECK-NEXT: [[E_PTR:%.*]] = ptrtoint %struct.A* [[E:%.*]] to i64
|
|
; CHECK-NEXT: [[B_PTR:%.*]] = ptrtoint %struct.A* [[B:%.*]] to i64
|
|
; CHECK-NEXT: [[SUB:%.*]] = sub i64 [[E_PTR]], [[B_PTR]]
|
|
; CHECK-NEXT: [[SDIV:%.*]] = sdiv exact i64 [[SUB]], 7
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [[STRUCT_A:%.*]], %struct.A* [[B]], i64 [[SDIV]]
|
|
; CHECK-NEXT: ret %struct.A* [[GEP]]
|
|
;
|
|
%e_ptr = ptrtoint %struct.A* %e to i64
|
|
%b_ptr = ptrtoint %struct.A* %b to i64
|
|
%sub = sub i64 %e_ptr, %b_ptr
|
|
%sdiv = sdiv exact i64 %sub, 7
|
|
%gep = getelementptr inbounds %struct.A, %struct.A* %b, i64 %sdiv
|
|
ret %struct.A* %gep
|
|
}
|
|
|
|
define i8* @test2(i8* %b, i8* %e) {
|
|
; CHECK-LABEL: @test2(
|
|
; CHECK-NEXT: [[E_PTR:%.*]] = ptrtoint i8* [[E:%.*]] to i64
|
|
; CHECK-NEXT: [[B_PTR:%.*]] = ptrtoint i8* [[B:%.*]] to i64
|
|
; CHECK-NEXT: [[SUB:%.*]] = sub i64 [[E_PTR]], [[B_PTR]]
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, i8* [[B]], i64 [[SUB]]
|
|
; CHECK-NEXT: ret i8* [[GEP]]
|
|
;
|
|
%e_ptr = ptrtoint i8* %e to i64
|
|
%b_ptr = ptrtoint i8* %b to i64
|
|
%sub = sub i64 %e_ptr, %b_ptr
|
|
%gep = getelementptr inbounds i8, i8* %b, i64 %sub
|
|
ret i8* %gep
|
|
}
|
|
|
|
define i64* @test3(i64* %b, i64* %e) {
|
|
; CHECK-LABEL: @test3(
|
|
; CHECK-NEXT: [[E_PTR:%.*]] = ptrtoint i64* [[E:%.*]] to i64
|
|
; CHECK-NEXT: [[B_PTR:%.*]] = ptrtoint i64* [[B:%.*]] to i64
|
|
; CHECK-NEXT: [[SUB:%.*]] = sub i64 [[E_PTR]], [[B_PTR]]
|
|
; CHECK-NEXT: [[ASHR:%.*]] = ashr exact i64 [[SUB]], 3
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i64, i64* [[B]], i64 [[ASHR]]
|
|
; CHECK-NEXT: ret i64* [[GEP]]
|
|
;
|
|
%e_ptr = ptrtoint i64* %e to i64
|
|
%b_ptr = ptrtoint i64* %b to i64
|
|
%sub = sub i64 %e_ptr, %b_ptr
|
|
%ashr = ashr exact i64 %sub, 3
|
|
%gep = getelementptr inbounds i64, i64* %b, i64 %ashr
|
|
ret i64* %gep
|
|
}
|
|
|
|
; The following tests should not be folded to null, because this would
|
|
; lose provenance of the base pointer %b.
|
|
|
|
define %struct.A* @test4(%struct.A* %b) {
|
|
; CHECK-LABEL: @test4(
|
|
; CHECK-NEXT: [[B_PTR:%.*]] = ptrtoint %struct.A* [[B:%.*]] to i64
|
|
; CHECK-NEXT: [[SUB:%.*]] = sub i64 0, [[B_PTR]]
|
|
; CHECK-NEXT: [[SDIV:%.*]] = sdiv exact i64 [[SUB]], 7
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr [[STRUCT_A:%.*]], %struct.A* [[B]], i64 [[SDIV]]
|
|
; CHECK-NEXT: ret %struct.A* [[GEP]]
|
|
;
|
|
%b_ptr = ptrtoint %struct.A* %b to i64
|
|
%sub = sub i64 0, %b_ptr
|
|
%sdiv = sdiv exact i64 %sub, 7
|
|
%gep = getelementptr %struct.A, %struct.A* %b, i64 %sdiv
|
|
ret %struct.A* %gep
|
|
}
|
|
|
|
define %struct.A* @test4_inbounds(%struct.A* %b) {
|
|
; CHECK-LABEL: @test4_inbounds(
|
|
; CHECK-NEXT: [[B_PTR:%.*]] = ptrtoint %struct.A* [[B:%.*]] to i64
|
|
; CHECK-NEXT: [[SUB:%.*]] = sub i64 0, [[B_PTR]]
|
|
; CHECK-NEXT: [[SDIV:%.*]] = sdiv exact i64 [[SUB]], 7
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [[STRUCT_A:%.*]], %struct.A* [[B]], i64 [[SDIV]]
|
|
; CHECK-NEXT: ret %struct.A* [[GEP]]
|
|
;
|
|
%b_ptr = ptrtoint %struct.A* %b to i64
|
|
%sub = sub i64 0, %b_ptr
|
|
%sdiv = sdiv exact i64 %sub, 7
|
|
%gep = getelementptr inbounds %struct.A, %struct.A* %b, i64 %sdiv
|
|
ret %struct.A* %gep
|
|
}
|
|
|
|
define i8* @test5(i8* %b) {
|
|
; CHECK-LABEL: @test5(
|
|
; CHECK-NEXT: [[B_PTR:%.*]] = ptrtoint i8* [[B:%.*]] to i64
|
|
; CHECK-NEXT: [[SUB:%.*]] = sub i64 0, [[B_PTR]]
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, i8* [[B]], i64 [[SUB]]
|
|
; CHECK-NEXT: ret i8* [[GEP]]
|
|
;
|
|
%b_ptr = ptrtoint i8* %b to i64
|
|
%sub = sub i64 0, %b_ptr
|
|
%gep = getelementptr i8, i8* %b, i64 %sub
|
|
ret i8* %gep
|
|
}
|
|
|
|
define i8* @test5_inbounds(i8* %b) {
|
|
; CHECK-LABEL: @test5_inbounds(
|
|
; CHECK-NEXT: [[B_PTR:%.*]] = ptrtoint i8* [[B:%.*]] to i64
|
|
; CHECK-NEXT: [[SUB:%.*]] = sub i64 0, [[B_PTR]]
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, i8* [[B]], i64 [[SUB]]
|
|
; CHECK-NEXT: ret i8* [[GEP]]
|
|
;
|
|
%b_ptr = ptrtoint i8* %b to i64
|
|
%sub = sub i64 0, %b_ptr
|
|
%gep = getelementptr inbounds i8, i8* %b, i64 %sub
|
|
ret i8* %gep
|
|
}
|
|
|
|
define i64* @test6(i64* %b) {
|
|
; CHECK-LABEL: @test6(
|
|
; CHECK-NEXT: [[B_PTR:%.*]] = ptrtoint i64* [[B:%.*]] to i64
|
|
; CHECK-NEXT: [[SUB:%.*]] = sub i64 0, [[B_PTR]]
|
|
; CHECK-NEXT: [[ASHR:%.*]] = ashr exact i64 [[SUB]], 3
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i64, i64* [[B]], i64 [[ASHR]]
|
|
; CHECK-NEXT: ret i64* [[GEP]]
|
|
;
|
|
%b_ptr = ptrtoint i64* %b to i64
|
|
%sub = sub i64 0, %b_ptr
|
|
%ashr = ashr exact i64 %sub, 3
|
|
%gep = getelementptr i64, i64* %b, i64 %ashr
|
|
ret i64* %gep
|
|
}
|
|
|
|
define i64* @test6_inbounds(i64* %b) {
|
|
; CHECK-LABEL: @test6_inbounds(
|
|
; CHECK-NEXT: [[B_PTR:%.*]] = ptrtoint i64* [[B:%.*]] to i64
|
|
; CHECK-NEXT: [[SUB:%.*]] = sub i64 0, [[B_PTR]]
|
|
; CHECK-NEXT: [[ASHR:%.*]] = ashr exact i64 [[SUB]], 3
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i64, i64* [[B]], i64 [[ASHR]]
|
|
; CHECK-NEXT: ret i64* [[GEP]]
|
|
;
|
|
%b_ptr = ptrtoint i64* %b to i64
|
|
%sub = sub i64 0, %b_ptr
|
|
%ashr = ashr exact i64 %sub, 3
|
|
%gep = getelementptr inbounds i64, i64* %b, i64 %ashr
|
|
ret i64* %gep
|
|
}
|
|
|
|
define i8* @test7(i8* %b, i8** %e) {
|
|
; CHECK-LABEL: @test7(
|
|
; CHECK-NEXT: [[E_PTR:%.*]] = ptrtoint i8** [[E:%.*]] to i64
|
|
; CHECK-NEXT: [[B_PTR:%.*]] = ptrtoint i8* [[B:%.*]] to i64
|
|
; CHECK-NEXT: [[SUB:%.*]] = sub i64 [[E_PTR]], [[B_PTR]]
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, i8* [[B]], i64 [[SUB]]
|
|
; CHECK-NEXT: ret i8* [[GEP]]
|
|
;
|
|
%e_ptr = ptrtoint i8** %e to i64
|
|
%b_ptr = ptrtoint i8* %b to i64
|
|
%sub = sub i64 %e_ptr, %b_ptr
|
|
%gep = getelementptr inbounds i8, i8* %b, i64 %sub
|
|
ret i8* %gep
|
|
}
|
|
|
|
define i64* @undef_inbounds_var_idx(i64 %idx) {
|
|
; CHECK-LABEL: @undef_inbounds_var_idx(
|
|
; CHECK-NEXT: ret i64* poison
|
|
;
|
|
%el = getelementptr inbounds i64, i64* undef, i64 %idx
|
|
ret i64* %el
|
|
}
|
|
|
|
define i64* @undef_no_inbounds_var_idx(i64 %idx) {
|
|
; CHECK-LABEL: @undef_no_inbounds_var_idx(
|
|
; CHECK-NEXT: ret i64* undef
|
|
;
|
|
%el = getelementptr i64, i64* undef, i64 %idx
|
|
ret i64* %el
|
|
}
|
|
|
|
define <8 x i64*> @undef_vec1() {
|
|
; CHECK-LABEL: @undef_vec1(
|
|
; CHECK-NEXT: ret <8 x i64*> poison
|
|
;
|
|
%el = getelementptr inbounds i64, i64* undef, <8 x i64> undef
|
|
ret <8 x i64*> %el
|
|
}
|
|
|
|
define <8 x i64*> @undef_vec2() {
|
|
; CHECK-LABEL: @undef_vec2(
|
|
; CHECK-NEXT: ret <8 x i64*> undef
|
|
;
|
|
%el = getelementptr i64, <8 x i64*> undef, <8 x i64> undef
|
|
ret <8 x i64*> %el
|
|
}
|
|
|
|
; Check ConstantExpr::getGetElementPtr() using ElementCount for size queries - begin.
|
|
|
|
; Constant ptr
|
|
|
|
define i32* @ptr_idx_scalar() {
|
|
; CHECK-LABEL: @ptr_idx_scalar(
|
|
; CHECK-NEXT: ret i32* inttoptr (i64 4 to i32*)
|
|
;
|
|
%gep = getelementptr <4 x i32>, <4 x i32>* null, i64 0, i64 1
|
|
ret i32* %gep
|
|
}
|
|
|
|
define <2 x i32*> @ptr_idx_vector() {
|
|
; CHECK-LABEL: @ptr_idx_vector(
|
|
; CHECK-NEXT: ret <2 x i32*> getelementptr (i32, i32* null, <2 x i64> <i64 1, i64 1>)
|
|
;
|
|
%gep = getelementptr i32, i32* null, <2 x i64> <i64 1, i64 1>
|
|
ret <2 x i32*> %gep
|
|
}
|
|
|
|
define <4 x i32*> @ptr_idx_mix_scalar_vector(){
|
|
; CHECK-LABEL: @ptr_idx_mix_scalar_vector(
|
|
; CHECK-NEXT: ret <4 x i32*> getelementptr ([42 x [3 x i32]], [42 x [3 x i32]]* null, <4 x i64> zeroinitializer, <4 x i64> <i64 0, i64 1, i64 2, i64 3>, <4 x i64> zeroinitializer)
|
|
;
|
|
%gep = getelementptr [42 x [3 x i32]], [42 x [3 x i32]]* null, i64 0, <4 x i64> <i64 0, i64 1, i64 2, i64 3>, i64 0
|
|
ret <4 x i32*> %gep
|
|
}
|
|
|
|
; Constant vector
|
|
|
|
define <4 x i32*> @vector_idx_scalar() {
|
|
; CHECK-LABEL: @vector_idx_scalar(
|
|
; CHECK-NEXT: ret <4 x i32*> getelementptr (i32, <4 x i32*> zeroinitializer, <4 x i64> <i64 1, i64 1, i64 1, i64 1>)
|
|
;
|
|
%gep = getelementptr i32, <4 x i32*> zeroinitializer, i64 1
|
|
ret <4 x i32*> %gep
|
|
}
|
|
|
|
define <4 x i32*> @vector_idx_vector() {
|
|
; CHECK-LABEL: @vector_idx_vector(
|
|
; CHECK-NEXT: ret <4 x i32*> getelementptr (i32, <4 x i32*> zeroinitializer, <4 x i64> <i64 1, i64 1, i64 1, i64 1>)
|
|
;
|
|
%gep = getelementptr i32, <4 x i32*> zeroinitializer, <4 x i64> <i64 1, i64 1, i64 1, i64 1>
|
|
ret <4 x i32*> %gep
|
|
}
|
|
|
|
%struct = type { double, float }
|
|
define <4 x float*> @vector_idx_mix_scalar_vector() {
|
|
; CHECK-LABEL: @vector_idx_mix_scalar_vector(
|
|
; CHECK-NEXT: ret <4 x float*> getelementptr ([[STRUCT:%.*]], <4 x %struct*> zeroinitializer, <4 x i64> zeroinitializer, i32 1)
|
|
;
|
|
%gep = getelementptr %struct, <4 x %struct*> zeroinitializer, i32 0, <4 x i32> <i32 1, i32 1, i32 1, i32 1>
|
|
ret <4 x float*> %gep
|
|
}
|
|
|
|
; Constant scalable
|
|
|
|
define <vscale x 4 x i32*> @scalable_idx_scalar() {
|
|
; CHECK-LABEL: @scalable_idx_scalar(
|
|
; CHECK-NEXT: ret <vscale x 4 x i32*> getelementptr (i32, <vscale x 4 x i32*> zeroinitializer, <vscale x 4 x i64> shufflevector (<vscale x 4 x i64> insertelement (<vscale x 4 x i64> poison, i64 1, i32 0), <vscale x 4 x i64> poison, <vscale x 4 x i32> zeroinitializer))
|
|
;
|
|
%gep = getelementptr i32, <vscale x 4 x i32*> zeroinitializer, i64 1
|
|
ret <vscale x 4 x i32*> %gep
|
|
}
|
|
|
|
define <vscale x 4 x float*> @scalable_vector_idx_mix_scalar_vector() {
|
|
; CHECK-LABEL: @scalable_vector_idx_mix_scalar_vector(
|
|
; CHECK-NEXT: ret <vscale x 4 x float*> getelementptr ([[STRUCT:%.*]], <vscale x 4 x %struct*> zeroinitializer, <vscale x 4 x i64> zeroinitializer, i32 1)
|
|
;
|
|
%gep = getelementptr %struct, <vscale x 4 x %struct*> zeroinitializer, i32 0, i32 1
|
|
ret <vscale x 4 x float*> %gep
|
|
}
|
|
|
|
define <vscale x 2 x i64*> @ptr_idx_mix_scalar_scalable_vector() {
|
|
; CHECK-LABEL: @ptr_idx_mix_scalar_scalable_vector(
|
|
; CHECK-NEXT: ret <vscale x 2 x i64*> zeroinitializer
|
|
;
|
|
%v = getelementptr [2 x i64], [2 x i64]* null, i64 0, <vscale x 2 x i64> zeroinitializer
|
|
ret <vscale x 2 x i64*> %v
|
|
}
|
|
|
|
; Check ConstantExpr::getGetElementPtr() using ElementCount for size queries - end.
|
|
|
|
define i8* @poison() {
|
|
; CHECK-LABEL: @poison(
|
|
; CHECK-NEXT: ret i8* poison
|
|
;
|
|
%v = getelementptr i8, i8* poison, i64 1
|
|
ret i8* %v
|
|
}
|
|
|
|
define i8* @poison2(i8* %baseptr) {
|
|
; CHECK-LABEL: @poison2(
|
|
; CHECK-NEXT: ret i8* poison
|
|
;
|
|
%v = getelementptr i8, i8* %baseptr, i64 poison
|
|
ret i8* %v
|
|
}
|
|
|
|
define i8* @D98611_1(i8* %c1, i64 %offset) {
|
|
; CHECK-LABEL: @D98611_1(
|
|
; CHECK-NEXT: [[C2:%.*]] = getelementptr inbounds i8, i8* [[C1:%.*]], i64 [[OFFSET:%.*]]
|
|
; CHECK-NEXT: ret i8* [[C2]]
|
|
;
|
|
%c2 = getelementptr inbounds i8, i8* %c1, i64 %offset
|
|
%ptrtoint1 = ptrtoint i8* %c1 to i64
|
|
%ptrtoint2 = ptrtoint i8* %c2 to i64
|
|
%sub = sub i64 %ptrtoint2, %ptrtoint1
|
|
%gep = getelementptr inbounds i8, i8* %c1, i64 %sub
|
|
ret i8* %gep
|
|
}
|
|
|
|
define %struct.A* @D98611_2(%struct.A* %c1, i64 %offset) {
|
|
; CHECK-LABEL: @D98611_2(
|
|
; CHECK-NEXT: [[C2:%.*]] = getelementptr inbounds [[STRUCT_A:%.*]], %struct.A* [[C1:%.*]], i64 [[OFFSET:%.*]]
|
|
; CHECK-NEXT: ret %struct.A* [[C2]]
|
|
;
|
|
%c2 = getelementptr inbounds %struct.A, %struct.A* %c1, i64 %offset
|
|
%ptrtoint1 = ptrtoint %struct.A* %c1 to i64
|
|
%ptrtoint2 = ptrtoint %struct.A* %c2 to i64
|
|
%sub = sub i64 %ptrtoint2, %ptrtoint1
|
|
%sdiv = sdiv exact i64 %sub, 7
|
|
%gep = getelementptr inbounds %struct.A, %struct.A* %c1, i64 %sdiv
|
|
ret %struct.A* %gep
|
|
}
|
|
|
|
define i32* @D98611_3(i32* %c1, i64 %offset) {
|
|
; CHECK-LABEL: @D98611_3(
|
|
; CHECK-NEXT: [[C2:%.*]] = getelementptr inbounds i32, i32* [[C1:%.*]], i64 [[OFFSET:%.*]]
|
|
; CHECK-NEXT: ret i32* [[C2]]
|
|
;
|
|
%c2 = getelementptr inbounds i32, i32* %c1, i64 %offset
|
|
%ptrtoint1 = ptrtoint i32* %c1 to i64
|
|
%ptrtoint2 = ptrtoint i32* %c2 to i64
|
|
%sub = sub i64 %ptrtoint2, %ptrtoint1
|
|
%ashr = ashr exact i64 %sub, 2
|
|
%gep = getelementptr inbounds i32, i32* %c1, i64 %ashr
|
|
ret i32* %gep
|
|
}
|
|
|
|
define <8 x i32*> @gep_vector_index_op2_poison([144 x i32]* %ptr) {
|
|
; CHECK-LABEL: @gep_vector_index_op2_poison(
|
|
; CHECK-NEXT: ret <8 x i32*> poison
|
|
;
|
|
%res = getelementptr inbounds [144 x i32], [144 x i32]* %ptr, i64 0, <8 x i64> poison
|
|
ret <8 x i32*> %res
|
|
}
|
|
|
|
%t.1 = type { i32, [144 x i32] }
|
|
|
|
define <8 x i32*> @gep_vector_index_op3_poison(%t.1* %ptr) {
|
|
; CHECK-LABEL: @gep_vector_index_op3_poison(
|
|
; CHECK-NEXT: ret <8 x i32*> poison
|
|
;
|
|
%res = getelementptr inbounds %t.1, %t.1* %ptr, i64 0, i32 1, <8 x i64> poison
|
|
ret <8 x i32*> %res
|
|
}
|
|
|
|
%t.2 = type { i32, i32 }
|
|
%t.3 = type { i32, [144 x %t.2 ] }
|
|
|
|
define <8 x i32*> @gep_vector_index_op3_poison_constant_index_afterwards(%t.3* %ptr) {
|
|
; CHECK-LABEL: @gep_vector_index_op3_poison_constant_index_afterwards(
|
|
; CHECK-NEXT: ret <8 x i32*> poison
|
|
;
|
|
%res = getelementptr inbounds %t.3, %t.3* %ptr, i64 0, i32 1, <8 x i64> poison, i32 1
|
|
ret <8 x i32*> %res
|
|
}
|