diff --git a/llvm/test/Transforms/InstCombine/ptrtoint-nullgep.ll b/llvm/test/Transforms/InstCombine/ptrtoint-nullgep.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/ptrtoint-nullgep.ll @@ -0,0 +1,553 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes +; Check that we can fold operations with (gep null) inputs. +; Note: the LLParser already does some constant folding, check that output first: +; RUN: opt -S -passes=verify < %s | FileCheck %s --check-prefixes=ALL,LLPARSER +; We should be able to fold almost everything in InstSimplify other than the final test which requries InstCombine +; RUN: opt -S -passes=instsimplify < %s | FileCheck %s --allow-unused-prefixes --check-prefixes=ALL,CHECK,INTEGRAL,INSTSIMPLIFY,INTEGRAL-INSTSIMPLIFY +; RUN: opt -S -passes=instcombine < %s | FileCheck %s --allow-unused-prefixes --check-prefixes=ALL,CHECK,INTEGRAL,INSTCOMBINE,INTEGRAL-INSTCOMBINE +; Non-integral pointers limit certain transformations on pointers: +; RUN: sed -e 's/p:64:64:64:64/p:64:64:64:64-ni:1/g' %s | opt -S -passes=instsimplify | \ +; RUN: FileCheck %s --allow-unused-prefixes --check-prefixes=ALL,CHECK,NONINTEGRAL,INSTSIMPLIFY,NONINTEGRAL-INSTSIMPLIFY +; RUN: sed -e 's/p:64:64:64:64/p:64:64:64:64-ni:1/g' %s | opt -S -passes=instcombine | \ +; RUN: FileCheck %s --allow-unused-prefixes --check-prefixes=ALL,CHECK,NONINTEGRAL,INSTCOMBINE,NONINTEGRAL-INSTCOMBINE +target datalayout = "p:64:64:64:64" + +declare void @use_i64(i64) +declare void @use_ptr(i8 addrspace(1)*) + +define i64 @constant_fold_ptrtoint_gep_zero() { +; ALL-LABEL: define {{[^@]+}}@constant_fold_ptrtoint_gep_zero() { +; ALL-NEXT: ret i64 0 +; + ret i64 ptrtoint (i32 addrspace(1)* getelementptr (i32, i32 addrspace(1)* null, i64 0) to i64) +} +define i64 @constant_fold_ptrtoint_gep_nonzero() { +; LLPARSER-LABEL: define {{[^@]+}}@constant_fold_ptrtoint_gep_nonzero() { +; LLPARSER-NEXT: ret i64 ptrtoint (i32 addrspace(1)* getelementptr (i32, i32 addrspace(1)* null, i64 1234) to i64) +; +; INTEGRAL-INSTSIMPLIFY-LABEL: define {{[^@]+}}@constant_fold_ptrtoint_gep_nonzero() { +; INTEGRAL-INSTSIMPLIFY-NEXT: ret i64 ptrtoint (i32 addrspace(1)* getelementptr (i32, i32 addrspace(1)* null, i64 1234) to i64) +; +; INTEGRAL-INSTCOMBINE-LABEL: define {{[^@]+}}@constant_fold_ptrtoint_gep_nonzero() { +; INTEGRAL-INSTCOMBINE-NEXT: ret i64 4936 +; +; NONINTEGRAL-LABEL: define {{[^@]+}}@constant_fold_ptrtoint_gep_nonzero() { +; NONINTEGRAL-NEXT: ret i64 ptrtoint (i32 addrspace(1)* getelementptr (i32, i32 addrspace(1)* null, i64 1234) to i64) +; + ret i64 ptrtoint (i32 addrspace(1)* getelementptr (i32, i32 addrspace(1)* null, i64 1234) to i64) +} + +define i64 @constant_fold_ptrtoint_gep_zero_inbounds() { +; ALL-LABEL: define {{[^@]+}}@constant_fold_ptrtoint_gep_zero_inbounds() { +; ALL-NEXT: ret i64 0 +; + ret i64 ptrtoint (i32 addrspace(1)* getelementptr inbounds (i32, i32 addrspace(1)* null, i64 0) to i64) +} + +; In theory we could fold this to poison/null, but that would break offsetof +; implementations that don't use __builtin_offsetof. +; TODO: should Clang special case ((INTEGER)&((TYPE *)0)->MEMBER) to emit a non-inbounds GEP? +define i64 @constant_fold_ptrtoint_gep_nonzero_inbounds() { +; LLPARSER-LABEL: define {{[^@]+}}@constant_fold_ptrtoint_gep_nonzero_inbounds() { +; LLPARSER-NEXT: ret i64 ptrtoint (i32 addrspace(1)* getelementptr inbounds (i32, i32 addrspace(1)* null, i64 1234) to i64) +; +; INTEGRAL-INSTSIMPLIFY-LABEL: define {{[^@]+}}@constant_fold_ptrtoint_gep_nonzero_inbounds() { +; INTEGRAL-INSTSIMPLIFY-NEXT: ret i64 ptrtoint (i32 addrspace(1)* getelementptr inbounds (i32, i32 addrspace(1)* null, i64 1234) to i64) +; +; INTEGRAL-INSTCOMBINE-LABEL: define {{[^@]+}}@constant_fold_ptrtoint_gep_nonzero_inbounds() { +; INTEGRAL-INSTCOMBINE-NEXT: ret i64 4936 +; +; NONINTEGRAL-LABEL: define {{[^@]+}}@constant_fold_ptrtoint_gep_nonzero_inbounds() { +; NONINTEGRAL-NEXT: ret i64 ptrtoint (i32 addrspace(1)* getelementptr inbounds (i32, i32 addrspace(1)* null, i64 1234) to i64) +; + ret i64 ptrtoint (i32 addrspace(1)* getelementptr inbounds (i32, i32 addrspace(1)* null, i64 1234) to i64) +} + +; Check all combinations of inbounds+non-inbounds GEP with the outer GEP having a non-zero offset +define void @constant_fold_ptrtoint_of_gep_of_nullgep() { +; LLPARSER-LABEL: define {{[^@]+}}@constant_fold_ptrtoint_of_gep_of_nullgep() { +; LLPARSER-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; LLPARSER-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; LLPARSER-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; LLPARSER-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; LLPARSER-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; LLPARSER-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; LLPARSER-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; LLPARSER-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; LLPARSER-NEXT: call void @use_i64(i64 0) +; LLPARSER-NEXT: call void @use_i64(i64 0) +; LLPARSER-NEXT: call void @use_i64(i64 0) +; LLPARSER-NEXT: call void @use_i64(i64 0) +; LLPARSER-NEXT: ret void +; +; INTEGRAL-INSTSIMPLIFY-LABEL: define {{[^@]+}}@constant_fold_ptrtoint_of_gep_of_nullgep() { +; INTEGRAL-INSTSIMPLIFY-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; INTEGRAL-INSTSIMPLIFY-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; INTEGRAL-INSTSIMPLIFY-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; INTEGRAL-INSTSIMPLIFY-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; INTEGRAL-INSTSIMPLIFY-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; INTEGRAL-INSTSIMPLIFY-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; INTEGRAL-INSTSIMPLIFY-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; INTEGRAL-INSTSIMPLIFY-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; INTEGRAL-INSTSIMPLIFY-NEXT: call void @use_i64(i64 0) +; INTEGRAL-INSTSIMPLIFY-NEXT: call void @use_i64(i64 0) +; INTEGRAL-INSTSIMPLIFY-NEXT: call void @use_i64(i64 0) +; INTEGRAL-INSTSIMPLIFY-NEXT: call void @use_i64(i64 0) +; INTEGRAL-INSTSIMPLIFY-NEXT: ret void +; +; INTEGRAL-INSTCOMBINE-LABEL: define {{[^@]+}}@constant_fold_ptrtoint_of_gep_of_nullgep() { +; INTEGRAL-INSTCOMBINE-NEXT: call void @use_i64(i64 1234) +; INTEGRAL-INSTCOMBINE-NEXT: call void @use_i64(i64 1234) +; INTEGRAL-INSTCOMBINE-NEXT: call void @use_i64(i64 1234) +; INTEGRAL-INSTCOMBINE-NEXT: call void @use_i64(i64 1234) +; INTEGRAL-INSTCOMBINE-NEXT: call void @use_i64(i64 1234) +; INTEGRAL-INSTCOMBINE-NEXT: call void @use_i64(i64 1234) +; INTEGRAL-INSTCOMBINE-NEXT: call void @use_i64(i64 1234) +; INTEGRAL-INSTCOMBINE-NEXT: call void @use_i64(i64 1234) +; INTEGRAL-INSTCOMBINE-NEXT: call void @use_i64(i64 0) +; INTEGRAL-INSTCOMBINE-NEXT: call void @use_i64(i64 0) +; INTEGRAL-INSTCOMBINE-NEXT: call void @use_i64(i64 0) +; INTEGRAL-INSTCOMBINE-NEXT: call void @use_i64(i64 0) +; INTEGRAL-INSTCOMBINE-NEXT: ret void +; +; NONINTEGRAL-LABEL: define {{[^@]+}}@constant_fold_ptrtoint_of_gep_of_nullgep() { +; NONINTEGRAL-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; NONINTEGRAL-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; NONINTEGRAL-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; NONINTEGRAL-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; NONINTEGRAL-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; NONINTEGRAL-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; NONINTEGRAL-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; NONINTEGRAL-NEXT: call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 1234) to i64)) +; NONINTEGRAL-NEXT: call void @use_i64(i64 0) +; NONINTEGRAL-NEXT: call void @use_i64(i64 0) +; NONINTEGRAL-NEXT: call void @use_i64(i64 0) +; NONINTEGRAL-NEXT: call void @use_i64(i64 0) +; NONINTEGRAL-NEXT: ret void +; + call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 0), i64 1234) to i64)) + call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 0), i64 1234) to i64)) + call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 0), i64 1234) to i64)) + call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 0), i64 1234) to i64)) + ; Same again but this time with the inner GEP using the non-zero offset + call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234), i64 0) to i64)) + call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234), i64 0) to i64)) + call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 1234), i64 0) to i64)) + call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 1234), i64 0) to i64)) + ; And finally with two constants that sum to zero + call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 -1), i64 1) to i64)) + call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 -1), i64 1) to i64)) + call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 -1), i64 1) to i64)) + call void @use_i64(i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 -1), i64 1) to i64)) + ret void +} + +; Another set of tests for instructions instead of constants +define i64 @fold_ptrtoint_nullgep_zero() { +; LLPARSER-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_zero() { +; LLPARSER-NEXT: [[OFFSET:%.*]] = add i64 0, 0 +; LLPARSER-NEXT: [[PTR:%.*]] = getelementptr i8, i8 addrspace(1)* null, i64 [[OFFSET]] +; LLPARSER-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; LLPARSER-NEXT: ret i64 [[RET]] +; +; CHECK-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_zero() { +; CHECK-NEXT: ret i64 0 +; + %offset = add i64 0, 0 + %ptr = getelementptr i8, i8 addrspace(1)* null, i64 %offset + %ret = ptrtoint i8 addrspace(1)* %ptr to i64 + ret i64 %ret +} + +define i64 @fold_ptrtoint_nullgep_zero_inbounds() { +; LLPARSER-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_zero_inbounds() { +; LLPARSER-NEXT: [[OFFSET:%.*]] = add i64 0, 0 +; LLPARSER-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* null, i64 [[OFFSET]] +; LLPARSER-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; LLPARSER-NEXT: ret i64 [[RET]] +; +; CHECK-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_zero_inbounds() { +; CHECK-NEXT: ret i64 0 +; + %offset = add i64 0, 0 + %ptr = getelementptr inbounds i8, i8 addrspace(1)* null, i64 %offset + %ret = ptrtoint i8 addrspace(1)* %ptr to i64 + ret i64 %ret +} + +define i64 @fold_ptrtoint_nullgep_nonzero() { +; LLPARSER-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_nonzero() { +; LLPARSER-NEXT: [[OFFSET:%.*]] = add i64 1234, 0 +; LLPARSER-NEXT: [[PTR:%.*]] = getelementptr i8, i8 addrspace(1)* null, i64 [[OFFSET]] +; LLPARSER-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; LLPARSER-NEXT: ret i64 [[RET]] +; +; INTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_nonzero() { +; INTEGRAL-NEXT: ret i64 1234 +; +; NONINTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_nonzero() { +; NONINTEGRAL-NEXT: ret i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 1234) to i64) +; + %offset = add i64 1234, 0 + %ptr = getelementptr i8, i8 addrspace(1)* null, i64 %offset + %ret = ptrtoint i8 addrspace(1)* %ptr to i64 + ret i64 %ret +} + +; Inbounds constant null-GEP with non-zero could be constant-folded to null/poison, +; but folding it to the value makes ((INTEGER)&((TYPE *)0)->MEMBER) work. +define i64 @fold_ptrtoint_nullgep_nonzero_inbounds() { +; LLPARSER-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_nonzero_inbounds() { +; LLPARSER-NEXT: [[OFFSET:%.*]] = add i64 1234, 0 +; LLPARSER-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* null, i64 [[OFFSET]] +; LLPARSER-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; LLPARSER-NEXT: ret i64 [[RET]] +; +; INTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_nonzero_inbounds() { +; INTEGRAL-NEXT: ret i64 1234 +; +; NONINTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_nonzero_inbounds() { +; NONINTEGRAL-NEXT: ret i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234) to i64) +; + %offset = add i64 1234, 0 + %ptr = getelementptr inbounds i8, i8 addrspace(1)* null, i64 %offset + %ret = ptrtoint i8 addrspace(1)* %ptr to i64 + ret i64 %ret +} + +; We should be able to fold ptrtoint(gep null, x) to x +define i64 @fold_ptrtoint_nullgep_variable(i64 %val) { +; ALL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable +; ALL-SAME: (i64 [[VAL:%.*]]) { +; ALL-NEXT: [[PTR:%.*]] = getelementptr i8, i8 addrspace(1)* null, i64 [[VAL]] +; ALL-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; ALL-NEXT: ret i64 [[RET]] +; + %ptr = getelementptr i8, i8 addrspace(1)* null, i64 %val + %ret = ptrtoint i8 addrspace(1)* %ptr to i64 + ret i64 %ret +} + +; Inbounds null-GEP with non-zero offset could be folded to poison/null. +define i64 @fold_ptrtoint_nullgep_variable_known_nonzero(i64 %val) { +; ALL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_known_nonzero +; ALL-SAME: (i64 [[VAL:%.*]]) { +; ALL-NEXT: [[NON_ZERO_OFFSET:%.*]] = or i64 [[VAL]], 1 +; ALL-NEXT: [[PTR:%.*]] = getelementptr i8, i8 addrspace(1)* null, i64 [[NON_ZERO_OFFSET]] +; ALL-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; ALL-NEXT: ret i64 [[RET]] +; + %non_zero_offset = or i64 %val, 1 + %ptr = getelementptr i8, i8 addrspace(1)* null, i64 %non_zero_offset + %ret = ptrtoint i8 addrspace(1)* %ptr to i64 + ret i64 %ret +} + +; This is only valid if %val is zero so we could fold the result to 0. +define i64 @fold_ptrtoint_nullgep_variable_inbounds(i64 %val) { +; ALL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_inbounds +; ALL-SAME: (i64 [[VAL:%.*]]) { +; ALL-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* null, i64 [[VAL]] +; ALL-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; ALL-NEXT: ret i64 [[RET]] +; + %ptr = getelementptr inbounds i8, i8 addrspace(1)* null, i64 %val + %ret = ptrtoint i8 addrspace(1)* %ptr to i64 + ret i64 %ret +} + +; A non-constant but known-non-zero GEP could be folded to poison/null +define i64 @fold_ptrtoint_nullgep_variable_known_nonzero_inbounds(i64 %val) { +; ALL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_known_nonzero_inbounds +; ALL-SAME: (i64 [[VAL:%.*]]) { +; ALL-NEXT: [[NON_ZERO_OFFSET:%.*]] = or i64 [[VAL]], 1 +; ALL-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* null, i64 [[NON_ZERO_OFFSET]] +; ALL-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; ALL-NEXT: ret i64 [[RET]] +; + %non_zero_offset = or i64 %val, 1 + %ptr = getelementptr inbounds i8, i8 addrspace(1)* null, i64 %non_zero_offset + %ret = ptrtoint i8 addrspace(1)* %ptr to i64 + ret i64 %ret +} + +; A non-constant but known-non-zero GEP could be folded to poison/null +define i64 @fold_ptrtoint_nullgep_variable_known_nonzero_inbounds_multiple_indices(i64 %val) { +; LLPARSER-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_known_nonzero_inbounds_multiple_indices +; LLPARSER-SAME: (i64 [[VAL:%.*]]) { +; LLPARSER-NEXT: [[NON_ZERO_OFFSET:%.*]] = or i64 [[VAL]], 1 +; LLPARSER-NEXT: [[PTR:%.*]] = getelementptr inbounds [2 x i8], [2 x i8] addrspace(1)* null, i64 [[NON_ZERO_OFFSET]], i32 1 +; LLPARSER-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; LLPARSER-NEXT: ret i64 [[RET]] +; +; INTEGRAL-INSTSIMPLIFY-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_known_nonzero_inbounds_multiple_indices +; INTEGRAL-INSTSIMPLIFY-SAME: (i64 [[VAL:%.*]]) { +; INTEGRAL-INSTSIMPLIFY-NEXT: [[NON_ZERO_OFFSET:%.*]] = or i64 [[VAL]], 1 +; INTEGRAL-INSTSIMPLIFY-NEXT: [[PTR:%.*]] = getelementptr inbounds [2 x i8], [2 x i8] addrspace(1)* null, i64 [[NON_ZERO_OFFSET]], i32 1 +; INTEGRAL-INSTSIMPLIFY-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; INTEGRAL-INSTSIMPLIFY-NEXT: ret i64 [[RET]] +; +; INTEGRAL-INSTCOMBINE-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_known_nonzero_inbounds_multiple_indices +; INTEGRAL-INSTCOMBINE-SAME: (i64 [[VAL:%.*]]) { +; INTEGRAL-INSTCOMBINE-NEXT: [[NON_ZERO_OFFSET:%.*]] = or i64 [[VAL]], 1 +; INTEGRAL-INSTCOMBINE-NEXT: [[PTR:%.*]] = getelementptr inbounds [2 x i8], [2 x i8] addrspace(1)* null, i64 [[NON_ZERO_OFFSET]], i64 1 +; INTEGRAL-INSTCOMBINE-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; INTEGRAL-INSTCOMBINE-NEXT: ret i64 [[RET]] +; +; NONINTEGRAL-INSTSIMPLIFY-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_known_nonzero_inbounds_multiple_indices +; NONINTEGRAL-INSTSIMPLIFY-SAME: (i64 [[VAL:%.*]]) { +; NONINTEGRAL-INSTSIMPLIFY-NEXT: [[NON_ZERO_OFFSET:%.*]] = or i64 [[VAL]], 1 +; NONINTEGRAL-INSTSIMPLIFY-NEXT: [[PTR:%.*]] = getelementptr inbounds [2 x i8], [2 x i8] addrspace(1)* null, i64 [[NON_ZERO_OFFSET]], i32 1 +; NONINTEGRAL-INSTSIMPLIFY-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; NONINTEGRAL-INSTSIMPLIFY-NEXT: ret i64 [[RET]] +; +; NONINTEGRAL-INSTCOMBINE-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_known_nonzero_inbounds_multiple_indices +; NONINTEGRAL-INSTCOMBINE-SAME: (i64 [[VAL:%.*]]) { +; NONINTEGRAL-INSTCOMBINE-NEXT: [[NON_ZERO_OFFSET:%.*]] = or i64 [[VAL]], 1 +; NONINTEGRAL-INSTCOMBINE-NEXT: [[PTR:%.*]] = getelementptr inbounds [2 x i8], [2 x i8] addrspace(1)* null, i64 [[NON_ZERO_OFFSET]], i64 1 +; NONINTEGRAL-INSTCOMBINE-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; NONINTEGRAL-INSTCOMBINE-NEXT: ret i64 [[RET]] +; + %non_zero_offset = or i64 %val, 1 + %ptr = getelementptr inbounds [2 x i8], [2 x i8] addrspace(1)* null, i64 %non_zero_offset, i32 1 + %ret = ptrtoint i8 addrspace(1)* %ptr to i64 + ret i64 %ret +} + +; We can't fold non-i8 GEPs in InstSimplify since that would require adding new arithmetic. +; TODO: handle this case in InstCombine +define i64 @fold_ptrtoint_nullgep_i32_variable(i64 %val) { +; ALL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_i32_variable +; ALL-SAME: (i64 [[VAL:%.*]]) { +; ALL-NEXT: [[PTR:%.*]] = getelementptr i32, i32 addrspace(1)* null, i64 [[VAL]] +; ALL-NEXT: [[RET:%.*]] = ptrtoint i32 addrspace(1)* [[PTR]] to i64 +; ALL-NEXT: ret i64 [[RET]] +; + %ptr = getelementptr i32, i32 addrspace(1)* null, i64 %val + %ret = ptrtoint i32 addrspace(1)* %ptr to i64 + ret i64 %ret +} + +; ptrtoint type does not match index type so requires requite a new trunc instruction +define i32 @fold_ptrtoint_nullgep_variable_trunc(i64 %val) { +; LLPARSER-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_trunc +; LLPARSER-SAME: (i64 [[VAL:%.*]]) { +; LLPARSER-NEXT: [[PTR:%.*]] = getelementptr i8, i8 addrspace(1)* null, i64 [[VAL]] +; LLPARSER-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i32 +; LLPARSER-NEXT: ret i32 [[RET]] +; +; INTEGRAL-INSTSIMPLIFY-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_trunc +; INTEGRAL-INSTSIMPLIFY-SAME: (i64 [[VAL:%.*]]) { +; INTEGRAL-INSTSIMPLIFY-NEXT: [[PTR:%.*]] = getelementptr i8, i8 addrspace(1)* null, i64 [[VAL]] +; INTEGRAL-INSTSIMPLIFY-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i32 +; INTEGRAL-INSTSIMPLIFY-NEXT: ret i32 [[RET]] +; +; INTEGRAL-INSTCOMBINE-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_trunc +; INTEGRAL-INSTCOMBINE-SAME: (i64 [[VAL:%.*]]) { +; INTEGRAL-INSTCOMBINE-NEXT: [[PTR:%.*]] = getelementptr i8, i8 addrspace(1)* null, i64 [[VAL]] +; INTEGRAL-INSTCOMBINE-NEXT: [[TMP1:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; INTEGRAL-INSTCOMBINE-NEXT: [[RET:%.*]] = trunc i64 [[TMP1]] to i32 +; INTEGRAL-INSTCOMBINE-NEXT: ret i32 [[RET]] +; +; NONINTEGRAL-INSTSIMPLIFY-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_trunc +; NONINTEGRAL-INSTSIMPLIFY-SAME: (i64 [[VAL:%.*]]) { +; NONINTEGRAL-INSTSIMPLIFY-NEXT: [[PTR:%.*]] = getelementptr i8, i8 addrspace(1)* null, i64 [[VAL]] +; NONINTEGRAL-INSTSIMPLIFY-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i32 +; NONINTEGRAL-INSTSIMPLIFY-NEXT: ret i32 [[RET]] +; +; NONINTEGRAL-INSTCOMBINE-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_trunc +; NONINTEGRAL-INSTCOMBINE-SAME: (i64 [[VAL:%.*]]) { +; NONINTEGRAL-INSTCOMBINE-NEXT: [[PTR:%.*]] = getelementptr i8, i8 addrspace(1)* null, i64 [[VAL]] +; NONINTEGRAL-INSTCOMBINE-NEXT: [[TMP1:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; NONINTEGRAL-INSTCOMBINE-NEXT: [[RET:%.*]] = trunc i64 [[TMP1]] to i32 +; NONINTEGRAL-INSTCOMBINE-NEXT: ret i32 [[RET]] +; + %ptr = getelementptr i8, i8 addrspace(1)* null, i64 %val + %ret = ptrtoint i8 addrspace(1)* %ptr to i32 + ret i32 %ret +} + +; For the following three tests, we could fold the result to poison/null since there is at least +; one inbounds GEP on null with a non-zero offset. +define i64 @fold_ptrtoint_zero_nullgep_of_nonzero_inbounds_nullgep() { +; LLPARSER-LABEL: define {{[^@]+}}@fold_ptrtoint_zero_nullgep_of_nonzero_inbounds_nullgep() { +; LLPARSER-NEXT: [[NONZERO_OFFSET:%.*]] = add i64 1234, 0 +; LLPARSER-NEXT: [[ZERO_OFFSET:%.*]] = sub i64 [[NONZERO_OFFSET]], 1234 +; LLPARSER-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* null, i64 [[NONZERO_OFFSET]] +; LLPARSER-NEXT: [[PTR2:%.*]] = getelementptr i8, i8 addrspace(1)* [[PTR]], i64 [[ZERO_OFFSET]] +; LLPARSER-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR2]] to i64 +; LLPARSER-NEXT: ret i64 [[RET]] +; +; INTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_zero_nullgep_of_nonzero_inbounds_nullgep() { +; INTEGRAL-NEXT: ret i64 1234 +; +; NONINTEGRAL-INSTSIMPLIFY-LABEL: define {{[^@]+}}@fold_ptrtoint_zero_nullgep_of_nonzero_inbounds_nullgep() { +; NONINTEGRAL-INSTSIMPLIFY-NEXT: ret i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234) to i64) +; +; NONINTEGRAL-INSTCOMBINE-LABEL: define {{[^@]+}}@fold_ptrtoint_zero_nullgep_of_nonzero_inbounds_nullgep() { +; NONINTEGRAL-INSTCOMBINE-NEXT: ret i64 ptrtoint (i8 addrspace(1)* getelementptr (i8, i8 addrspace(1)* null, i64 1234) to i64) +; + %nonzero_offset = add i64 1234, 0 + %zero_offset = sub i64 %nonzero_offset, 1234 + %ptr = getelementptr inbounds i8, i8 addrspace(1)* null, i64 %nonzero_offset + %ptr2 = getelementptr i8, i8 addrspace(1)* %ptr, i64 %zero_offset + %ret = ptrtoint i8 addrspace(1)* %ptr2 to i64 + ret i64 %ret +} + +define i64 @fold_ptrtoint_nonzero_inbounds_nullgep_of_zero_noninbounds_nullgep() { +; LLPARSER-LABEL: define {{[^@]+}}@fold_ptrtoint_nonzero_inbounds_nullgep_of_zero_noninbounds_nullgep() { +; LLPARSER-NEXT: [[NONZERO_OFFSET:%.*]] = add i64 1234, 0 +; LLPARSER-NEXT: [[ZERO_OFFSET:%.*]] = sub i64 [[NONZERO_OFFSET]], 1234 +; LLPARSER-NEXT: [[PTR:%.*]] = getelementptr i8, i8 addrspace(1)* null, i64 [[ZERO_OFFSET]] +; LLPARSER-NEXT: [[PTR2:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[PTR]], i64 [[NONZERO_OFFSET]] +; LLPARSER-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR2]] to i64 +; LLPARSER-NEXT: ret i64 [[RET]] +; +; INTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_nonzero_inbounds_nullgep_of_zero_noninbounds_nullgep() { +; INTEGRAL-NEXT: ret i64 1234 +; +; NONINTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_nonzero_inbounds_nullgep_of_zero_noninbounds_nullgep() { +; NONINTEGRAL-NEXT: ret i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234) to i64) +; + %nonzero_offset = add i64 1234, 0 + %zero_offset = sub i64 %nonzero_offset, 1234 + %ptr = getelementptr i8, i8 addrspace(1)* null, i64 %zero_offset + %ptr2 = getelementptr inbounds i8, i8 addrspace(1)* %ptr, i64 %nonzero_offset + %ret = ptrtoint i8 addrspace(1)* %ptr2 to i64 + ret i64 %ret +} + +; We should also be able to fold GEPs with multiple indices. +%struct.S = type { [2 x %struct.K] } +%struct.K = type { [32 x i8] } + +define i64 @fold_complex_index_last_nonzero(i64 %x) local_unnamed_addr #0 { +; ALL-LABEL: define {{[^@]+}}@fold_complex_index_last_nonzero +; ALL-SAME: (i64 [[X:%.*]]) local_unnamed_addr { +; ALL-NEXT: entry: +; ALL-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_S:%.*]], [[STRUCT_S]] addrspace(1)* null, i64 0, i32 0, i64 0, i32 0, i64 [[X]] +; ALL-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; ALL-NEXT: ret i64 [[RET]] +; +entry: + %ptr = getelementptr inbounds %struct.S, %struct.S addrspace(1)* null, i64 0, i32 0, i64 0, i32 0, i64 %x + %ret = ptrtoint i8 addrspace(1)* %ptr to i64 + ret i64 %ret +} + +define i64 @fold_complex_index_multiple_nonzero(i64 %x) local_unnamed_addr #0 { +; ALL-LABEL: define {{[^@]+}}@fold_complex_index_multiple_nonzero +; ALL-SAME: (i64 [[X:%.*]]) local_unnamed_addr { +; ALL-NEXT: entry: +; ALL-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_S:%.*]], [[STRUCT_S]] addrspace(1)* null, i64 1, i32 0, i64 1, i32 0, i64 [[X]] +; ALL-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; ALL-NEXT: ret i64 [[RET]] +; +entry: + %ptr = getelementptr inbounds %struct.S, %struct.S addrspace(1)* null, i64 1, i32 0, i64 1, i32 0, i64 %x + %ret = ptrtoint i8 addrspace(1)* %ptr to i64 + ret i64 %ret +} + +define i64 @fold_ptrtoint_inbounds_nullgep_of_nonzero_inbounds_nullgep() { +; LLPARSER-LABEL: define {{[^@]+}}@fold_ptrtoint_inbounds_nullgep_of_nonzero_inbounds_nullgep() { +; LLPARSER-NEXT: [[NONZERO_OFFSET:%.*]] = add i64 1234, 0 +; LLPARSER-NEXT: [[ZERO_OFFSET:%.*]] = sub i64 [[NONZERO_OFFSET]], 1234 +; LLPARSER-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* null, i64 [[NONZERO_OFFSET]] +; LLPARSER-NEXT: [[PTR2:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[PTR]], i64 [[ZERO_OFFSET]] +; LLPARSER-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR2]] to i64 +; LLPARSER-NEXT: ret i64 [[RET]] +; +; INTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_inbounds_nullgep_of_nonzero_inbounds_nullgep() { +; INTEGRAL-NEXT: ret i64 1234 +; +; NONINTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_inbounds_nullgep_of_nonzero_inbounds_nullgep() { +; NONINTEGRAL-NEXT: ret i64 ptrtoint (i8 addrspace(1)* getelementptr inbounds (i8, i8 addrspace(1)* null, i64 1234) to i64) +; + %nonzero_offset = add i64 1234, 0 + %zero_offset = sub i64 %nonzero_offset, 1234 + %ptr = getelementptr inbounds i8, i8 addrspace(1)* null, i64 %nonzero_offset + %ptr2 = getelementptr inbounds i8, i8 addrspace(1)* %ptr, i64 %zero_offset + %ret = ptrtoint i8 addrspace(1)* %ptr2 to i64 + ret i64 %ret +} + +; Check that InstCombine can convert ptrtoint(gep null) with multiple indices +define i64 @fold_ptrtoint_nullgep_array_one_var_1(i64 %x) { +; ALL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_array_one_var_1 +; ALL-SAME: (i64 [[X:%.*]]) { +; ALL-NEXT: [[PTR:%.*]] = getelementptr [2 x i16], [2 x i16] addrspace(1)* null, i64 [[X]], i64 3 +; ALL-NEXT: [[RET:%.*]] = ptrtoint i16 addrspace(1)* [[PTR]] to i64 +; ALL-NEXT: ret i64 [[RET]] +; + %ptr = getelementptr [2 x i16], [2 x i16] addrspace(1)* null, i64 %x, i64 3 + %ret = ptrtoint i16 addrspace(1)* %ptr to i64 + ret i64 %ret +} + +define i64 @fold_ptrtoint_nullgep_array_one_var_2(i64 %x) { +; ALL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_array_one_var_2 +; ALL-SAME: (i64 [[X:%.*]]) { +; ALL-NEXT: [[PTR:%.*]] = getelementptr [2 x i16], [2 x i16] addrspace(1)* null, i64 7, i64 [[X]] +; ALL-NEXT: [[RET:%.*]] = ptrtoint i16 addrspace(1)* [[PTR]] to i64 +; ALL-NEXT: ret i64 [[RET]] +; + %ptr = getelementptr [2 x i16], [2 x i16] addrspace(1)* null, i64 7, i64 %x + %ret = ptrtoint i16 addrspace(1)* %ptr to i64 + ret i64 %ret +} + +define i64 @fold_ptrtoint_nested_array_two_vars(i64 %x, i64 %y) { +; ALL-LABEL: define {{[^@]+}}@fold_ptrtoint_nested_array_two_vars +; ALL-SAME: (i64 [[X:%.*]], i64 [[Y:%.*]]) { +; ALL-NEXT: [[PTR:%.*]] = getelementptr [2 x i16], [2 x i16] addrspace(1)* null, i64 [[X]], i64 [[Y]] +; ALL-NEXT: [[RET:%.*]] = ptrtoint i16 addrspace(1)* [[PTR]] to i64 +; ALL-NEXT: ret i64 [[RET]] +; + + %ptr = getelementptr [2 x i16], [2 x i16] addrspace(1)* null, i64 %x, i64 %y + %ret = ptrtoint i16 addrspace(1)* %ptr to i64 + ret i64 %ret +} + +define i64 @fold_ptrtoint_nested_array_two_vars_plus_zero(i64 %x, i64 %y) { +; ALL-LABEL: define {{[^@]+}}@fold_ptrtoint_nested_array_two_vars_plus_zero +; ALL-SAME: (i64 [[X:%.*]], i64 [[Y:%.*]]) { +; ALL-NEXT: [[PTR:%.*]] = getelementptr [2 x [2 x i16]], [2 x [2 x i16]] addrspace(1)* null, i64 [[X]], i64 [[Y]], i64 0 +; ALL-NEXT: [[RET:%.*]] = ptrtoint i16 addrspace(1)* [[PTR]] to i64 +; ALL-NEXT: ret i64 [[RET]] +; + %ptr = getelementptr [2 x [2 x i16]], [2 x [2 x i16]] addrspace(1)* null, i64 %x, i64 %y, i64 0 + %ret = ptrtoint i16 addrspace(1)* %ptr to i64 + ret i64 %ret +} + +define i64 @fold_ptrtoint_nested_array_two_vars_plus_const(i64 %x, i64 %y) { +; ALL-LABEL: define {{[^@]+}}@fold_ptrtoint_nested_array_two_vars_plus_const +; ALL-SAME: (i64 [[X:%.*]], i64 [[Y:%.*]]) { +; ALL-NEXT: [[PTR:%.*]] = getelementptr [2 x [2 x i16]], [2 x [2 x i16]] addrspace(1)* null, i64 [[X]], i64 [[Y]], i64 1 +; ALL-NEXT: [[RET:%.*]] = ptrtoint i16 addrspace(1)* [[PTR]] to i64 +; ALL-NEXT: ret i64 [[RET]] +; + %ptr = getelementptr [2 x [2 x i16]], [2 x [2 x i16]] addrspace(1)* null, i64 %x, i64 %y, i64 1 + %ret = ptrtoint i16 addrspace(1)* %ptr to i64 + ret i64 %ret +} + +; Negative test -- should not be folded since there are multiple GEP uses +define i64 @fold_ptrtoint_nested_nullgep_array_variable_multiple_uses(i64 %x, i64 %y) { +; ALL-LABEL: define {{[^@]+}}@fold_ptrtoint_nested_nullgep_array_variable_multiple_uses +; ALL-SAME: (i64 [[X:%.*]], i64 [[Y:%.*]]) { +; ALL-NEXT: [[PTR:%.*]] = getelementptr [2 x i16], [2 x i16] addrspace(1)* null, i64 [[X]], i64 [[Y]] +; ALL-NEXT: [[PTRI8:%.*]] = bitcast i16 addrspace(1)* [[PTR]] to i8 addrspace(1)* +; ALL-NEXT: call void @use_ptr(i8 addrspace(1)* [[PTRI8]]) +; ALL-NEXT: [[RET:%.*]] = ptrtoint i16 addrspace(1)* [[PTR]] to i64 +; ALL-NEXT: ret i64 [[RET]] +; + %ptr = getelementptr [2 x i16], [2 x i16] addrspace(1)* null, i64 %x, i64 %y + %ptri8 = bitcast i16 addrspace(1)* %ptr to i8 addrspace(1)* + call void @use_ptr(i8 addrspace(1)* %ptri8) + %ret = ptrtoint i16 addrspace(1)* %ptr to i64 + ret i64 %ret +}