Skip to content

Commit 1510b3d

Browse files
committed
SROA: Recognize llvm.protected.field.ptr intrinsics.
When an alloc slice's users include llvm.protected.field.ptr intrinsics and their discriminators are consistent, drop the intrinsics in order to avoid unnecessary pointer sign and auth operations. Pull Request: llvm#151650
1 parent e4a29e1 commit 1510b3d

File tree

4 files changed

+114
-6
lines changed

4 files changed

+114
-6
lines changed

llvm/include/llvm/Analysis/PtrUseVisitor.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ class PtrUseVisitorBase {
134134

135135
UseAndIsOffsetKnownPair UseAndIsOffsetKnown;
136136
APInt Offset;
137+
Value *ProtectedFieldDisc;
137138
};
138139

139140
/// The worklist of to-visit uses.
@@ -158,6 +159,10 @@ class PtrUseVisitorBase {
158159
/// The constant offset of the use if that is known.
159160
APInt Offset;
160161

162+
// When this access is via an llvm.protected.field.ptr intrinsic, contains
163+
// the second argument to the intrinsic, the discriminator.
164+
Value *ProtectedFieldDisc;
165+
161166
/// @}
162167

163168
/// Note that the constructor is protected because this class must be a base
@@ -230,6 +235,7 @@ class PtrUseVisitor : protected InstVisitor<DerivedT>,
230235
IntegerType *IntIdxTy = cast<IntegerType>(DL.getIndexType(I.getType()));
231236
IsOffsetKnown = true;
232237
Offset = APInt(IntIdxTy->getBitWidth(), 0);
238+
ProtectedFieldDisc = nullptr;
233239
PI.reset();
234240

235241
// Enqueue the uses of this pointer.
@@ -242,6 +248,7 @@ class PtrUseVisitor : protected InstVisitor<DerivedT>,
242248
IsOffsetKnown = ToVisit.UseAndIsOffsetKnown.getInt();
243249
if (IsOffsetKnown)
244250
Offset = std::move(ToVisit.Offset);
251+
ProtectedFieldDisc = ToVisit.ProtectedFieldDisc;
245252

246253
Instruction *I = cast<Instruction>(U->getUser());
247254
static_cast<DerivedT*>(this)->visit(I);
@@ -300,6 +307,14 @@ class PtrUseVisitor : protected InstVisitor<DerivedT>,
300307
case Intrinsic::lifetime_start:
301308
case Intrinsic::lifetime_end:
302309
return; // No-op intrinsics.
310+
311+
case Intrinsic::protected_field_ptr: {
312+
if (!IsOffsetKnown)
313+
return Base::visitIntrinsicInst(II);
314+
ProtectedFieldDisc = II.getArgOperand(1);
315+
enqueueUsers(II);
316+
break;
317+
}
303318
}
304319
}
305320

