diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -4667,9 +4667,35 @@ } // bitcast x -> x - if (CastOpc == Instruction::BitCast) + if (CastOpc == Instruction::BitCast) { if (Op->getType() == Ty) return Op; + } else if (CastOpc == Instruction::PtrToInt) { + // Fold (ptrtoint (gep i8 null, x)) -> x. + // We don't perform this fold for non-integral pointers since the bitwise + // representation is undefined (even for GEPs on null). + // FIXME: Or is this also valid for non-integral pointers? + if (auto *GEP = dyn_cast(Op)) { + if (isa(GEP->getOperand(0)) && + !Q.DL.isNonIntegralAddressSpace(GEP->getAddressSpace())) { + bool OnlyLastIndexNonzero = true; + for (unsigned i = 1, e = GEP->getNumOperands() - 1; i < e; ++i) { + if (!match(GEP->getOperand(i), m_ZeroInt())) + OnlyLastIndexNonzero = false; + } + Value *LastIndexOp = GEP->getOperand(GEP->getNumOperands() - 1); + // We can't create any zext/trunc instructions here, so this fold is + // limited to cases where the result type matches the GEP index type. + if (OnlyLastIndexNonzero && LastIndexOp->getType() == Ty) { + // We also can't perform the fold if we need a type size multiplier. + // More complex cases are handled in InstCombineCompares.cpp. + TypeSize Stride = Q.DL.getTypeAllocSize(GEP->getResultElementType()); + if (!Stride.isScalable() && Stride == 1) + return LastIndexOp; + } + } + } + } return nullptr; } 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 @@ -2071,6 +2071,11 @@ return CastInst::CreateIntegerCast(P, Ty, /*isSigned=*/false); } + // TODO: should we call SimplifyCastInst() in commonCastTransforms()? + if (Value *V = SimplifyCastInst(CI.getOpcode(), SrcOp, Ty, + SQ.getWithInstruction(&CI))) + return replaceInstUsesWith(CI, V); + 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 @@ -189,11 +189,21 @@ ; 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]] +; LLPARSER-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable +; LLPARSER-SAME: (i64 [[VAL:%.*]]) { +; LLPARSER-NEXT: [[PTR:%.*]] = getelementptr i8, i8 addrspace(1)* null, i64 [[VAL]] +; LLPARSER-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; LLPARSER-NEXT: ret i64 [[RET]] +; +; INTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable +; INTEGRAL-SAME: (i64 [[VAL:%.*]]) { +; INTEGRAL-NEXT: ret i64 [[VAL]] +; +; NONINTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable +; NONINTEGRAL-SAME: (i64 [[VAL:%.*]]) { +; NONINTEGRAL-NEXT: [[PTR:%.*]] = getelementptr i8, i8 addrspace(1)* null, i64 [[VAL]] +; NONINTEGRAL-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; NONINTEGRAL-NEXT: ret i64 [[RET]] ; %ptr = getelementptr i8, i8 addrspace(1)* null, i64 %val %ret = ptrtoint i8 addrspace(1)* %ptr to i64 @@ -202,12 +212,24 @@ ; Inbounds null-GEP with non-zero offset can be folded to poison. 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]] +; LLPARSER-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_known_nonzero +; LLPARSER-SAME: (i64 [[VAL:%.*]]) { +; LLPARSER-NEXT: [[NON_ZERO_OFFSET:%.*]] = or i64 [[VAL]], 1 +; LLPARSER-NEXT: [[PTR:%.*]] = getelementptr i8, i8 addrspace(1)* null, i64 [[NON_ZERO_OFFSET]] +; LLPARSER-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; LLPARSER-NEXT: ret i64 [[RET]] +; +; INTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_known_nonzero +; INTEGRAL-SAME: (i64 [[VAL:%.*]]) { +; INTEGRAL-NEXT: [[NON_ZERO_OFFSET:%.*]] = or i64 [[VAL]], 1 +; INTEGRAL-NEXT: ret i64 [[NON_ZERO_OFFSET]] +; +; NONINTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_known_nonzero +; NONINTEGRAL-SAME: (i64 [[VAL:%.*]]) { +; NONINTEGRAL-NEXT: [[NON_ZERO_OFFSET:%.*]] = or i64 [[VAL]], 1 +; NONINTEGRAL-NEXT: [[PTR:%.*]] = getelementptr i8, i8 addrspace(1)* null, i64 [[NON_ZERO_OFFSET]] +; NONINTEGRAL-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; NONINTEGRAL-NEXT: ret i64 [[RET]] ; %non_zero_offset = or i64 %val, 1 %ptr = getelementptr i8, i8 addrspace(1)* null, i64 %non_zero_offset @@ -217,11 +239,21 @@ ; This is only valid if %val is zero so we can 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]] +; LLPARSER-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_inbounds +; LLPARSER-SAME: (i64 [[VAL:%.*]]) { +; LLPARSER-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* null, i64 [[VAL]] +; LLPARSER-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; LLPARSER-NEXT: ret i64 [[RET]] +; +; INTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_inbounds +; INTEGRAL-SAME: (i64 [[VAL:%.*]]) { +; INTEGRAL-NEXT: ret i64 [[VAL]] +; +; NONINTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_inbounds +; NONINTEGRAL-SAME: (i64 [[VAL:%.*]]) { +; NONINTEGRAL-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* null, i64 [[VAL]] +; NONINTEGRAL-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; NONINTEGRAL-NEXT: ret i64 [[RET]] ; %ptr = getelementptr inbounds i8, i8 addrspace(1)* null, i64 %val %ret = ptrtoint i8 addrspace(1)* %ptr to i64 @@ -230,12 +262,24 @@ ; A non-constant but known-non-zero GEP should fold to poison 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]] +; LLPARSER-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_known_nonzero_inbounds +; LLPARSER-SAME: (i64 [[VAL:%.*]]) { +; LLPARSER-NEXT: [[NON_ZERO_OFFSET:%.*]] = or i64 [[VAL]], 1 +; LLPARSER-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* null, i64 [[NON_ZERO_OFFSET]] +; LLPARSER-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; LLPARSER-NEXT: ret i64 [[RET]] +; +; INTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_known_nonzero_inbounds +; INTEGRAL-SAME: (i64 [[VAL:%.*]]) { +; INTEGRAL-NEXT: [[NON_ZERO_OFFSET:%.*]] = or i64 [[VAL]], 1 +; INTEGRAL-NEXT: ret i64 [[NON_ZERO_OFFSET]] +; +; NONINTEGRAL-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_known_nonzero_inbounds +; NONINTEGRAL-SAME: (i64 [[VAL:%.*]]) { +; NONINTEGRAL-NEXT: [[NON_ZERO_OFFSET:%.*]] = or i64 [[VAL]], 1 +; NONINTEGRAL-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* null, i64 [[NON_ZERO_OFFSET]] +; NONINTEGRAL-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; NONINTEGRAL-NEXT: ret i64 [[RET]] ; %non_zero_offset = or i64 %val, 1 %ptr = getelementptr inbounds i8, i8 addrspace(1)* null, i64 %non_zero_offset @@ -301,12 +345,17 @@ ; INSTSIMPLIFY-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i32 ; INSTSIMPLIFY-NEXT: ret i32 [[RET]] ; -; INSTCOMBINE-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_trunc -; INSTCOMBINE-SAME: (i64 [[VAL:%.*]]) { -; INSTCOMBINE-NEXT: [[PTR:%.*]] = getelementptr i8, i8 addrspace(1)* null, i64 [[VAL]] -; INSTCOMBINE-NEXT: [[TMP1:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 -; INSTCOMBINE-NEXT: [[RET:%.*]] = trunc i64 [[TMP1]] to i32 -; INSTCOMBINE-NEXT: ret i32 [[RET]] +; INTEGRAL-INSTCOMBINE-LABEL: define {{[^@]+}}@fold_ptrtoint_nullgep_variable_trunc +; INTEGRAL-INSTCOMBINE-SAME: (i64 [[VAL:%.*]]) { +; INTEGRAL-INSTCOMBINE-NEXT: [[RET:%.*]] = trunc i64 [[VAL]] to i32 +; INTEGRAL-INSTCOMBINE-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 @@ -361,12 +410,24 @@ %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]] +; LLPARSER-LABEL: define {{[^@]+}}@fold_complex_index_last_nonzero +; LLPARSER-SAME: (i64 [[X:%.*]]) local_unnamed_addr { +; LLPARSER-NEXT: entry: +; LLPARSER-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_S:%.*]], [[STRUCT_S]] addrspace(1)* null, i64 0, i32 0, i64 0, i32 0, i64 [[X]] +; LLPARSER-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; LLPARSER-NEXT: ret i64 [[RET]] +; +; INTEGRAL-LABEL: define {{[^@]+}}@fold_complex_index_last_nonzero +; INTEGRAL-SAME: (i64 [[X:%.*]]) local_unnamed_addr { +; INTEGRAL-NEXT: entry: +; INTEGRAL-NEXT: ret i64 [[X]] +; +; NONINTEGRAL-LABEL: define {{[^@]+}}@fold_complex_index_last_nonzero +; NONINTEGRAL-SAME: (i64 [[X:%.*]]) local_unnamed_addr { +; NONINTEGRAL-NEXT: entry: +; NONINTEGRAL-NEXT: [[PTR:%.*]] = getelementptr inbounds [[STRUCT_S:%.*]], [[STRUCT_S]] addrspace(1)* null, i64 0, i32 0, i64 0, i32 0, i64 [[X]] +; NONINTEGRAL-NEXT: [[RET:%.*]] = ptrtoint i8 addrspace(1)* [[PTR]] to i64 +; NONINTEGRAL-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