Skip to content

Commit df11106

Browse files
committed
Initializes attribute inference
1 parent 689b479 commit df11106

File tree

2 files changed

+716
-3
lines changed

2 files changed

+716
-3
lines changed

llvm/lib/Transforms/IPO/FunctionAttrs.cpp

Lines changed: 258 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "llvm/Transforms/IPO/FunctionAttrs.h"
1616
#include "llvm/ADT/ArrayRef.h"
1717
#include "llvm/ADT/DenseMap.h"
18+
#include "llvm/ADT/PostOrderIterator.h"
1819
#include "llvm/ADT/SCCIterator.h"
1920
#include "llvm/ADT/STLExtras.h"
2021
#include "llvm/ADT/SetVector.h"
@@ -36,6 +37,7 @@
3637
#include "llvm/IR/Attributes.h"
3738
#include "llvm/IR/BasicBlock.h"
3839
#include "llvm/IR/Constant.h"
40+
#include "llvm/IR/ConstantRangeList.h"
3941
#include "llvm/IR/Constants.h"
4042
#include "llvm/IR/Function.h"
4143
#include "llvm/IR/InstIterator.h"
@@ -866,9 +868,258 @@ static bool addAccessAttr(Argument *A, Attribute::AttrKind R) {
866868
return true;
867869
}
868870

