Skip to content

Commit 7da1da8

Browse files
nikicLukacma
authored andcommitted
[InstSimplify] Support ptrtoaddr in simplifyGEPInst() (llvm#164262)
This adds support for ptrtoaddr in the `ptradd p, ptrtoaddr(p2) - ptrtoaddr(p) -> p2` fold. This fold requires that p and p2 have the same underlying object (otherwise the provenance may not be the same). The argument I would like to make here is that because the underlying objects are the same (and the pointers in the same address space), the non-address bits of the pointer must be the same. Looking at some specific cases of underlying object relationship: * phi/select: Trivially true. * getelementptr: Only modifies address bits, non-address bits must remain the same. * addrspacecast round-trip cast: Must preserve all bits because we optimize such round-trip casts away. * non-interposable global alias: I'm a bit unsure about this one, but I guess the alias and the aliasee must have the same non-address bits? * various intrinsics like launder.invariant.group, ptrmask. I think these all either preserve all pointer bits (like the invariant.group ones) or at least the non-address bits (like ptrmask). There are some interesting cases like amdgcn.make.buffer.rsrc, but those are cross address-space. ----- There is a second `gep (gep p, C), (sub 0, ptrtoint(p)) -> C` transform in this function, which I am not extending to handle ptrtoaddr, adding negative tests instead. This transform is overall dubious for provenance reasons, but especially dubious with ptrtoaddr, as then we don't have the guarantee that provenance of `p` has been exposed.
1 parent e089aab commit 7da1da8

File tree

2 files changed

+119
-9
lines changed

2 files changed

+119
-9
lines changed

llvm/lib/Analysis/InstructionSimplify.cpp

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5106,32 +5106,33 @@ static Value *simplifyGEPInst(Type *SrcTy, Value *Ptr,
51065106
return Ptr;
51075107

51085108
// The following transforms are only safe if the ptrtoint cast
5109-
// doesn't truncate the pointers.
5110-
if (Indices[0]->getType()->getScalarSizeInBits() ==
5111-
Q.DL.getPointerSizeInBits(AS)) {
5109+
// doesn't truncate the address of the pointers. The non-address bits
5110+
// must be the same, as the underlying objects are the same.
5111+
if (Indices[0]->getType()->getScalarSizeInBits() >=
5112+
Q.DL.getAddressSizeInBits(AS)) {
51125113
auto CanSimplify = [GEPTy, &P, Ptr]() -> bool {
51135114
return P->getType() == GEPTy &&
51145115
getUnderlyingObject(P) == getUnderlyingObject(Ptr);
51155116
};
51165117
// getelementptr V, (sub P, V) -> P if P points to a type of size 1.
51175118
if (TyAllocSize == 1 &&
5118-
match(Indices[0],
5119-
m_Sub(m_PtrToInt(m_Value(P)), m_PtrToInt(m_Specific(Ptr)))) &&
5119+
match(Indices[0], m_Sub(m_PtrToIntOrAddr(m_Value(P)),
5120+
m_PtrToIntOrAddr(m_Specific(Ptr)))) &&
51205121
CanSimplify())
51215122
return P;
51225123

51235124
// getelementptr V, (ashr (sub P, V), C) -> P if P points to a type of
51245125
// size 1 << C.
5125-
if (match(Indices[0], m_AShr(m_Sub(m_PtrToInt(m_Value(P)),
5126-
m_PtrToInt(m_Specific(Ptr))),
5126+
if (match(Indices[0], m_AShr(m_Sub(m_PtrToIntOrAddr(m_Value(P)),
5127+
m_PtrToIntOrAddr(m_Specific(Ptr))),
51275128
m_ConstantInt(C))) &&
51285129
TyAllocSize == 1ULL << C && CanSimplify())
51295130
return P;
51305131

51315132
// getelementptr V, (sdiv (sub P, V), C) -> P if P points to a type of
51325133
// size C.
5133-
if (match(Indices[0], m_SDiv(m_Sub(m_PtrToInt(m_Value(P)),
5134-
m_PtrToInt(m_Specific(Ptr))),
5134+
if (match(Indices[0], m_SDiv(m_Sub(m_PtrToIntOrAddr(m_Value(P)),
5135+
m_PtrToIntOrAddr(m_Specific(Ptr))),
51355136
m_SpecificInt(TyAllocSize))) &&
51365137
CanSimplify())
51375138
return P;

llvm/test/Transforms/InstSimplify/ptrtoaddr.ll

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,3 +207,112 @@ define i32 @ptrtoaddr_of_ptradd_of_sub_addrsize(i32 %x, ptr addrspace(1) %p) {
207207
%ptradd.addr = ptrtoaddr ptr addrspace(1) %ptradd to i32
208208
ret i32 %ptradd.addr
209209
}
210+
211+
define ptr @gep_of_sub_ptrtoaddr_unrelated_pointers(ptr %p, ptr %p2, i64 %x) {
212+
; CHECK-LABEL: define ptr @gep_of_sub_ptrtoaddr_unrelated_pointers(
213+
; CHECK-SAME: ptr [[P:%.*]], ptr [[P2:%.*]], i64 [[X:%.*]]) {
214+
; CHECK-NEXT: [[P2_ADDR:%.*]] = ptrtoaddr ptr [[P2]] to i64
215+
; CHECK-NEXT: [[P_ADDR:%.*]] = ptrtoaddr ptr [[P]] to i64
216+
; CHECK-NEXT: [[SUB:%.*]] = sub i64 [[P2_ADDR]], [[P_ADDR]]
217+
; CHECK-NEXT: [[GEP2:%.*]] = getelementptr i8, ptr [[P]], i64 [[SUB]]
218+
; CHECK-NEXT: ret ptr [[GEP2]]
219+
;
220+
%p2.addr = ptrtoaddr ptr %p2 to i64
221+
%p.addr = ptrtoaddr ptr %p to i64
222+
%sub = sub i64 %p2.addr, %p.addr
223+
%gep2 = getelementptr i8, ptr %p, i64 %sub
224+
ret ptr %gep2
225+
}
226+
227+
define ptr @gep_of_sub_ptrtoaddr(ptr %p, i64 %x) {
228+
; CHECK-LABEL: define ptr @gep_of_sub_ptrtoaddr(
229+
; CHECK-SAME: ptr [[P:%.*]], i64 [[X:%.*]]) {
230+
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr i8, ptr [[P]], i64 [[X]]
231+
; CHECK-NEXT: ret ptr [[GEP1]]
232+
;
233+
%gep1 = getelementptr i8, ptr %p, i64 %x
234+
%gep1.addr = ptrtoaddr ptr %gep1 to i64
235+
%p.addr = ptrtoaddr ptr %p to i64
236+
%sub = sub i64 %gep1.addr, %p.addr
237+
%gep2 = getelementptr i8, ptr %p, i64 %sub
238+
ret ptr %gep2
239+
}
240+
241+
define ptr addrspace(1) @gep_of_sub_ptrtoaddr_addrsize(ptr addrspace(1) %p, i32 %x) {
242+
; CHECK-LABEL: define ptr addrspace(1) @gep_of_sub_ptrtoaddr_addrsize(
243+
; CHECK-SAME: ptr addrspace(1) [[P:%.*]], i32 [[X:%.*]]) {
244+
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr i8, ptr addrspace(1) [[P]], i32 [[X]]
245+
; CHECK-NEXT: ret ptr addrspace(1) [[GEP1]]
246+
;
247+
%gep1 = getelementptr i8, ptr addrspace(1) %p, i32 %x
248+
%gep1.addr = ptrtoaddr ptr addrspace(1) %gep1 to i32
249+
%p.addr = ptrtoaddr ptr addrspace(1) %p to i32
250+
%sub = sub i32 %gep1.addr, %p.addr
251+
%gep2 = getelementptr i8, ptr addrspace(1) %p, i32 %sub
252+
ret ptr addrspace(1) %gep2
253+
}
254+
255+
define ptr @gep_of_sub_ptrtoaddr_ashr(ptr %p, i64 %x) {
256+
; CHECK-LABEL: define ptr @gep_of_sub_ptrtoaddr_ashr(
257+
; CHECK-SAME: ptr [[P:%.*]], i64 [[X:%.*]]) {
258+
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr i8, ptr [[P]], i64 [[X]]
259+
; CHECK-NEXT: ret ptr [[GEP1]]
260+
;
261+
%gep1 = getelementptr i8, ptr %p, i64 %x
262+
%gep1.addr = ptrtoaddr ptr %gep1 to i64
263+
%p.addr = ptrtoaddr ptr %p to i64
264+
%sub = sub i64 %gep1.addr, %p.addr
265+
%ashr = ashr i64 %sub, 1
266+
%gep2 = getelementptr i16, ptr %p, i64 %ashr
267+
ret ptr %gep2
268+
}
269+
270+
define ptr addrspace(1) @gep_of_sub_ptrtoaddr_ashr_addrsize(ptr addrspace(1) %p, i32 %x) {
271+
; CHECK-LABEL: define ptr addrspace(1) @gep_of_sub_ptrtoaddr_ashr_addrsize(
272+
; CHECK-SAME: ptr addrspace(1) [[P:%.*]], i32 [[X:%.*]]) {
273+
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr i8, ptr addrspace(1) [[P]], i32 [[X]]
274+
; CHECK-NEXT: ret ptr addrspace(1) [[GEP1]]
275+
;
276+
%gep1 = getelementptr i8, ptr addrspace(1) %p, i32 %x
277+
%gep1.addr = ptrtoaddr ptr addrspace(1) %gep1 to i32
278+
%p.addr = ptrtoaddr ptr addrspace(1) %p to i32
279+
%sub = sub i32 %gep1.addr, %p.addr
280+
%sdiv = sdiv i32 %sub, 3
281+
%gep2 = getelementptr [3 x i8], ptr addrspace(1) %p, i32 %sdiv
282+
ret ptr addrspace(1) %gep2
283+
}
284+
285+
; Not folding this to inttoptr(123), as this may have different provenance from
286+
; %p, and the use of ptrtoaddr implies that the provenance of %p may not be
287+
; exposed, such that inttoptr cannot recover it.
288+
define ptr @gep_gep_neg_ptrtoaddr(ptr %p) {
289+
; CHECK-LABEL: define ptr @gep_gep_neg_ptrtoaddr(
290+
; CHECK-SAME: ptr [[P:%.*]]) {
291+
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 123
292+
; CHECK-NEXT: [[P_ADDR:%.*]] = ptrtoaddr ptr [[P]] to i64
293+
; CHECK-NEXT: [[P_ADDR_NEG:%.*]] = sub i64 0, [[P_ADDR]]
294+
; CHECK-NEXT: [[GEP2:%.*]] = getelementptr i8, ptr [[GEP1]], i64 [[P_ADDR_NEG]]
295+
; CHECK-NEXT: ret ptr [[GEP2]]
296+
;
297+
%gep1 = getelementptr inbounds i8, ptr %p, i64 123
298+
%p.addr = ptrtoaddr ptr %p to i64
299+
%p.addr.neg = sub i64 0, %p.addr
300+
%gep2 = getelementptr i8, ptr %gep1, i64 %p.addr.neg
301+
ret ptr %gep2
302+
}
303+
304+
define ptr @gep_gep_inv_ptrtoaddr(ptr %p) {
305+
; CHECK-LABEL: define ptr @gep_gep_inv_ptrtoaddr(
306+
; CHECK-SAME: ptr [[P:%.*]]) {
307+
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 123
308+
; CHECK-NEXT: [[P_ADDR:%.*]] = ptrtoaddr ptr [[P]] to i64
309+
; CHECK-NEXT: [[P_ADDR_INV:%.*]] = xor i64 [[P_ADDR]], -1
310+
; CHECK-NEXT: [[GEP2:%.*]] = getelementptr i8, ptr [[GEP1]], i64 [[P_ADDR_INV]]
311+
; CHECK-NEXT: ret ptr [[GEP2]]
312+
;
313+
%gep1 = getelementptr inbounds i8, ptr %p, i64 123
314+
%p.addr = ptrtoaddr ptr %p to i64
315+
%p.addr.inv = xor i64 %p.addr, -1
316+
%gep2 = getelementptr i8, ptr %gep1, i64 %p.addr.inv
317+
ret ptr %gep2
318+
}

0 commit comments

Comments
 (0)