diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp @@ -2076,6 +2076,33 @@ SQ.getWithInstruction(&CI))) return replaceInstUsesWith(CI, V); + if (auto *GEP = dyn_cast(SrcOp)) { + // Fold ptrtoint(gep null, x) to multiply + constant if there is one use + // and if we only need a single multiply or if we need two multiplies and + // don't have a constant offset. + // FIXME: can we do this for non-integral pointers? + if (GEP->hasOneUse() && !DL.isNonIntegralPointerType(GEP->getType()) && + isa(GEP->getPointerOperand())) { + MapVector VariableOffsets; + unsigned BitWidth = DL.getIndexSizeInBits(GEP->getPointerAddressSpace()); + APInt ConstantOffset(BitWidth, 0); + if (GEP->collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset)) { + Value *Result = nullptr; + if (VariableOffsets.empty()) { + Result = Builder.getInt(ConstantOffset); + } else if (VariableOffsets.size() == 1) { + auto &ValAndFactor = VariableOffsets.back(); + Value *Mul = Builder.CreateMul(ValAndFactor.first, + Builder.getInt(ValAndFactor.second)); + Result = Builder.CreateAdd(Mul, Builder.getInt(ConstantOffset)); + } + if (Result) + return replaceInstUsesWith( + CI, Builder.CreateIntCast(Result, Ty, /*isSigned=*/false)); + } + } + } + Value *Vec, *Scalar, *Index; if (match(SrcOp, m_OneUse(m_InsertElt(m_IntToPtr(m_Value(Vec)), m_Value(Scalar), m_Value(Index)))) && diff --git a/llvm/test/Transforms/InstCombine/ptrtoint-nullgep.ll b/llvm/test/Transforms/InstCombine/ptrtoint-nullgep.ll --- a/llvm/test/Transforms/InstCombine/ptrtoint-nullgep.ll +++ b/llvm/test/Transforms/InstCombine/ptrtoint-nullgep.ll @@ -59,8 +59,8 @@ 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 at least one of the GEPs having a non-zero offset define void @constant_fold_ptrtoint_of_gep_of_nullgep() { - ; Check all combinations of inbounds+non-inbounds GEP with the outer GEP having a non-zero offset ; 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)) @@ -305,12 +305,18 @@ ; INSTSIMPLIFY-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 ; INSTSIMPLIFY-NEXT: ret i64 [[RET]] ; -; INSTCOMBINE-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_known_nonzero_inbounds_multiple_indices -; INSTCOMBINE-SAME: (i64 [[VAL:%.*]]) { -; INSTCOMBINE-NEXT: [[NON_ZERO_OFFSET:%.*]] = or i64 [[VAL]], 1 -; INSTCOMBINE-NEXT: [[PTR:%.*]] = getelementptr inbounds [2 x i8], [2 x i8] addrspace(1)* null, i64 [[NON_ZERO_OFFSET]], i64 1 -; INSTCOMBINE-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 -; INSTCOMBINE-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:%.*]] = shl i64 [[VAL]], 1 +; INTEGRAL-INSTCOMBINE-NEXT: [[TMP1:%.*]] = or i64 [[NON_ZERO_OFFSET]], 3 +; INTEGRAL-INSTCOMBINE-NEXT: ret i64 [[TMP1]] +; +; 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 @@ -319,13 +325,30 @@ } ; We can't fold non-i8 GEPs in InstSimplify since that would require adding new arithmetic. -; TODO: handle this case in InstCombine +; However, InstCombine can decompose the null gep and convert it to a shift. 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]] +; LLPARSER-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_i32_variable +; LLPARSER-SAME: (i64 [[VAL:%.*]]) { +; LLPARSER-NEXT: [[PTR:%.*]] = getelementptr i32, i32 addrspace(1)* null, i64 [[VAL]] +; LLPARSER-NEXT: [[RET:%.*]] = ptrtoint i32 addrspace(1)* [[PTR]] to i64 +; LLPARSER-NEXT: ret i64 [[RET]] +; +; INTEGRAL-INSTSIMPLIFY-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_i32_variable +; INTEGRAL-INSTSIMPLIFY-SAME: (i64 [[VAL:%.*]]) { +; INTEGRAL-INSTSIMPLIFY-NEXT: [[PTR:%.*]] = getelementptr i32, i32 addrspace(1)* null, i64 [[VAL]] +; INTEGRAL-INSTSIMPLIFY-NEXT: [[RET:%.*]] = ptrtoint i32 addrspace(1)* [[PTR]] to i64 +; INTEGRAL-INSTSIMPLIFY-NEXT: ret i64 [[RET]] +; +; INTEGRAL-INSTCOMBINE-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_i32_variable +; INTEGRAL-INSTCOMBINE-SAME: (i64 [[VAL:%.*]]) { +; INTEGRAL-INSTCOMBINE-NEXT: [[TMP1:%.*]] = shl i64 [[VAL]], 2 +; INTEGRAL-INSTCOMBINE-NEXT: ret i64 [[TMP1]] +; +; NONINTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_i32_variable +; NONINTEGRAL-SAME: (i64 [[VAL:%.*]]) { +; NONINTEGRAL-NEXT: [[PTR:%.*]] = getelementptr i32, i32 addrspace(1)* null, i64 [[VAL]] +; NONINTEGRAL-NEXT: [[RET:%.*]] = ptrtoint i32 addrspace(1)* [[PTR]] to i64 +; NONINTEGRAL-NEXT: ret i64 [[RET]] ; %ptr = getelementptr i32, i32 addrspace(1)* null, i64 %val %ret = ptrtoint i32 addrspace(1)* %ptr to i64 @@ -427,3 +450,123 @@ %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) { +; LLPARSER-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_array_one_var_1 +; LLPARSER-SAME: (i64 [[X:%.*]]) { +; LLPARSER-NEXT: [[PTR:%.*]] = getelementptr [2 x i16], [2 x i16] addrspace(1)* null, i64 [[X]], i64 3 +; LLPARSER-NEXT: [[RET:%.*]] = ptrtoint i16 addrspace(1)* [[PTR]] to i64 +; LLPARSER-NEXT: ret i64 [[RET]] +; +; INTEGRAL-INSTSIMPLIFY-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_array_one_var_1 +; INTEGRAL-INSTSIMPLIFY-SAME: (i64 [[X:%.*]]) { +; INTEGRAL-INSTSIMPLIFY-NEXT: [[PTR:%.*]] = getelementptr [2 x i16], [2 x i16] addrspace(1)* null, i64 [[X]], i64 3 +; INTEGRAL-INSTSIMPLIFY-NEXT: [[RET:%.*]] = ptrtoint i16 addrspace(1)* [[PTR]] to i64 +; INTEGRAL-INSTSIMPLIFY-NEXT: ret i64 [[RET]] +; +; INTEGRAL-INSTCOMBINE-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_array_one_var_1 +; INTEGRAL-INSTCOMBINE-SAME: (i64 [[X:%.*]]) { +; INTEGRAL-INSTCOMBINE-NEXT: [[TMP1:%.*]] = shl i64 [[X]], 2 +; INTEGRAL-INSTCOMBINE-NEXT: [[TMP2:%.*]] = add i64 [[TMP1]], 6 +; INTEGRAL-INSTCOMBINE-NEXT: ret i64 [[TMP2]] +; +; NONINTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_array_one_var_1 +; NONINTEGRAL-SAME: (i64 [[X:%.*]]) { +; NONINTEGRAL-NEXT: [[PTR:%.*]] = getelementptr [2 x i16], [2 x i16] addrspace(1)* null, i64 [[X]], i64 3 +; NONINTEGRAL-NEXT: [[RET:%.*]] = ptrtoint i16 addrspace(1)* [[PTR]] to i64 +; NONINTEGRAL-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) { +; LLPARSER-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_array_one_var_2 +; LLPARSER-SAME: (i64 [[X:%.*]]) { +; LLPARSER-NEXT: [[PTR:%.*]] = getelementptr [2 x i16], [2 x i16] addrspace(1)* null, i64 7, i64 [[X]] +; LLPARSER-NEXT: [[RET:%.*]] = ptrtoint i16 addrspace(1)* [[PTR]] to i64 +; LLPARSER-NEXT: ret i64 [[RET]] +; +; INTEGRAL-INSTSIMPLIFY-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_array_one_var_2 +; INTEGRAL-INSTSIMPLIFY-SAME: (i64 [[X:%.*]]) { +; INTEGRAL-INSTSIMPLIFY-NEXT: [[PTR:%.*]] = getelementptr [2 x i16], [2 x i16] addrspace(1)* null, i64 7, i64 [[X]] +; INTEGRAL-INSTSIMPLIFY-NEXT: [[RET:%.*]] = ptrtoint i16 addrspace(1)* [[PTR]] to i64 +; INTEGRAL-INSTSIMPLIFY-NEXT: ret i64 [[RET]] +; +; INTEGRAL-INSTCOMBINE-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_array_one_var_2 +; INTEGRAL-INSTCOMBINE-SAME: (i64 [[X:%.*]]) { +; INTEGRAL-INSTCOMBINE-NEXT: [[TMP1:%.*]] = shl i64 [[X]], 1 +; INTEGRAL-INSTCOMBINE-NEXT: [[TMP2:%.*]] = add i64 [[TMP1]], 28 +; INTEGRAL-INSTCOMBINE-NEXT: ret i64 [[TMP2]] +; +; NONINTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_array_one_var_2 +; NONINTEGRAL-SAME: (i64 [[X:%.*]]) { +; NONINTEGRAL-NEXT: [[PTR:%.*]] = getelementptr [2 x i16], [2 x i16] addrspace(1)* null, i64 7, i64 [[X]] +; NONINTEGRAL-NEXT: [[RET:%.*]] = ptrtoint i16 addrspace(1)* [[PTR]] to i64 +; NONINTEGRAL-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 +} + +; Negative test -- should not be folded since there are two variable indices +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 +} + +; Negative test -- should not be folded since there are two variable indices and a zero constant +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 +} + +; Negative test -- should not be folded since there are two variable indices and a non-zero constant +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 +}