Skip to content

Commit 6bf1601

Browse files
authored
[InstCombine] Fold pointer adding in integer to arithmetic add (#91596)
Fold ``` llvm define i32 @src(i32 %x, i32 %y) { %base = inttoptr i32 %x to ptr %ptr = getelementptr inbounds i8, ptr %base, i32 %y %r = ptrtoint ptr %ptr to i32 ret i32 %r } ``` where both `%base` and `%ptr` have only one use, to ``` llvm define i32 @tgt(i32 %x, i32 %y) { %r = add i32 %x, %y ret i32 %r } ``` The `add` can be `nuw` if the GEP is `inbounds` and the offset is non-negative. The relevant Alive2 proof is https://alive2.llvm.org/ce/z/nP3RWy. ### Motivation It seems unnecessary to convert `int` to `ptr` just to get its offset. In most cases, they generates the same assembly, but sometimes it may miss some optimizations since the analysis of `GEP` is not as perfect as that of arithmetic operation. One example is https://github.com/dtcxzyw/llvm-opt-benchmark/blob/e3c822bf41df3a88ca38eba884a52b0cc7e70bf2/bench/protobuf/optimized/generated_message_reflection.cc.ll#L39860-L39873 ``` llvm %conv.i188 = zext i32 %145 to i64 %add.i189 = add i64 %conv.i188, %125 %146 = load i16, ptr %num_aux_entries10.i, align 2 %conv2.i191 = zext i16 %146 to i64 %mul.i192 = shl nuw nsw i64 %conv2.i191, 3 %add3.i193 = add i64 %add.i189, %mul.i192 %147 = inttoptr i64 %add3.i193 to ptr %sub.ptr.lhs.cast.i195 = ptrtoint ptr %144 to i64 %sub.ptr.rhs.cast.i196 = ptrtoint ptr %143 to i64 %sub.ptr.sub.i197 = sub i64 %sub.ptr.lhs.cast.i195, %sub.ptr.rhs.cast.i196 %add.ptr = getelementptr inbounds i8, ptr %147, i64 %sub.ptr.sub.i197 %sub.ptr.lhs.cast = ptrtoint ptr %add.ptr to i64 %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %125 ``` where `%conv.i188` first adds `%125` and then subtracts `%125` (the result is `%sub.ptr.sub`), which can be optimized.
1 parent 91423d7 commit 6bf1601

File tree

2 files changed

+167
-4
lines changed

2 files changed

+167
-4
lines changed

llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2049,16 +2049,28 @@ Instruction *InstCombinerImpl::visitPtrToInt(PtrToIntInst &CI) {
20492049
Mask->getType() == Ty)
20502050
return BinaryOperator::CreateAnd(Builder.CreatePtrToInt(Ptr, Ty), Mask);
20512051

2052-
if (auto *GEP = dyn_cast<GetElementPtrInst>(SrcOp)) {
2052+
if (auto *GEP = dyn_cast<GEPOperator>(SrcOp)) {
20532053
// Fold ptrtoint(gep null, x) to multiply + constant if the GEP has one use.
20542054
// While this can increase the number of instructions it doesn't actually
20552055
// increase the overall complexity since the arithmetic is just part of
20562056
// the GEP otherwise.
20572057
if (GEP->hasOneUse() &&
20582058
isa<ConstantPointerNull>(GEP->getPointerOperand())) {
2059-
return replaceInstUsesWith(
2060-
CI, Builder.CreateIntCast(EmitGEPOffset(cast<GEPOperator>(GEP)), Ty,
2061-
/*isSigned=*/false));
2059+
return replaceInstUsesWith(CI,
2060+
Builder.CreateIntCast(EmitGEPOffset(GEP), Ty,
2061+
/*isSigned=*/false));
2062+
}
2063+
2064+
// (ptrtoint (gep (inttoptr Base), ...)) -> Base + Offset
2065+
Value *Base;
2066+
if (GEP->hasOneUse() &&
2067+
match(GEP->getPointerOperand(), m_OneUse(m_IntToPtr(m_Value(Base)))) &&
2068+
Base->getType() == Ty) {
2069+
Value *Offset = EmitGEPOffset(GEP);
2070+
auto *NewOp = BinaryOperator::CreateAdd(Base, Offset);
2071+
if (GEP->isInBounds() && isKnownNonNegative(Offset, SQ))
2072+
NewOp->setHasNoUnsignedWrap(true);
2073+
return NewOp;
20622074
}
20632075
}
20642076

llvm/test/Transforms/InstCombine/cast_ptr.ll

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,3 +244,154 @@ define <2 x i32> @insertelt_extra_use2(<2 x i32> %x, ptr %p) {
244244
%r = ptrtoint <2 x ptr> %i to <2 x i32>
245245
ret <2 x i32> %r
246246
}
247+
248+
define i32 @ptr_add_in_int(i32 %x, i32 %y) {
249+
; CHECK-LABEL: @ptr_add_in_int(
250+
; CHECK-NEXT: [[R:%.*]] = add i32 [[X:%.*]], [[Y:%.*]]
251+
; CHECK-NEXT: ret i32 [[R]]
252+
;
253+
%ptr = inttoptr i32 %x to ptr
254+
%p2 = getelementptr inbounds i8, ptr %ptr, i32 %y
255+
%r = ptrtoint ptr %p2 to i32
256+
ret i32 %r
257+
}
258+
259+
define i32 @ptr_add_in_int_2(i32 %x, i32 %y) {
260+
; CHECK-LABEL: @ptr_add_in_int_2(
261+
; CHECK-NEXT: [[P2_IDX:%.*]] = shl nsw i32 [[Y:%.*]], 2
262+
; CHECK-NEXT: [[R:%.*]] = add i32 [[P2_IDX]], [[X:%.*]]
263+
; CHECK-NEXT: ret i32 [[R]]
264+
;
265+
%ptr = inttoptr i32 %x to ptr
266+
%p2 = getelementptr inbounds i32, ptr %ptr, i32 %y
267+
%r = ptrtoint ptr %p2 to i32
268+
ret i32 %r
269+
}
270+
271+
define i32 @ptr_add_in_int_nneg(i32 %x, i32 %y) {
272+
; CHECK-LABEL: @ptr_add_in_int_nneg(
273+
; CHECK-NEXT: [[Z:%.*]] = call i32 @llvm.abs.i32(i32 [[Y:%.*]], i1 true)
274+
; CHECK-NEXT: [[R:%.*]] = add nuw i32 [[Z]], [[X:%.*]]
275+
; CHECK-NEXT: ret i32 [[R]]
276+
;
277+
%z = call i32 @llvm.abs.i32(i32 %y, i1 true)
278+
%ptr = inttoptr i32 %x to ptr
279+
%p2 = getelementptr inbounds i8, ptr %ptr, i32 %z
280+
%r = ptrtoint ptr %p2 to i32
281+
ret i32 %r
282+
}
283+
284+
define i64 @ptr_add_in_int_different_type_1(i32 %x, i32 %y) {
285+
; CHECK-LABEL: @ptr_add_in_int_different_type_1(
286+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], [[Y:%.*]]
287+
; CHECK-NEXT: [[R:%.*]] = zext i32 [[TMP1]] to i64
288+
; CHECK-NEXT: ret i64 [[R]]
289+
;
290+
%ptr = inttoptr i32 %x to ptr
291+
%p2 = getelementptr i8, ptr %ptr, i32 %y
292+
%r = ptrtoint ptr %p2 to i64
293+
ret i64 %r
294+
}
295+
296+
define i16 @ptr_add_in_int_different_type_2(i32 %x, i32 %y) {
297+
; CHECK-LABEL: @ptr_add_in_int_different_type_2(
298+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], [[Y:%.*]]
299+
; CHECK-NEXT: [[R:%.*]] = trunc i32 [[TMP1]] to i16
300+
; CHECK-NEXT: ret i16 [[R]]
301+
;
302+
%ptr = inttoptr i32 %x to ptr
303+
%p2 = getelementptr i8, ptr %ptr, i32 %y
304+
%r = ptrtoint ptr %p2 to i16
305+
ret i16 %r
306+
}
307+
308+
define i32 @ptr_add_in_int_different_type_3(i16 %x, i32 %y) {
309+
; CHECK-LABEL: @ptr_add_in_int_different_type_3(
310+
; CHECK-NEXT: [[TMP1:%.*]] = zext i16 [[X:%.*]] to i32
311+
; CHECK-NEXT: [[R:%.*]] = add i32 [[TMP1]], [[Y:%.*]]
312+
; CHECK-NEXT: ret i32 [[R]]
313+
;
314+
%ptr = inttoptr i16 %x to ptr
315+
%p2 = getelementptr i8, ptr %ptr, i32 %y
316+
%r = ptrtoint ptr %p2 to i32
317+
ret i32 %r
318+
}
319+
320+
define i32 @ptr_add_in_int_different_type_4(i64 %x, i32 %y) {
321+
; CHECK-LABEL: @ptr_add_in_int_different_type_4(
322+
; CHECK-NEXT: [[TMP1:%.*]] = trunc i64 [[X:%.*]] to i32
323+
; CHECK-NEXT: [[R:%.*]] = add i32 [[TMP1]], [[Y:%.*]]
324+
; CHECK-NEXT: ret i32 [[R]]
325+
;
326+
%ptr = inttoptr i64 %x to ptr
327+
%p2 = getelementptr i8, ptr %ptr, i32 %y
328+
%r = ptrtoint ptr %p2 to i32
329+
ret i32 %r
330+
}
331+
332+
define i32 @ptr_add_in_int_not_inbounds(i32 %x, i32 %y) {
333+
; CHECK-LABEL: @ptr_add_in_int_not_inbounds(
334+
; CHECK-NEXT: [[Z:%.*]] = call i32 @llvm.abs.i32(i32 [[Y:%.*]], i1 true)
335+
; CHECK-NEXT: [[R:%.*]] = add i32 [[Z]], [[X:%.*]]
336+
; CHECK-NEXT: ret i32 [[R]]
337+
;
338+
%z = call i32 @llvm.abs.i32(i32 %y, i1 true)
339+
%ptr = inttoptr i32 %x to ptr
340+
%p2 = getelementptr i8, ptr %ptr, i32 %z
341+
%r = ptrtoint ptr %p2 to i32
342+
ret i32 %r
343+
}
344+
345+
define i32 @ptr_add_in_int_const(i32 %x) {
346+
; CHECK-LABEL: @ptr_add_in_int_const(
347+
; CHECK-NEXT: [[R:%.*]] = add nuw i32 [[X:%.*]], 4096
348+
; CHECK-NEXT: ret i32 [[R]]
349+
;
350+
%ptr = inttoptr i32 %x to ptr
351+
%p2 = getelementptr inbounds i8, ptr %ptr, i32 4096
352+
%r = ptrtoint ptr %p2 to i32
353+
ret i32 %r
354+
}
355+
356+
define i32 @ptr_add_in_int_const_negative(i32 %x) {
357+
; CHECK-LABEL: @ptr_add_in_int_const_negative(
358+
; CHECK-NEXT: [[R:%.*]] = add i32 [[X:%.*]], -4096
359+
; CHECK-NEXT: ret i32 [[R]]
360+
;
361+
%ptr = inttoptr i32 %x to ptr
362+
%p2 = getelementptr inbounds i8, ptr %ptr, i32 -4096
363+
%r = ptrtoint ptr %p2 to i32
364+
ret i32 %r
365+
}
366+
367+
declare void @use_ptr(ptr)
368+
369+
define i32 @ptr_add_in_int_extra_use1(i32 %x) {
370+
; CHECK-LABEL: @ptr_add_in_int_extra_use1(
371+
; CHECK-NEXT: [[PTR:%.*]] = inttoptr i32 [[X:%.*]] to ptr
372+
; CHECK-NEXT: call void @use_ptr(ptr [[PTR]])
373+
; CHECK-NEXT: [[P2:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i32 4096
374+
; CHECK-NEXT: [[R:%.*]] = ptrtoint ptr [[P2]] to i32
375+
; CHECK-NEXT: ret i32 [[R]]
376+
;
377+
%ptr = inttoptr i32 %x to ptr
378+
call void @use_ptr(ptr %ptr)
379+
%p2 = getelementptr inbounds i8, ptr %ptr, i32 4096
380+
%r = ptrtoint ptr %p2 to i32
381+
ret i32 %r
382+
}
383+
384+
define i32 @ptr_add_in_int_extra_use2(i32 %x) {
385+
; CHECK-LABEL: @ptr_add_in_int_extra_use2(
386+
; CHECK-NEXT: [[PTR:%.*]] = inttoptr i32 [[X:%.*]] to ptr
387+
; CHECK-NEXT: [[P2:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i32 4096
388+
; CHECK-NEXT: call void @use_ptr(ptr nonnull [[P2]])
389+
; CHECK-NEXT: [[R:%.*]] = ptrtoint ptr [[P2]] to i32
390+
; CHECK-NEXT: ret i32 [[R]]
391+
;
392+
%ptr = inttoptr i32 %x to ptr
393+
%p2 = getelementptr inbounds i8, ptr %ptr, i32 4096
394+
call void @use_ptr(ptr %p2)
395+
%r = ptrtoint ptr %p2 to i32
396+
ret i32 %r
397+
}

0 commit comments

Comments
 (0)