Skip to content

Commit 0a093f1

Browse files
nikicdvbuka
authored andcommitted
[InstCombine] Add support for ptrtoaddr in pointer difference folds (llvm#164428)
This adds support for folding `ptrtoaddr(p2) - ptrtoaddr(p)` pointer subtractions. We can treat ptrtoaddr the same as ptrtoint as the transform is truncation safe anyway (and in fact supports explicit truncation as well). The only interesting case is the subtraction of zext of ptrtoaddr. For this transform it's important that the address bits are not truncated -- and if any pointer bits are truncated, that the truncation is consistent for both operands. I've explicitly spelled out the three different cases for this, which also fixes a miscompile in the existing ptrtoint fold.
1 parent ab24c4e commit 0a093f1

File tree

3 files changed

+182
-7
lines changed

3 files changed

+182
-7
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2760,21 +2760,34 @@ Instruction *InstCombinerImpl::visitSub(BinaryOperator &I) {
27602760
// Optimize pointer differences into the same array into a size. Consider:
27612761
// &A[10] - &A[0]: we should compile this to "10".
27622762
Value *LHSOp, *RHSOp;
2763-
if (match(Op0, m_PtrToInt(m_Value(LHSOp))) &&
2764-
match(Op1, m_PtrToInt(m_Value(RHSOp))))
2763+
if (match(Op0, m_PtrToIntOrAddr(m_Value(LHSOp))) &&
2764+
match(Op1, m_PtrToIntOrAddr(m_Value(RHSOp))))
27652765
if (Value *Res = OptimizePointerDifference(LHSOp, RHSOp, I.getType(),
27662766
I.hasNoUnsignedWrap()))
27672767
return replaceInstUsesWith(I, Res);
27682768

27692769
// trunc(p)-trunc(q) -> trunc(p-q)
2770-
if (match(Op0, m_Trunc(m_PtrToInt(m_Value(LHSOp)))) &&
2771-
match(Op1, m_Trunc(m_PtrToInt(m_Value(RHSOp)))))
2770+
if (match(Op0, m_Trunc(m_PtrToIntOrAddr(m_Value(LHSOp)))) &&
2771+
match(Op1, m_Trunc(m_PtrToIntOrAddr(m_Value(RHSOp)))))
27722772
if (Value *Res = OptimizePointerDifference(LHSOp, RHSOp, I.getType(),
27732773
/* IsNUW */ false))
27742774
return replaceInstUsesWith(I, Res);
27752775

2776-
if (match(Op0, m_ZExt(m_PtrToIntSameSize(DL, m_Value(LHSOp)))) &&
2777-
match(Op1, m_ZExtOrSelf(m_PtrToInt(m_Value(RHSOp))))) {
2776+
auto MatchSubOfZExtOfPtrToIntOrAddr = [&]() {
2777+
if (match(Op0, m_ZExt(m_PtrToIntSameSize(DL, m_Value(LHSOp)))) &&
2778+
match(Op1, m_ZExt(m_PtrToIntSameSize(DL, m_Value(RHSOp)))))
2779+
return true;
2780+
if (match(Op0, m_ZExt(m_PtrToAddr(m_Value(LHSOp)))) &&
2781+
match(Op1, m_ZExt(m_PtrToAddr(m_Value(RHSOp)))))
2782+
return true;
2783+
// Special case for non-canonical ptrtoint in constant expression,
2784+
// where the zext has been folded into the ptrtoint.
2785+
if (match(Op0, m_ZExt(m_PtrToIntSameSize(DL, m_Value(LHSOp)))) &&
2786+
match(Op1, m_PtrToInt(m_Value(RHSOp))))
2787+
return true;
2788+
return false;
2789+
};
2790+
if (MatchSubOfZExtOfPtrToIntOrAddr()) {
27782791
if (auto *GEP = dyn_cast<GEPOperator>(LHSOp)) {
27792792
if (GEP->getPointerOperand() == RHSOp) {
27802793
if (GEP->hasNoUnsignedWrap() || GEP->hasNoUnsignedSignedWrap()) {

llvm/test/Transforms/InstCombine/ptrtoaddr.ll

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,134 @@ define i128 @ptrtoaddr_sext(ptr %p) {
4040
%ext = sext i64 %p.addr to i128
4141
ret i128 %ext
4242
}
43+
44+
define i64 @sub_ptrtoaddr(ptr %p, i64 %offset) {
45+
; CHECK-LABEL: define i64 @sub_ptrtoaddr(
46+
; CHECK-SAME: ptr [[P:%.*]], i64 [[OFFSET:%.*]]) {
47+
; CHECK-NEXT: ret i64 [[OFFSET]]
48+
;
49+
%p2 = getelementptr i8, ptr %p, i64 %offset
50+
%p.addr = ptrtoaddr ptr %p to i64
51+
%p2.addr = ptrtoaddr ptr %p2 to i64
52+
%sub = sub i64 %p2.addr, %p.addr
53+
ret i64 %sub
54+
}
55+
56+
define i64 @sub_ptrtoint_ptrtoaddr(ptr %p, i64 %offset) {
57+
; CHECK-LABEL: define i64 @sub_ptrtoint_ptrtoaddr(
58+
; CHECK-SAME: ptr [[P:%.*]], i64 [[OFFSET:%.*]]) {
59+
; CHECK-NEXT: ret i64 [[OFFSET]]
60+
;
61+
%p2 = getelementptr i8, ptr %p, i64 %offset
62+
%p.int = ptrtoint ptr %p to i64
63+
%p2.addr = ptrtoaddr ptr %p2 to i64
64+
%sub = sub i64 %p2.addr, %p.int
65+
ret i64 %sub
66+
}
67+
68+
define i32 @sub_ptrtoaddr_addrsize(ptr addrspace(1) %p, i32 %offset) {
69+
; CHECK-LABEL: define i32 @sub_ptrtoaddr_addrsize(
70+
; CHECK-SAME: ptr addrspace(1) [[P:%.*]], i32 [[OFFSET:%.*]]) {
71+
; CHECK-NEXT: ret i32 [[OFFSET]]
72+
;
73+
%p2 = getelementptr i8, ptr addrspace(1) %p, i32 %offset
74+
%p.addr = ptrtoaddr ptr addrspace(1) %p to i32
75+
%p2.addr = ptrtoaddr ptr addrspace(1) %p2 to i32
76+
%sub = sub i32 %p2.addr, %p.addr
77+
ret i32 %sub
78+
}
79+
80+
define i32 @sub_trunc_ptrtoaddr(ptr %p, i64 %offset) {
81+
; CHECK-LABEL: define i32 @sub_trunc_ptrtoaddr(
82+
; CHECK-SAME: ptr [[P:%.*]], i64 [[OFFSET:%.*]]) {
83+
; CHECK-NEXT: [[SUB:%.*]] = trunc i64 [[OFFSET]] to i32
84+
; CHECK-NEXT: ret i32 [[SUB]]
85+
;
86+
%p2 = getelementptr i8, ptr %p, i64 %offset
87+
%p.addr = ptrtoaddr ptr %p to i64
88+
%p2.addr = ptrtoaddr ptr %p2 to i64
89+
%p.addr.trunc = trunc i64 %p.addr to i32
90+
%p2.addr.trunc = trunc i64 %p2.addr to i32
91+
%sub = sub i32 %p2.addr.trunc, %p.addr.trunc
92+
ret i32 %sub
93+
}
94+
95+
define i16 @sub_trunc_ptrtoaddr_addrsize(ptr addrspace(1) %p, i32 %offset) {
96+
; CHECK-LABEL: define i16 @sub_trunc_ptrtoaddr_addrsize(
97+
; CHECK-SAME: ptr addrspace(1) [[P:%.*]], i32 [[OFFSET:%.*]]) {
98+
; CHECK-NEXT: [[SUB:%.*]] = trunc i32 [[OFFSET]] to i16
99+
; CHECK-NEXT: ret i16 [[SUB]]
100+
;
101+
%p2 = getelementptr i8, ptr addrspace(1) %p, i32 %offset
102+
%p.addr = ptrtoaddr ptr addrspace(1) %p to i32
103+
%p2.addr = ptrtoaddr ptr addrspace(1) %p2 to i32
104+
%p.addr.trunc = trunc i32 %p.addr to i16
105+
%p2.addr.trunc = trunc i32 %p2.addr to i16
106+
%sub = sub i16 %p2.addr.trunc, %p.addr.trunc
107+
ret i16 %sub
108+
}
109+
110+
define i16 @sub_trunc_ptrtoint_ptrtoaddr_addrsize(ptr addrspace(1) %p, i32 %offset) {
111+
; CHECK-LABEL: define i16 @sub_trunc_ptrtoint_ptrtoaddr_addrsize(
112+
; CHECK-SAME: ptr addrspace(1) [[P:%.*]], i32 [[OFFSET:%.*]]) {
113+
; CHECK-NEXT: [[SUB:%.*]] = trunc i32 [[OFFSET]] to i16
114+
; CHECK-NEXT: ret i16 [[SUB]]
115+
;
116+
%p2 = getelementptr i8, ptr addrspace(1) %p, i32 %offset
117+
%p.int = ptrtoint ptr addrspace(1) %p to i64
118+
%p2.addr = ptrtoaddr ptr addrspace(1) %p2 to i32
119+
%p.int.trunc = trunc i64 %p.int to i16
120+
%p2.addr.trunc = trunc i32 %p2.addr to i16
121+
%sub = sub i16 %p2.addr.trunc, %p.int.trunc
122+
ret i16 %sub
123+
}
124+
125+
define i128 @sub_zext_ptrtoaddr(ptr %p, i64 %offset) {
126+
; CHECK-LABEL: define i128 @sub_zext_ptrtoaddr(
127+
; CHECK-SAME: ptr [[P:%.*]], i64 [[OFFSET:%.*]]) {
128+
; CHECK-NEXT: [[SUB:%.*]] = zext i64 [[OFFSET]] to i128
129+
; CHECK-NEXT: ret i128 [[SUB]]
130+
;
131+
%p2 = getelementptr nuw i8, ptr %p, i64 %offset
132+
%p.addr = ptrtoaddr ptr %p to i64
133+
%p2.addr = ptrtoaddr ptr %p2 to i64
134+
%p.addr.ext = zext i64 %p.addr to i128
135+
%p2.addr.ext = zext i64 %p2.addr to i128
136+
%sub = sub i128 %p2.addr.ext, %p.addr.ext
137+
ret i128 %sub
138+
}
139+
140+
define i64 @sub_zext_ptrtoaddr_addrsize(ptr addrspace(1) %p, i32 %offset) {
141+
; CHECK-LABEL: define i64 @sub_zext_ptrtoaddr_addrsize(
142+
; CHECK-SAME: ptr addrspace(1) [[P:%.*]], i32 [[OFFSET:%.*]]) {
143+
; CHECK-NEXT: [[SUB:%.*]] = zext i32 [[OFFSET]] to i64
144+
; CHECK-NEXT: ret i64 [[SUB]]
145+
;
146+
%p2 = getelementptr nuw i8, ptr addrspace(1) %p, i32 %offset
147+
%p.addr = ptrtoaddr ptr addrspace(1) %p to i32
148+
%p2.addr = ptrtoaddr ptr addrspace(1) %p2 to i32
149+
%p.addr.ext = zext i32 %p.addr to i64
150+
%p2.addr.ext = zext i32 %p2.addr to i64
151+
%sub = sub i64 %p2.addr.ext, %p.addr.ext
152+
ret i64 %sub
153+
}
154+
155+
define i128 @sub_zext_ptrtoint_ptrtoaddr_addrsize(ptr addrspace(1) %p, i32 %offset) {
156+
; CHECK-LABEL: define i128 @sub_zext_ptrtoint_ptrtoaddr_addrsize(
157+
; CHECK-SAME: ptr addrspace(1) [[P:%.*]], i32 [[OFFSET:%.*]]) {
158+
; CHECK-NEXT: [[P2:%.*]] = getelementptr nuw i8, ptr addrspace(1) [[P]], i32 [[OFFSET]]
159+
; CHECK-NEXT: [[P_INT:%.*]] = ptrtoint ptr addrspace(1) [[P]] to i64
160+
; CHECK-NEXT: [[P2_ADDR:%.*]] = ptrtoaddr ptr addrspace(1) [[P2]] to i32
161+
; CHECK-NEXT: [[P_INT_EXT:%.*]] = zext i64 [[P_INT]] to i128
162+
; CHECK-NEXT: [[P2_ADDR_EXT:%.*]] = zext i32 [[P2_ADDR]] to i128
163+
; CHECK-NEXT: [[SUB:%.*]] = sub nsw i128 [[P2_ADDR_EXT]], [[P_INT_EXT]]
164+
; CHECK-NEXT: ret i128 [[SUB]]
165+
;
166+
%p2 = getelementptr nuw i8, ptr addrspace(1) %p, i32 %offset
167+
%p.int = ptrtoint ptr addrspace(1) %p to i64
168+
%p2.addr = ptrtoaddr ptr addrspace(1) %p2 to i32
169+
%p.int.ext = zext i64 %p.int to i128
170+
%p2.addr.ext = zext i32 %p2.addr to i128
171+
%sub = sub i128 %p2.addr.ext, %p.int.ext
172+
ret i128 %sub
173+
}

llvm/test/Transforms/InstCombine/sub-gep.ll

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
22
; RUN: opt -S -passes=instcombine < %s | FileCheck %s
33

4-
target datalayout = "e-p:64:64:64-p1:16:16:16-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-p2:32:32"
4+
target datalayout = "e-p:64:64:64-p1:16:16:16-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-p2:32:32-p3:32:32:32:16"
55

66
define i64 @test_inbounds(ptr %base, i64 %idx) {
77
; CHECK-LABEL: @test_inbounds(
@@ -505,6 +505,23 @@ define i64 @negative_zext_ptrtoint_sub_ptrtoint_as2_nuw(i32 %offset) {
505505
ret i64 %D
506506
}
507507

508+
define i64 @negative_zext_ptrtoint_sub_zext_ptrtoint_as2_nuw_truncating(i32 %offset) {
509+
; CHECK-LABEL: @negative_zext_ptrtoint_sub_zext_ptrtoint_as2_nuw_truncating(
510+
; CHECK-NEXT: [[A:%.*]] = getelementptr nuw bfloat, ptr addrspace(2) @Arr_as2, i32 [[OFFSET:%.*]]
511+
; CHECK-NEXT: [[A_IDX:%.*]] = ptrtoint ptr addrspace(2) [[A]] to i32
512+
; CHECK-NEXT: [[E:%.*]] = zext i32 [[A_IDX]] to i64
513+
; CHECK-NEXT: [[D:%.*]] = zext i16 ptrtoint (ptr addrspace(2) @Arr_as2 to i16) to i64
514+
; CHECK-NEXT: [[E1:%.*]] = sub nsw i64 [[E]], [[D]]
515+
; CHECK-NEXT: ret i64 [[E1]]
516+
;
517+
%A = getelementptr nuw bfloat, ptr addrspace(2) @Arr_as2, i32 %offset
518+
%B = ptrtoint ptr addrspace(2) %A to i32
519+
%C = zext i32 %B to i64
520+
%D = zext i16 ptrtoint (ptr addrspace(2) @Arr_as2 to i16) to i64
521+
%E = sub i64 %C, %D
522+
ret i64 %E
523+
}
524+
508525
define i64 @ptrtoint_sub_zext_ptrtoint_as2_inbounds_local(ptr addrspace(2) %p, i32 %offset) {
509526
; CHECK-LABEL: @ptrtoint_sub_zext_ptrtoint_as2_inbounds_local(
510527
; CHECK-NEXT: [[A:%.*]] = getelementptr inbounds bfloat, ptr addrspace(2) [[P:%.*]], i32 [[OFFSET:%.*]]
@@ -614,6 +631,20 @@ define i64 @negative_zext_ptrtoint_sub_ptrtoint_as2_nuw_local(ptr addrspace(2) %
614631
ret i64 %D
615632
}
616633

634+
define i64 @zext_ptrtoint_sub_ptrtoint_as3_nuw_local(ptr addrspace(3) %p, i16 %offset) {
635+
; CHECK-LABEL: @zext_ptrtoint_sub_ptrtoint_as3_nuw_local(
636+
; CHECK-NEXT: [[SUB:%.*]] = zext i16 [[GEP_IDX:%.*]] to i64
637+
; CHECK-NEXT: ret i64 [[SUB]]
638+
;
639+
%gep = getelementptr nuw i8, ptr addrspace(3) %p, i16 %offset
640+
%gep.int = ptrtoint ptr addrspace(3) %gep to i32
641+
%p.int = ptrtoint ptr addrspace(3) %p to i32
642+
%gep.int.ext = zext i32 %gep.int to i64
643+
%p.int.ext = zext i32 %p.int to i64
644+
%sub = sub i64 %gep.int.ext, %p.int.ext
645+
ret i64 %sub
646+
}
647+
617648
define i64 @test30(ptr %foo, i64 %i, i64 %j) {
618649
; CHECK-LABEL: @test30(
619650
; CHECK-NEXT: [[GEP1_IDX:%.*]] = shl nsw i64 [[I:%.*]], 2

0 commit comments

Comments
 (0)