871+
struct InitializesUse {
872+
Use *U;
873+
std::optional<int64_t> Offset;
874+
};
875+
876+
struct InitializesInfo {
877+
std::optional<int64_t> Offset;
878+
unsigned CallBaseArgNo;
879+
bool IsClobber;
880+
};
881+
882+
struct UsesPerBlockInfo {
883+
DenseMap<Instruction *, InitializesInfo> Insts;
884+
bool HasInitializes;
885+
bool HasClobber;
886+
};
887+
888+
static bool inferInitializes(Argument &A, Function &F) {
889+
auto &DL = F.getParent()->getDataLayout();
890+
BasicBlock &EntryBB = F.getEntryBlock();
891+
DenseMap<const BasicBlock *, UsesPerBlockInfo> UsesPerBlock;
892+
SmallVector<InitializesUse, 4> Worklist;
893+
for (Use &U : A.uses()) {
894+
Worklist.push_back({&U, 0});
895+
}
896+
897+
bool HasAnyInitialize = false;
898+
bool HasInitializeOutsideEntryBB = false;
899+
auto PointerSize =
900+
DL.getIndexSizeInBits(A.getType()->getPointerAddressSpace());
901+
// No need for a visited set because we don't look through phis, so there are
902+
// no cycles.
903+
while (!Worklist.empty()) {
904+
InitializesUse IU = Worklist.pop_back_val();
905+
User *U = IU.U->getUser();
906+
// Add GEP uses to worklist.
907+
// If the GEP is not a constant GEP, set IsInitialize to false.
908+
if (auto *GEP = dyn_cast<GEPOperator>(U)) {
909+
APInt Offset(PointerSize, 0,
910+
/*isSigned=*/true);
911+
bool IsConstGEP = GEP->accumulateConstantOffset(DL, Offset);
912+
std::optional<int64_t> NewOffset = std::nullopt;
913+
if (IsConstGEP && IU.Offset.has_value()) {
914+
NewOffset = *IU.Offset + Offset.getSExtValue();
915+
}
916+
for (Use &U : GEP->uses()) {
917+
Worklist.push_back({&U, NewOffset});
918+
}
919+
continue;
920+
}
921+
922+
auto *I = cast<Instruction>(U);
923+
auto *BB = I->getParent();
924+
auto &BBInfo = UsesPerBlock.getOrInsertDefault(BB);
925+
bool AlreadyVisitedInst = BBInfo.Insts.contains(I);
926+
auto &IInfo = BBInfo.Insts[I];
927+
bool AddedToBBInfo = false;
928+
929+
if (auto *SI = dyn_cast<StoreInst>(I)) {
930+
bool StoringToPointer = &SI->getOperandUse(1) == IU.U;
931+
IInfo = {StoringToPointer ? IU.Offset : std::nullopt, 0,
932+
!StoringToPointer};
933+
AddedToBBInfo = true;
934+
} else if (auto *MemSet = dyn_cast<MemSetInst>(I)) {
935+
IInfo = {MemSet->isVolatile() ? std::nullopt : IU.Offset, 0,
936+
MemSet->isVolatile()};
937+
AddedToBBInfo = true;
938+
} else if (auto *MemCpy = dyn_cast<MemCpyInst>(I)) {
939+
if (&MemCpy->getOperandUse(0) == IU.U) {
940+
IInfo = {MemCpy->isVolatile() ? std::nullopt : IU.Offset, 0,
941+
MemCpy->isVolatile()};
942+
} else {
943+
IInfo = {std::nullopt, 0, true};
944+
}
945+
AddedToBBInfo = true;
946+
} else if (auto *CB = dyn_cast<CallBase>(I)) {
947+
if (CB->isArgOperand(IU.U)) {
948+
unsigned ArgNo = CB->getArgOperandNo(IU.U);
949+
bool IsInitialize = CB->paramHasAttr(ArgNo, Attribute::Initializes);
950+
// Argument is only not clobbered when parameter is writeonly/readnone
951+
// and nocapture.
952+
bool IsClobber = !(CB->onlyWritesMemory(ArgNo) &&
953+
CB->paramHasAttr(ArgNo, Attribute::NoCapture));
954+
IInfo = {IsInitialize ? IU.Offset : std::nullopt, ArgNo, IsClobber};
955+
} else {
956+
IInfo = {std::nullopt, 0, true};
957+
}
958+
AddedToBBInfo = true;
959+
}
960+
// Unrecognized instructions and instructions that have more than one use of
961+
// the argument are considered clobbers.
962+
if (!AddedToBBInfo || AlreadyVisitedInst) {
963+
IInfo = {std::nullopt, 0, true};
964+
}
965+
BBInfo.HasClobber |= IInfo.IsClobber;
966+
BBInfo.HasInitializes |= IInfo.Offset.has_value();
967+
968+
HasAnyInitialize |= IInfo.Offset.has_value();
969+
970+
if (IInfo.Offset.has_value() && BB != &EntryBB) {
971+
HasInitializeOutsideEntryBB = true;
972+
}
973+
}
974+
975+
// No initialization anywhere in the function, bail.
976+
if (!HasAnyInitialize) {
977+
return false;
978+
}
979+
980+
// TODO: a load doesn't clobber the entire range
981+
DenseMap<const BasicBlock *, ConstantRangeList> Initialized;
982+
auto VisitBlock = [&](const BasicBlock *BB) -> ConstantRangeList {
983+
auto UPB = UsesPerBlock.find(BB);
984+
985+
// If this block has uses and none are initializes, the argument is not
986+
// initialized in this block.
987+
if (UPB != UsesPerBlock.end() && !UPB->second.HasInitializes) {
988+
return ConstantRangeList();
989+
}
990+
991+
ConstantRangeList CRL;
992+
993+
// Start with intersection of successors.
994+
// If this block has any non-initializing use, we're going to clear out the
995+
// ranges at some point in this block anyway, so don't bother looking at
996+
// successors.
997+
if (UPB == UsesPerBlock.end() || !UPB->second.HasClobber) {
998+
bool HasAddedSuccessor = false;
999+
for (auto *Succ : successors(BB)) {
1000+
if (auto SuccI = Initialized.find(Succ); SuccI != Initialized.end()) {
1001+
if (HasAddedSuccessor) {
1002+
CRL = CRL.intersectWith(SuccI->second);
1003+
} else {
1004+
CRL = SuccI->second;
1005+
HasAddedSuccessor = true;
1006+
}
1007+
} else {
1008+
CRL = ConstantRangeList();
1009+
break;
1010+
}
1011+
}
1012+
}
1013+
1014+
if (UPB != UsesPerBlock.end()) {
1015+
// Sort uses in this block by instruction order.
1016+
SmallVector<std::pair<Instruction *, InitializesInfo>, 2> Insts;
1017+
append_range(Insts, UPB->second.Insts);
1018+
sort(Insts, [](std::pair<Instruction *, InitializesInfo> &LHS,
1019+
std::pair<Instruction *, InitializesInfo> &RHS) {
1020+
return LHS.first->comesBefore(RHS.first);
1021+
});
1022+
1023+
// From the end of the block to the beginning of the block, set
1024+
// initializes ranges.
1025+
for (auto [I, Info] : reverse(Insts)) {
1026+
if (Info.IsClobber) {
1027+
CRL = ConstantRangeList();
1028+
}
1029+
if (Info.Offset.has_value()) {
1030+
int64_t Offset = *Info.Offset;
1031+
if (auto *SI = dyn_cast<StoreInst>(I)) {
1032+
int64_t StoreSize = DL.getTypeStoreSize(SI->getAccessType());
1033+
CRL.insert(ConstantRange(APInt(64, Offset, true),
1034+
APInt(64, Offset + StoreSize, true)));
1035+
} else if (auto *MemSet = dyn_cast<MemSetInst>(I)) {
1036+
if (auto *Len = dyn_cast<ConstantInt>(MemSet->getLength())) {
1037+
CRL.insert(
1038+
ConstantRange(APInt(64, Offset, true),
1039+
APInt(64, Offset + Len->getSExtValue(), true)));
1040+
}
1041+
} else if (auto *MemCpy = dyn_cast<MemCpyInst>(I)) {
1042+
if (auto *Len = dyn_cast<ConstantInt>(MemCpy->getLength())) {
1043+
CRL.insert(
1044+
ConstantRange(APInt(64, Offset, true),
1045+
APInt(64, Offset + Len->getSExtValue(), true)));
1046+
}
1047+
} else if (auto *CB = dyn_cast<CallBase>(I)) {
1048+
assert(
1049+
CB->paramHasAttr(Info.CallBaseArgNo, Attribute::Initializes));
1050+
// FIXME: check callee in CB->getParamAttr
1051+
Attribute Attr =
1052+
CB->getParamAttr(Info.CallBaseArgNo, Attribute::Initializes);
1053+
if (!Attr.isValid()) {
1054+
Attr = CB->getCalledFunction()->getParamAttribute(
1055+
Info.CallBaseArgNo, Attribute::Initializes);
1056+
}
1057+
ConstantRangeList CBCRL = Attr.getValueAsConstantRangeList();
1058+
for (ConstantRange &CR : CBCRL) {
1059+
CR =
1060+
ConstantRange(CR.getLower() + Offset, CR.getUpper() + Offset);
1061+
}
1062+
CRL = CRL.unionWith(CBCRL);
1063+
}
1064+
}
1065+
}
1066+
}
1067+
return CRL;
1068+
};
1069+
1070+
ConstantRangeList EntryCRL;
1071+
// If all initializing instructions are in the EntryBB, or if the EntryBB has
1072+
// a clobbering use, we only need to look at EntryBB.
1073+
bool OnlyScanEntryBlock = !HasInitializeOutsideEntryBB;
1074+
if (!OnlyScanEntryBlock) {
1075+
if (auto EntryUPB = UsesPerBlock.find(&EntryBB);
1076+
EntryUPB != UsesPerBlock.end()) {
1077+
OnlyScanEntryBlock = EntryUPB->second.HasClobber;
1078+
}
1079+
}
1080+
if (OnlyScanEntryBlock) {
1081+
EntryCRL = VisitBlock(&EntryBB);
1082+
if (EntryCRL.empty()) {
1083+
return false;
1084+
}
1085+
} else {
1086+
// Visit successors before predecessors with a post-order walk of the blocks.
1087+
for (const BasicBlock *BB : post_order(&F)) {
1088+
ConstantRangeList CRL = VisitBlock(BB);
1089+
if (!CRL.empty()) {
1090+
Initialized[BB] = CRL;
1091+
}
1092+
}
1093+
1094+
auto EntryCRLI = Initialized.find(&EntryBB);
1095+
if (EntryCRLI == Initialized.end()) {
1096+
return false;
1097+
}
1098+
1099+
EntryCRL = EntryCRLI->second;
1100+
}
1101+
1102+
assert(!EntryCRL.empty() &&
1103+
"should have bailed already if EntryCRL is empty");
1104+
1105+
if (A.hasAttribute(Attribute::Initializes)) {
1106+
ConstantRangeList PreviousCRL =
1107+
A.getAttribute(Attribute::Initializes).getValueAsConstantRangeList();
1108+
if (PreviousCRL == EntryCRL) {
1109+
return false;
1110+
}
1111+
EntryCRL = EntryCRL.unionWith(PreviousCRL);
1112+
}
1113+
1114+
A.addAttr(Attribute::get(A.getContext(), Attribute::Initializes, EntryCRL));
1115+
1116+
return true;
1117+
}
1118+
8691119
/// Deduce nocapture attributes for the SCC.
8701120
static void addArgumentAttrs(const SCCNodeSet &SCCNodes,
871-
SmallSet<Function *, 8> &Changed) {
1121+
SmallSet<Function *, 8> &Changed,
1122+
bool SkipInitializes) {
8721123
ArgumentGraph AG;
8731124

8741125
// Check each function in turn, determining which pointer arguments are not
@@ -936,6 +1187,10 @@ static void addArgumentAttrs(const SCCNodeSet &SCCNodes,
9361187
if (addAccessAttr(&A, R))
9371188
Changed.insert(F);
9381189
}
1190+
if (!SkipInitializes && !A.onlyReadsMemory()) {
1191+
if (inferInitializes(A, *F))
1192+
Changed.insert(F);
1193+
}
9391194
}
9401195
}
9411196

@@ -1839,13 +2094,13 @@ deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter,
18392094

18402095
SmallSet<Function *, 8> Changed;
18412096
if (ArgAttrsOnly) {
1842-
addArgumentAttrs(Nodes.SCCNodes, Changed);
2097+
addArgumentAttrs(Nodes.SCCNodes, Changed, /*SkipInitializes=*/true);
18432098
return Changed;
18442099
}
18452100

18462101
addArgumentReturnedAttrs(Nodes.SCCNodes, Changed);
18472102
addMemoryAttrs(Nodes.SCCNodes, AARGetter, Changed);
1848-
addArgumentAttrs(Nodes.SCCNodes, Changed);
2103+
addArgumentAttrs(Nodes.SCCNodes, Changed, /*SkipInitializes=*/false);
18492104
inferConvergent(Nodes.SCCNodes, Changed);
18502105
addNoReturnAttrs(Nodes.SCCNodes, Changed);
18512106
addWillReturn(Nodes.SCCNodes, Changed);

0 commit comments

Comments
 (0)