llvm/lib/Analysis/PtrUseVisitor.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ void detail::PtrUseVisitorBase::enqueueUsers(Value &I) {
2222
if (VisitedUses.insert(&U).second) {
2323
UseToVisit NewU = {
2424
UseToVisit::UseAndIsOffsetKnownPair(&U, IsOffsetKnown),
25-
Offset
25+
Offset,
26+
ProtectedFieldDisc,
2627
};
2728
Worklist.push_back(std::move(NewU));
2829
}

llvm/lib/Transforms/Scalar/SROA.cpp

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
#include "llvm/IR/Instruction.h"
6363
#include "llvm/IR/Instructions.h"
6464
#include "llvm/IR/IntrinsicInst.h"
65+
#include "llvm/IR/Intrinsics.h"
6566
#include "llvm/IR/LLVMContext.h"
6667
#include "llvm/IR/Metadata.h"
6768
#include "llvm/IR/Module.h"
@@ -523,9 +524,10 @@ class Slice {
523524
public:
524525
Slice() = default;
525526

526-
Slice(uint64_t BeginOffset, uint64_t EndOffset, Use *U, bool IsSplittable)
527+
Slice(uint64_t BeginOffset, uint64_t EndOffset, Use *U, bool IsSplittable,
528+
Value *ProtectedFieldDisc)
527529
: BeginOffset(BeginOffset), EndOffset(EndOffset),
528-
UseAndIsSplittable(U, IsSplittable) {}
530+
UseAndIsSplittable(U, IsSplittable), ProtectedFieldDisc(ProtectedFieldDisc) {}
529531

530532
uint64_t beginOffset() const { return BeginOffset; }
531533
uint64_t endOffset() const { return EndOffset; }
@@ -538,6 +540,10 @@ class Slice {
538540
bool isDead() const { return getUse() == nullptr; }
539541
void kill() { UseAndIsSplittable.setPointer(nullptr); }
540542

543+
// When this access is via an llvm.protected.field.ptr intrinsic, contains
544+
// the second argument to the intrinsic, the discriminator.
545+
Value *ProtectedFieldDisc;
546+
541547
/// Support for ordering ranges.
542548
///
543549
/// This provides an ordering over ranges such that start offsets are
@@ -631,6 +637,9 @@ class AllocaSlices {
631637
/// Access the dead users for this alloca.
632638
ArrayRef<Instruction *> getDeadUsers() const { return DeadUsers; }
633639

640+
/// Access the PFP users for this alloca.
641+
ArrayRef<IntrinsicInst *> getPFPUsers() const { return PFPUsers; }
642+
634643
/// Access Uses that should be dropped if the alloca is promotable.
635644
ArrayRef<Use *> getDeadUsesIfPromotable() const {
636645
return DeadUseIfPromotable;
@@ -691,6 +700,10 @@ class AllocaSlices {
691700
/// they come from outside of the allocated space.
692701
SmallVector<Instruction *, 8> DeadUsers;
693702

703+
/// Users that are llvm.protected.field.ptr intrinsics. These will be RAUW'd
704+
/// to their first argument if we rewrite the alloca.
705+
SmallVector<IntrinsicInst *, 0> PFPUsers;
706+
694707
/// Uses which will become dead if can promote the alloca.
695708
SmallVector<Use *, 8> DeadUseIfPromotable;
696709

@@ -1064,7 +1077,8 @@ class AllocaSlices::SliceBuilder : public PtrUseVisitor<SliceBuilder> {
10641077
EndOffset = AllocSize;
10651078
}
10661079

1067-
AS.Slices.push_back(Slice(BeginOffset, EndOffset, U, IsSplittable));
1080+
AS.Slices.push_back(
1081+
Slice(BeginOffset, EndOffset, U, IsSplittable, ProtectedFieldDisc));
10681082
}
10691083

10701084
void visitBitCastInst(BitCastInst &BC) {
@@ -1274,6 +1288,9 @@ class AllocaSlices::SliceBuilder : public PtrUseVisitor<SliceBuilder> {
12741288
return;
12751289
}
12761290

1291+
if (II.getIntrinsicID() == Intrinsic::protected_field_ptr)
1292+
AS.PFPUsers.push_back(&II);
1293+
12771294
Base::visitIntrinsicInst(II);
12781295
}
12791296

@@ -4682,7 +4699,7 @@ bool SROA::presplitLoadsAndStores(AllocaInst &AI, AllocaSlices &AS) {
46824699
NewSlices.push_back(
46834700
Slice(BaseOffset + PartOffset, BaseOffset + PartOffset + PartSize,
46844701
&PLoad->getOperandUse(PLoad->getPointerOperandIndex()),
4685-
/*IsSplittable*/ false));
4702+
/*IsSplittable*/ false, nullptr));
46864703
LLVM_DEBUG(dbgs() << " new slice [" << NewSlices.back().beginOffset()
46874704
<< ", " << NewSlices.back().endOffset()
46884705
<< "): " << *PLoad << "\n");
@@ -4838,10 +4855,12 @@ bool SROA::presplitLoadsAndStores(AllocaInst &AI, AllocaSlices &AS) {
48384855
LLVMContext::MD_access_group});
48394856

48404857
// Now build a new slice for the alloca.
4858+
// ProtectedFieldDisc==nullptr is a lie, but it doesn't matter because we
4859+
// already determined that all accesses are consistent.
48414860
NewSlices.push_back(
48424861
Slice(BaseOffset + PartOffset, BaseOffset + PartOffset + PartSize,
48434862
&PStore->getOperandUse(PStore->getPointerOperandIndex()),
4844-
/*IsSplittable*/ false));
4863+
/*IsSplittable*/ false, nullptr));
48454864
LLVM_DEBUG(dbgs() << " new slice [" << NewSlices.back().beginOffset()
48464865
<< ", " << NewSlices.back().endOffset()
48474866
<< "): " << *PStore << "\n");
@@ -5618,6 +5637,32 @@ SROA::runOnAlloca(AllocaInst &AI) {
56185637
return {Changed, CFGChanged};
56195638
}
56205639

5640+
for (auto &P : AS.partitions()) {
5641+
std::optional<Value *> ProtectedFieldDisc;
5642+
// For now, we can't split if a field is accessed both via protected
5643+
// field and not.
5644+
for (Slice &S : P) {
5645+
if (auto *II = dyn_cast<IntrinsicInst>(S.getUse()->getUser()))
5646+
if (II->getIntrinsicID() == Intrinsic::lifetime_start ||
5647+
II->getIntrinsicID() == Intrinsic::lifetime_end)
5648+
continue;
5649+
if (!ProtectedFieldDisc)
5650+
ProtectedFieldDisc = S.ProtectedFieldDisc;
5651+
if (*ProtectedFieldDisc != S.ProtectedFieldDisc)
5652+
return {Changed, CFGChanged};
5653+
}
5654+
for (Slice *S : P.splitSliceTails()) {
5655+
if (auto *II = dyn_cast<IntrinsicInst>(S->getUse()->getUser()))
5656+
if (II->getIntrinsicID() == Intrinsic::lifetime_start ||
5657+
II->getIntrinsicID() == Intrinsic::lifetime_end)
5658+
continue;
5659+
if (!ProtectedFieldDisc)
5660+
ProtectedFieldDisc = S->ProtectedFieldDisc;
5661+
if (*ProtectedFieldDisc != S->ProtectedFieldDisc)
5662+
return {Changed, CFGChanged};
5663+
}
5664+
}
5665+
56215666
// Delete all the dead users of this alloca before splitting and rewriting it.
56225667
for (Instruction *DeadUser : AS.getDeadUsers()) {
56235668
// Free up everything used by this instruction.
@@ -5635,6 +5680,12 @@ SROA::runOnAlloca(AllocaInst &AI) {
56355680
clobberUse(*DeadOp);
56365681
Changed = true;
56375682
}
5683+
for (IntrinsicInst *PFPUser : AS.getPFPUsers()) {
5684+
PFPUser->replaceAllUsesWith(PFPUser->getArgOperand(0));
5685+
5686+
DeadInsts.push_back(PFPUser);
5687+
Changed = true;
5688+
}
56385689

56395690
// No slices to split. Leave the dead alloca for a later pass to clean up.
56405691
if (AS.begin() == AS.end())
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
; RUN: opt -passes=sroa -S < %s | FileCheck %s
2+
3+
target triple = "aarch64-unknown-linux-gnu"
4+
5+
; CHECK: define void @slice
6+
define void @slice(ptr %ptr1, ptr %ptr2, ptr %out1, ptr %out2) {
7+
%alloca = alloca { ptr, ptr }
8+
9+
%protptrptr1.1 = call ptr @llvm.protected.field.ptr(ptr %alloca, i64 1, i1 true)
10+
store ptr %ptr1, ptr %protptrptr1.1
11+
%protptrptr1.2 = call ptr @llvm.protected.field.ptr(ptr %alloca, i64 1, i1 true)
12+
%ptr1a = load ptr, ptr %protptrptr1.2
13+
14+
%gep = getelementptr { ptr, ptr }, ptr %alloca, i64 0, i32 1
15+
%protptrptr2.1 = call ptr @llvm.protected.field.ptr(ptr %gep, i64 2, i1 true)
16+
store ptr %ptr2, ptr %protptrptr1.1
17+
%protptrptr2.2 = call ptr @llvm.protected.field.ptr(ptr %gep, i64 2, i1 true)
18+
%ptr2a = load ptr, ptr %protptrptr1.2
19+
20+
; CHECK-NEXT: store ptr %ptr1, ptr %out1, align 8
21+
store ptr %ptr1a, ptr %out1
22+
; CHECK-NEXT: store ptr %ptr2, ptr %out2, align 8
23+
store ptr %ptr2a, ptr %out2
24+
ret void
25+
}
26+
27+
; CHECK: define ptr @mixed
28+
define ptr @mixed(ptr %ptr) {
29+
; CHECK-NEXT: %alloca = alloca ptr, align 8
30+
%alloca = alloca ptr
31+
32+
; CHECK-NEXT: store ptr %ptr, ptr %alloca, align 8
33+
store ptr %ptr, ptr %alloca
34+
; CHECK-NEXT: %protptrptr1.2 = call ptr @llvm.protected.field.ptr(ptr %alloca, i64 1, i1 true)
35+
%protptrptr1.2 = call ptr @llvm.protected.field.ptr(ptr %alloca, i64 1, i1 true)
36+
; CHECK-NEXT: %ptr1a = load ptr, ptr %protptrptr1.2, align 8
37+
%ptr1a = load ptr, ptr %protptrptr1.2
38+
39+
; CHECK-NEXT: ret ptr %ptr
40+
ret ptr %ptr
41+
}

0 commit comments

Comments
 (0)