Skip to content

Commit 335e137

Browse files
jdoerfertJohannes Doerfert
andauthored
[Attributor][FIX] Track returned pointer offsets (#110534)
If the pointer returned by a function is not "the base pointer" but has an offset, we need to track the offset such that users can apply it to their offset chain when they create accesses. This was reported by @ye-luo and reduced test cases are included. The OffsetInfo was moved and the container was replaced with a set to avoid excessive growth. Otherwise, the patch just replaces the "returns pointer" flag with the "returned offsets", and deals with the applying to offsets at the call site. --------- Co-authored-by: Johannes Doerfert <[email protected]>
1 parent b16e694 commit 335e137

File tree

3 files changed

+296
-70
lines changed

3 files changed

+296
-70
lines changed

llvm/include/llvm/Transforms/IPO/Attributor.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5786,6 +5786,53 @@ struct AAPointerInfo : public AbstractAttribute {
57865786
AK_MUST_READ_WRITE = AK_MUST | AK_R | AK_W,
57875787
};
57885788

5789+
/// A helper containing a list of offsets computed for a Use. Ideally this
5790+
/// list should be strictly ascending, but we ensure that only when we
5791+
/// actually translate the list of offsets to a RangeList.
5792+
struct OffsetInfo {
5793+
using VecTy = SmallSet<int64_t, 4>;
5794+
using const_iterator = VecTy::const_iterator;
5795+
VecTy Offsets;
5796+
5797+
const_iterator begin() const { return Offsets.begin(); }
5798+
const_iterator end() const { return Offsets.end(); }
5799+
5800+
bool operator==(const OffsetInfo &RHS) const {
5801+
return Offsets == RHS.Offsets;
5802+
}
5803+
5804+
bool operator!=(const OffsetInfo &RHS) const { return !(*this == RHS); }
5805+
5806+
bool insert(int64_t Offset) { return Offsets.insert(Offset).second; }
5807+
bool isUnassigned() const { return Offsets.size() == 0; }
5808+
5809+
bool isUnknown() const {
5810+
if (isUnassigned())
5811+
return false;
5812+
if (Offsets.size() == 1)
5813+
return *Offsets.begin() == AA::RangeTy::Unknown;
5814+
return false;
5815+
}
5816+
5817+
void setUnknown() {
5818+
Offsets.clear();
5819+
Offsets.insert(AA::RangeTy::Unknown);
5820+
}
5821+
5822+
void addToAll(int64_t Inc) {
5823+
VecTy NewOffsets;
5824+
for (auto &Offset : Offsets)
5825+
NewOffsets.insert(Offset + Inc);
5826+
Offsets = std::move(NewOffsets);
5827+
}
5828+
5829+
/// Copy offsets from \p R into the current list.
5830+
///
5831+
/// Ideally all lists should be strictly ascending, but we defer that to the
5832+
/// actual use of the list. So we just blindly append here.
5833+
bool merge(const OffsetInfo &R) { return set_union(Offsets, R.Offsets); }
5834+
};
5835+
57895836
/// A container for a list of ranges.
57905837
struct RangeList {
57915838
// The set of ranges rarely contains more than one element, and is unlikely
@@ -6123,6 +6170,7 @@ struct AAPointerInfo : public AbstractAttribute {
61236170
virtual const_bin_iterator end() const = 0;
61246171
virtual int64_t numOffsetBins() const = 0;
61256172
virtual bool reachesReturn() const = 0;
6173+
virtual void addReturnedOffsetsTo(OffsetInfo &) const = 0;
61266174

61276175
/// Call \p CB on all accesses that might interfere with \p Range and return
61286176
/// true if all such accesses were known and the callback returned true for

llvm/lib/Transforms/IPO/AttributorAttributes.cpp

Lines changed: 57 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,7 @@ struct AA::PointerInfo::State : public AbstractState {
827827
AccessList = R.AccessList;
828828
OffsetBins = R.OffsetBins;
829829
RemoteIMap = R.RemoteIMap;
830-
ReachesReturn = R.ReachesReturn;
830+
ReturnedOffsets = R.ReturnedOffsets;
831831
return *this;
832832
}
833833

@@ -838,7 +838,7 @@ struct AA::PointerInfo::State : public AbstractState {
838838
std::swap(AccessList, R.AccessList);
839839
std::swap(OffsetBins, R.OffsetBins);
840840
std::swap(RemoteIMap, R.RemoteIMap);
841-
std::swap(ReachesReturn, R.ReachesReturn);
841+
std::swap(ReturnedOffsets, R.ReturnedOffsets);
842842
return *this;
843843
}
844844

@@ -883,13 +883,13 @@ struct AA::PointerInfo::State : public AbstractState {
883883
/// Flag to determine if the underlying pointer is reaching a return statement
884884
/// in the associated function or not. Returns in other functions cause
885885
/// invalidation.
886-
bool ReachesReturn = false;
886+
AAPointerInfo::OffsetInfo ReturnedOffsets;
887887

888888
/// See AAPointerInfo::forallInterferingAccesses.
889889
bool forallInterferingAccesses(
890890
AA::RangeTy Range,
891891
function_ref<bool(const AAPointerInfo::Access &, bool)> CB) const {
892-
if (!isValidState() || ReachesReturn)
892+
if (!isValidState() || !ReturnedOffsets.isUnassigned())
893893
return false;
894894

895895
for (const auto &It : OffsetBins) {
@@ -911,7 +911,7 @@ struct AA::PointerInfo::State : public AbstractState {
911911
Instruction &I,
912912
function_ref<bool(const AAPointerInfo::Access &, bool)> CB,
913913
AA::RangeTy &Range) const {
914-
if (!isValidState() || ReachesReturn)
914+
if (!isValidState() || !ReturnedOffsets.isUnassigned())
915915
return false;
916916

917917
auto LocalList = RemoteIMap.find(&I);
@@ -1010,54 +1010,9 @@ ChangeStatus AA::PointerInfo::State::addAccess(
10101010

10111011
namespace {
10121012

1013-
/// A helper containing a list of offsets computed for a Use. Ideally this
1014-
/// list should be strictly ascending, but we ensure that only when we
1015-
/// actually translate the list of offsets to a RangeList.
1016-
struct OffsetInfo {
1017-
using VecTy = SmallVector<int64_t>;
1018-
using const_iterator = VecTy::const_iterator;
1019-
VecTy Offsets;
1020-
1021-
const_iterator begin() const { return Offsets.begin(); }
1022-
const_iterator end() const { return Offsets.end(); }
1023-
1024-
bool operator==(const OffsetInfo &RHS) const {
1025-
return Offsets == RHS.Offsets;
1026-
}
1027-
1028-
bool operator!=(const OffsetInfo &RHS) const { return !(*this == RHS); }
1029-
1030-
void insert(int64_t Offset) { Offsets.push_back(Offset); }
1031-
bool isUnassigned() const { return Offsets.size() == 0; }
1032-
1033-
bool isUnknown() const {
1034-
if (isUnassigned())
1035-
return false;
1036-
if (Offsets.size() == 1)
1037-
return Offsets.front() == AA::RangeTy::Unknown;
1038-
return false;
1039-
}
1040-
1041-
void setUnknown() {
1042-
Offsets.clear();
1043-
Offsets.push_back(AA::RangeTy::Unknown);
1044-
}
1045-
1046-
void addToAll(int64_t Inc) {
1047-
for (auto &Offset : Offsets) {
1048-
Offset += Inc;
1049-
}
1050-
}
1051-
1052-
/// Copy offsets from \p R into the current list.
1053-
///
1054-
/// Ideally all lists should be strictly ascending, but we defer that to the
1055-
/// actual use of the list. So we just blindly append here.
1056-
void merge(const OffsetInfo &R) { Offsets.append(R.Offsets); }
1057-
};
1058-
10591013
#ifndef NDEBUG
1060-
static raw_ostream &operator<<(raw_ostream &OS, const OffsetInfo &OI) {
1014+
static raw_ostream &operator<<(raw_ostream &OS,
1015+
const AAPointerInfo::OffsetInfo &OI) {
10611016
ListSeparator LS;
10621017
OS << "[";
10631018
for (auto Offset : OI) {
@@ -1079,7 +1034,13 @@ struct AAPointerInfoImpl
10791034
(isValidState() ? (std::string("#") +
10801035
std::to_string(OffsetBins.size()) + " bins")
10811036
: "<invalid>") +
1082-
(ReachesReturn ? " (returned)" : "");
1037+
(reachesReturn()
1038+
? (" (returned:" +
1039+
join(map_range(ReturnedOffsets,
1040+
[](int64_t O) { return std::to_string(O); }),
1041+
", ") +
1042+
")")
1043+
: "");
10831044
}
10841045

10851046
/// See AbstractAttribute::manifest(...).
@@ -1092,13 +1053,34 @@ struct AAPointerInfoImpl
10921053
virtual int64_t numOffsetBins() const override {
10931054
return State::numOffsetBins();
10941055
}
1095-
virtual bool reachesReturn() const override { return ReachesReturn; }
1096-
ChangeStatus setReachesReturn(bool Val) {
1097-
if (ReachesReturn == Val)
1098-
return ChangeStatus::UNCHANGED;
1056+
virtual bool reachesReturn() const override {
1057+
return !ReturnedOffsets.isUnassigned();
1058+
}
1059+
virtual void addReturnedOffsetsTo(OffsetInfo &OI) const override {
1060+
if (ReturnedOffsets.isUnknown()) {
1061+
OI.setUnknown();
1062+
return;
1063+
}
10991064

1100-
ReachesReturn = Val;
1101-
return ChangeStatus::CHANGED;
1065+
OffsetInfo MergedOI;
1066+
for (auto Offset : ReturnedOffsets) {
1067+
OffsetInfo TmpOI = OI;
1068+
TmpOI.addToAll(Offset);
1069+
MergedOI.merge(TmpOI);
1070+
}
1071+
OI = std::move(MergedOI);
1072+
}
1073+
1074+
ChangeStatus setReachesReturn(const OffsetInfo &ReachedReturnedOffsets) {
1075+
if (ReturnedOffsets.isUnknown())
1076+
return ChangeStatus::UNCHANGED;
1077+
if (ReachedReturnedOffsets.isUnknown()) {
1078+
ReturnedOffsets.setUnknown();
1079+
return ChangeStatus::CHANGED;
1080+
}
1081+
if (ReturnedOffsets.merge(ReachedReturnedOffsets))
1082+
return ChangeStatus::CHANGED;
1083+
return ChangeStatus::UNCHANGED;
11021084
}
11031085

11041086
bool forallInterferingAccesses(
@@ -1390,7 +1372,7 @@ struct AAPointerInfoImpl
13901372
ChangeStatus Changed = ChangeStatus::UNCHANGED;
13911373
const auto &OtherAAImpl = static_cast<const AAPointerInfoImpl &>(OtherAA);
13921374
bool IsByval = OtherAAImpl.getAssociatedArgument()->hasByValAttr();
1393-
Changed |= setReachesReturn(OtherAAImpl.ReachesReturn);
1375+
Changed |= setReachesReturn(OtherAAImpl.ReturnedOffsets);
13941376

13951377
// Combine the accesses bin by bin.
13961378
const auto &State = OtherAAImpl.getState();
@@ -1485,7 +1467,7 @@ struct AAPointerInfoFloating : public AAPointerInfoImpl {
14851467
/// Deal with an access and signal if it was handled successfully.
14861468
bool handleAccess(Attributor &A, Instruction &I,
14871469
std::optional<Value *> Content, AccessKind Kind,
1488-
SmallVectorImpl<int64_t> &Offsets, ChangeStatus &Changed,
1470+
OffsetInfo::VecTy &Offsets, ChangeStatus &Changed,
14891471
Type &Ty) {
14901472
using namespace AA::PointerInfo;
14911473
auto Size = AA::RangeTy::Unknown;
@@ -1495,16 +1477,16 @@ struct AAPointerInfoFloating : public AAPointerInfoImpl {
14951477
Size = AccessSize.getFixedValue();
14961478

14971479
// Make a strictly ascending list of offsets as required by addAccess()
1498-
llvm::sort(Offsets);
1499-
auto *Last = llvm::unique(Offsets);
1500-
Offsets.erase(Last, Offsets.end());
1480+
SmallVector<int64_t> OffsetsSorted(Offsets.begin(), Offsets.end());
1481+
llvm::sort(OffsetsSorted);
15011482

15021483
VectorType *VT = dyn_cast<VectorType>(&Ty);
15031484
if (!VT || VT->getElementCount().isScalable() ||
15041485
!Content.value_or(nullptr) || !isa<Constant>(*Content) ||
15051486
(*Content)->getType() != VT ||
15061487
DL.getTypeStoreSize(VT->getElementType()).isScalable()) {
1507-
Changed = Changed | addAccess(A, {Offsets, Size}, I, Content, Kind, &Ty);
1488+
Changed =
1489+
Changed | addAccess(A, {OffsetsSorted, Size}, I, Content, Kind, &Ty);
15081490
} else {
15091491
// Handle vector stores with constant content element-wise.
15101492
// TODO: We could look for the elements or create instructions
@@ -1689,8 +1671,12 @@ ChangeStatus AAPointerInfoFloating::updateImpl(Attributor &A) {
16891671
// then check the call site return. Returns from other functions can't be
16901672
// tracked and are cause for invalidation.
16911673
if (auto *RI = dyn_cast<ReturnInst>(Usr)) {
1692-
Changed |= setReachesReturn(RI->getFunction() == getAssociatedFunction());
1693-
return ReachesReturn;
1674+
if (RI->getFunction() == getAssociatedFunction()) {
1675+
auto &PtrOI = OffsetInfoMap[CurPtr];
1676+
Changed |= setReachesReturn(PtrOI);
1677+
return true;
1678+
}
1679+
return false;
16941680
}
16951681

16961682
// For PHIs we need to take care of the recurrence explicitly as the value
@@ -1946,9 +1932,10 @@ ChangeStatus AAPointerInfoFloating::updateImpl(Attributor &A) {
19461932
*this, IRPosition::callsite_returned(*CB), DepClassTy::REQUIRED);
19471933
if (!CSRetPI)
19481934
return false;
1949-
Changed = translateAndAddState(A, *CSRetPI, OffsetInfoMap[CurPtr], *CB,
1950-
IsRetMustAcc) |
1951-
Changed;
1935+
OffsetInfo OI = OffsetInfoMap[CurPtr];
1936+
CSArgPI->addReturnedOffsetsTo(OI);
1937+
Changed =
1938+
translateAndAddState(A, *CSRetPI, OI, *CB, IsRetMustAcc) | Changed;
19521939
return isValidState();
19531940
}
19541941
LLVM_DEBUG(dbgs() << "[AAPointerInfo] Call user not handled " << *CB

0 commit comments

Comments
 (0)