Skip to content

Commit 09b0885

Browse files
authored
[LangRef] Specify icmp on pointers to only compare address (#163936)
This changes LangRef to specify that pointer icmp only compares the address bits of the pointers. That is, `icmp pred %a, %b` is equivalent to `icmp pred ptrtoaddr(%a), ptrtoaddr(%b)`. Similarly, it specifies that the `nonnull` attribute requires that the address bits are non-zero. There are a couple of motivations for this: * For inequality comparisons, this is really the only sensible semantics. Relational comparison of address and metadata bits as a single integer is generally meaningless (unless the metadata bits are equal). * This matches (as far as I understand) the behavior of existing CHERI implementations. * LLVM can only reason about the address bits. These semantics allow pointers with non-address bits to receive essentially the same comparison optimization support as ordinary pointers. In terms of implementation, this PR adjusts: * The AMDGPULowerBufferFatPointers pass. * An InstCombine fold that may replace pointers with different non-address bits. * The fold that replaces pointers based on dominating pointer equality. It does not adjust: * ISel, because we don't have in-tree targets where we can show a difference. * Various icmp+ptrtoint transforms, because we'll have to change this code for ptrtoaddr optimization support anyway, and these changes are tightly related. Related discussion starting from: https://discourse.llvm.org/t/clarifiying-the-semantics-of-ptrtoint/83987/60?u=nikic
1 parent 15d7c01 commit 09b0885

File tree

7 files changed

+34
-41
lines changed

7 files changed

+34
-41
lines changed

llvm/docs/LangRef.rst

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,6 +1556,9 @@ Currently, only the following parameter attributes are defined:
15561556
attribute may only be applied to pointer-typed parameters. This is not
15571557
checked or enforced by LLVM; if the parameter or return pointer is null,
15581558
:ref:`poison value <poisonvalues>` is returned or passed instead.
1559+
The ``nonnull`` attribute only refers to the address bits of the pointers.
1560+
If all the address bits are zero, the result will be a poison value, even
1561+
if the pointer has non-zero non-address bits or non-zero external state.
15591562
The ``nonnull`` attribute should be combined with the ``noundef`` attribute
15601563
to ensure a pointer is not null or otherwise the behavior is undefined.
15611564

@@ -13064,8 +13067,10 @@ code given as ``cond``. The comparison performed always yields either an
1306413067
#. ``sle``: interprets the operands as signed values and yields ``true``
1306513068
if ``op1`` is less than or equal to ``op2``.
1306613069

13067-
If the operands are :ref:`pointer <t_pointer>` typed, the pointer values
13068-
are compared as if they were integers.
13070+
If the operands are :ref:`pointer <t_pointer>` typed, the address bits of the
13071+
pointers are compared as if they were integers. Non-address bits or external
13072+
state are not compared. That is, ``icmp`` on pointers is equivalent to ``icmp``
13073+
on the ``ptrtoaddr`` of the pointers.
1306913074

1307013075
If the operands are integer vectors, then they are compared element by
1307113076
element. The result is an ``i1`` vector with the same number of elements

llvm/lib/Analysis/Loads.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,7 @@ Value *llvm::FindAvailableLoadedValue(LoadInst *Load, BatchAAResults &AA,
803803

804804
// Returns true if a use is either in an ICmp/PtrToInt or a Phi/Select that only
805805
// feeds into them.
806-
static bool isPointerUseReplacable(const Use &U) {
806+
static bool isPointerUseReplacable(const Use &U, bool HasNonAddressBits) {
807807
unsigned Limit = 40;
808808
SmallVector<const User *> Worklist({U.getUser()});
809809
SmallPtrSet<const User *, 8> Visited;
@@ -812,9 +812,11 @@ static bool isPointerUseReplacable(const Use &U) {
812812
auto *User = Worklist.pop_back_val();
813813
if (!Visited.insert(User).second)
814814
continue;
815+
if (isa<ICmpInst, PtrToAddrInst>(User))
816+
continue;
815817
// FIXME: The PtrToIntInst case here is not strictly correct, as it
816818
// changes which provenance is exposed.
817-
if (isa<ICmpInst, PtrToIntInst, PtrToAddrInst>(User))
819+
if (!HasNonAddressBits && isa<PtrToIntInst>(User))
818820
continue;
819821
if (isa<PHINode, SelectInst>(User))
820822
Worklist.append(User->user_begin(), User->user_end());
@@ -842,9 +844,10 @@ static bool isPointerAlwaysReplaceable(const Value *From, const Value *To,
842844

843845
bool llvm::canReplacePointersInUseIfEqual(const Use &U, const Value *To,
844846
const DataLayout &DL) {
845-
assert(U->getType() == To->getType() && "values must have matching types");
847+
Type *Ty = To->getType();
848+
assert(U->getType() == Ty && "values must have matching types");
846849
// Not a pointer, just return true.
847-
if (!To->getType()->isPointerTy())
850+
if (!Ty->isPointerTy())
848851
return true;
849852

850853
// Do not perform replacements in lifetime intrinsic arguments.
@@ -853,7 +856,10 @@ bool llvm::canReplacePointersInUseIfEqual(const Use &U, const Value *To,
853856

854857
if (isPointerAlwaysReplaceable(&*U, To, DL))
855858
return true;
856-
return isPointerUseReplacable(U);
859+
860+
bool HasNonAddressBits =
861+
DL.getAddressSizeInBits(Ty) != DL.getPointerTypeSizeInBits(Ty);
862+
return isPointerUseReplacable(U, HasNonAddressBits);
857863
}
858864

859865
bool llvm::canReplacePointersIfEqual(const Value *From, const Value *To,

llvm/lib/Target/AMDGPU/AMDGPULowerBufferFatPointers.cpp

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2062,17 +2062,7 @@ PtrParts SplitPtrStructs::visitICmpInst(ICmpInst &Cmp) {
20622062
"Pointer comparison is only equal or unequal");
20632063
auto [LhsRsrc, LhsOff] = getPtrParts(Lhs);
20642064
auto [RhsRsrc, RhsOff] = getPtrParts(Rhs);
2065-
Value *RsrcCmp =
2066-
IRB.CreateICmp(Pred, LhsRsrc, RhsRsrc, Cmp.getName() + ".rsrc");
2067-
copyMetadata(RsrcCmp, &Cmp);
2068-
Value *OffCmp = IRB.CreateICmp(Pred, LhsOff, RhsOff, Cmp.getName() + ".off");
2069-
copyMetadata(OffCmp, &Cmp);
2070-
2071-
Value *Res = nullptr;
2072-
if (Pred == ICmpInst::ICMP_EQ)
2073-
Res = IRB.CreateAnd(RsrcCmp, OffCmp);
2074-
else if (Pred == ICmpInst::ICMP_NE)
2075-
Res = IRB.CreateOr(RsrcCmp, OffCmp);
2065+
Value *Res = IRB.CreateICmp(Pred, LhsOff, RhsOff);
20762066
copyMetadata(Res, &Cmp);
20772067
Res->takeName(&Cmp);
20782068
SplitUsers.insert(&Cmp);

llvm/lib/Transforms/InstCombine/InstructionCombining.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3373,9 +3373,9 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
33733373
DL.getAddressSizeInBits(AS) != DL.getPointerSizeInBits(AS);
33743374
bool Changed = false;
33753375
GEP.replaceUsesWithIf(Y, [&](Use &U) {
3376-
bool ShouldReplace = isa<PtrToAddrInst>(U.getUser()) ||
3377-
(!HasNonAddressBits &&
3378-
isa<ICmpInst, PtrToIntInst>(U.getUser()));
3376+
bool ShouldReplace =
3377+
isa<PtrToAddrInst, ICmpInst>(U.getUser()) ||
3378+
(!HasNonAddressBits && isa<PtrToIntInst>(U.getUser()));
33793379
Changed |= ShouldReplace;
33803380
return ShouldReplace;
33813381
});

llvm/test/CodeGen/AMDGPU/lower-buffer-fat-pointers-pointer-ops.ll

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -405,9 +405,7 @@ define i1 @test_null(ptr addrspace(7) %p) {
405405
; CHECK-SAME: ({ ptr addrspace(8), i32 } [[P:%.*]]) #[[ATTR0]] {
406406
; CHECK-NEXT: [[P_RSRC:%.*]] = extractvalue { ptr addrspace(8), i32 } [[P]], 0
407407
; CHECK-NEXT: [[P_OFF:%.*]] = extractvalue { ptr addrspace(8), i32 } [[P]], 1
408-
; CHECK-NEXT: [[IS_NULL_RSRC:%.*]] = icmp eq ptr addrspace(8) [[P_RSRC]], null
409-
; CHECK-NEXT: [[IS_NULL_OFF:%.*]] = icmp eq i32 [[P_OFF]], 0
410-
; CHECK-NEXT: [[IS_NULL:%.*]] = and i1 [[IS_NULL_RSRC]], [[IS_NULL_OFF]]
408+
; CHECK-NEXT: [[IS_NULL:%.*]] = icmp eq i32 [[P_OFF]], 0
411409
; CHECK-NEXT: ret i1 [[IS_NULL]]
412410
;
413411
%is.null = icmp eq ptr addrspace(7) %p, addrspacecast (ptr null to ptr addrspace(7))
@@ -471,9 +469,7 @@ define i1 @icmp_eq(ptr addrspace(7) %a, ptr addrspace(7) %b) {
471469
; CHECK-NEXT: [[B_OFF:%.*]] = extractvalue { ptr addrspace(8), i32 } [[B]], 1
472470
; CHECK-NEXT: [[A_RSRC:%.*]] = extractvalue { ptr addrspace(8), i32 } [[A]], 0
473471
; CHECK-NEXT: [[A_OFF:%.*]] = extractvalue { ptr addrspace(8), i32 } [[A]], 1
474-
; CHECK-NEXT: [[RET_RSRC:%.*]] = icmp eq ptr addrspace(8) [[A_RSRC]], [[B_RSRC]]
475-
; CHECK-NEXT: [[RET_OFF:%.*]] = icmp eq i32 [[A_OFF]], [[B_OFF]]
476-
; CHECK-NEXT: [[RET:%.*]] = and i1 [[RET_RSRC]], [[RET_OFF]]
472+
; CHECK-NEXT: [[RET:%.*]] = icmp eq i32 [[A_OFF]], [[B_OFF]]
477473
; CHECK-NEXT: ret i1 [[RET]]
478474
;
479475
%ret = icmp eq ptr addrspace(7) %a, %b
@@ -487,9 +483,7 @@ define i1 @icmp_ne(ptr addrspace(7) %a, ptr addrspace(7) %b) {
487483
; CHECK-NEXT: [[B_OFF:%.*]] = extractvalue { ptr addrspace(8), i32 } [[B]], 1
488484
; CHECK-NEXT: [[A_RSRC:%.*]] = extractvalue { ptr addrspace(8), i32 } [[A]], 0
489485
; CHECK-NEXT: [[A_OFF:%.*]] = extractvalue { ptr addrspace(8), i32 } [[A]], 1
490-
; CHECK-NEXT: [[RET_RSRC:%.*]] = icmp ne ptr addrspace(8) [[A_RSRC]], [[B_RSRC]]
491-
; CHECK-NEXT: [[RET_OFF:%.*]] = icmp ne i32 [[A_OFF]], [[B_OFF]]
492-
; CHECK-NEXT: [[RET:%.*]] = or i1 [[RET_RSRC]], [[RET_OFF]]
486+
; CHECK-NEXT: [[RET:%.*]] = icmp ne i32 [[A_OFF]], [[B_OFF]]
493487
; CHECK-NEXT: ret i1 [[RET]]
494488
;
495489
%ret = icmp ne ptr addrspace(7) %a, %b
@@ -503,9 +497,7 @@ define <2 x i1> @icmp_eq_vec(<2 x ptr addrspace(7)> %a, <2 x ptr addrspace(7)> %
503497
; CHECK-NEXT: [[B_OFF:%.*]] = extractvalue { <2 x ptr addrspace(8)>, <2 x i32> } [[B]], 1
504498
; CHECK-NEXT: [[A_RSRC:%.*]] = extractvalue { <2 x ptr addrspace(8)>, <2 x i32> } [[A]], 0
505499
; CHECK-NEXT: [[A_OFF:%.*]] = extractvalue { <2 x ptr addrspace(8)>, <2 x i32> } [[A]], 1
506-
; CHECK-NEXT: [[RET_RSRC:%.*]] = icmp eq <2 x ptr addrspace(8)> [[A_RSRC]], [[B_RSRC]]
507-
; CHECK-NEXT: [[RET_OFF:%.*]] = icmp eq <2 x i32> [[A_OFF]], [[B_OFF]]
508-
; CHECK-NEXT: [[RET:%.*]] = and <2 x i1> [[RET_RSRC]], [[RET_OFF]]
500+
; CHECK-NEXT: [[RET:%.*]] = icmp eq <2 x i32> [[A_OFF]], [[B_OFF]]
509501
; CHECK-NEXT: ret <2 x i1> [[RET]]
510502
;
511503
%ret = icmp eq <2 x ptr addrspace(7)> %a, %b
@@ -519,9 +511,7 @@ define <2 x i1> @icmp_ne_vec(<2 x ptr addrspace(7)> %a, <2 x ptr addrspace(7)> %
519511
; CHECK-NEXT: [[B_OFF:%.*]] = extractvalue { <2 x ptr addrspace(8)>, <2 x i32> } [[B]], 1
520512
; CHECK-NEXT: [[A_RSRC:%.*]] = extractvalue { <2 x ptr addrspace(8)>, <2 x i32> } [[A]], 0
521513
; CHECK-NEXT: [[A_OFF:%.*]] = extractvalue { <2 x ptr addrspace(8)>, <2 x i32> } [[A]], 1
522-
; CHECK-NEXT: [[RET_RSRC:%.*]] = icmp ne <2 x ptr addrspace(8)> [[A_RSRC]], [[B_RSRC]]
523-
; CHECK-NEXT: [[RET_OFF:%.*]] = icmp ne <2 x i32> [[A_OFF]], [[B_OFF]]
524-
; CHECK-NEXT: [[RET:%.*]] = or <2 x i1> [[RET_RSRC]], [[RET_OFF]]
514+
; CHECK-NEXT: [[RET:%.*]] = icmp ne <2 x i32> [[A_OFF]], [[B_OFF]]
525515
; CHECK-NEXT: ret <2 x i1> [[RET]]
526516
;
527517
%ret = icmp ne <2 x ptr addrspace(7)> %a, %b

llvm/test/Transforms/GVN/assume-equal.ll

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -404,12 +404,14 @@ define i64 @assume_ptr_eq_different_prov_does_not_matter_ptrtoint(ptr %p, ptr %p
404404
ret i64 %int
405405
}
406406

407-
define i64 @assume_ptr_eq_different_prov_does_not_matter_ptrtoint_addrsize(ptr addrspace(1) %p, ptr addrspace(1) %p2) {
408-
; CHECK-LABEL: define i64 @assume_ptr_eq_different_prov_does_not_matter_ptrtoint_addrsize(
407+
; If the pointer and address size does not match, we can't replace a pointer
408+
; with different provenance in ptrtoint, as the non-address bits may not match.
409+
define i64 @assume_ptr_eq_different_prov_matters_ptrtoint_addrsize(ptr addrspace(1) %p, ptr addrspace(1) %p2) {
410+
; CHECK-LABEL: define i64 @assume_ptr_eq_different_prov_matters_ptrtoint_addrsize(
409411
; CHECK-SAME: ptr addrspace(1) [[P:%.*]], ptr addrspace(1) [[P2:%.*]]) {
410412
; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr addrspace(1) [[P]], [[P2]]
411413
; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
412-
; CHECK-NEXT: [[INT:%.*]] = ptrtoint ptr addrspace(1) [[P]] to i64
414+
; CHECK-NEXT: [[INT:%.*]] = ptrtoint ptr addrspace(1) [[P2]] to i64
413415
; CHECK-NEXT: ret i64 [[INT]]
414416
;
415417
%cmp = icmp eq ptr addrspace(1) %p, %p2

llvm/test/Transforms/InstCombine/ptrtoaddr.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ define ptr @gep_sub_ptrtoaddr_different_obj(ptr %p, ptr %p2, ptr %p3) {
206206
ret ptr %gep
207207
}
208208

209-
; The use in ptrtoaddr should be replaced. The uses in ptrtoint and icmp should
209+
; The use in ptrtoaddr and icmp should be replaced. The use in ptrtoint should
210210
; not be replaced, as the non-address bits differ. The use in the return value
211211
; should not be replaced as the provenace differs.
212212
define ptr addrspace(1) @gep_sub_ptrtoaddr_different_obj_addrsize(ptr addrspace(1) %p, ptr addrspace(1) %p2, ptr addrspace(1) %p3) {
@@ -216,7 +216,7 @@ define ptr addrspace(1) @gep_sub_ptrtoaddr_different_obj_addrsize(ptr addrspace(
216216
; CHECK-NEXT: [[P2_ADDR:%.*]] = ptrtoaddr ptr addrspace(1) [[P2]] to i32
217217
; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[P2_ADDR]], [[P_ADDR]]
218218
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr addrspace(1) [[P]], i32 [[SUB]]
219-
; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr addrspace(1) [[GEP]], [[P3]]
219+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr addrspace(1) [[P2]], [[P3]]
220220
; CHECK-NEXT: call void @use.i1(i1 [[CMP]])
221221
; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr addrspace(1) [[GEP]] to i64
222222
; CHECK-NEXT: [[INT:%.*]] = trunc i64 [[TMP1]] to i32

0 commit comments

Comments
 (0)