|
15 | 15 | #include "llvm/Transforms/IPO/FunctionAttrs.h"
|
16 | 16 | #include "llvm/ADT/ArrayRef.h"
|
17 | 17 | #include "llvm/ADT/DenseMap.h"
|
| 18 | +#include "llvm/ADT/PostOrderIterator.h" |
18 | 19 | #include "llvm/ADT/SCCIterator.h"
|
19 | 20 | #include "llvm/ADT/STLExtras.h"
|
20 | 21 | #include "llvm/ADT/SetVector.h"
|
|
36 | 37 | #include "llvm/IR/Attributes.h"
|
37 | 38 | #include "llvm/IR/BasicBlock.h"
|
38 | 39 | #include "llvm/IR/Constant.h"
|
| 40 | +#include "llvm/IR/ConstantRangeList.h" |
39 | 41 | #include "llvm/IR/Constants.h"
|
40 | 42 | #include "llvm/IR/Function.h"
|
41 | 43 | #include "llvm/IR/InstIterator.h"
|
@@ -866,9 +868,258 @@ static bool addAccessAttr(Argument *A, Attribute::AttrKind R) {
|
866 | 868 | return true;
|
867 | 869 | }
|
868 | 870 |
|
| 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 | + |
869 | 1119 | /// Deduce nocapture attributes for the SCC.
|
870 | 1120 | static void addArgumentAttrs(const SCCNodeSet &SCCNodes,
|
871 |
| - SmallSet<Function *, 8> &Changed) { |
| 1121 | + SmallSet<Function *, 8> &Changed, |
| 1122 | + bool SkipInitializes) { |
872 | 1123 | ArgumentGraph AG;
|
873 | 1124 |
|
874 | 1125 | // Check each function in turn, determining which pointer arguments are not
|
@@ -936,6 +1187,10 @@ static void addArgumentAttrs(const SCCNodeSet &SCCNodes,
|
936 | 1187 | if (addAccessAttr(&A, R))
|
937 | 1188 | Changed.insert(F);
|
938 | 1189 | }
|
| 1190 | + if (!SkipInitializes && !A.onlyReadsMemory()) { |
| 1191 | + if (inferInitializes(A, *F)) |
| 1192 | + Changed.insert(F); |
| 1193 | + } |
939 | 1194 | }
|
940 | 1195 | }
|
941 | 1196 |
|
@@ -1839,13 +2094,13 @@ deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter,
|
1839 | 2094 |
|
1840 | 2095 | SmallSet<Function *, 8> Changed;
|
1841 | 2096 | if (ArgAttrsOnly) {
|
1842 |
| - addArgumentAttrs(Nodes.SCCNodes, Changed); |
| 2097 | + addArgumentAttrs(Nodes.SCCNodes, Changed, /*SkipInitializes=*/true); |
1843 | 2098 | return Changed;
|
1844 | 2099 | }
|
1845 | 2100 |
|
1846 | 2101 | addArgumentReturnedAttrs(Nodes.SCCNodes, Changed);
|
1847 | 2102 | addMemoryAttrs(Nodes.SCCNodes, AARGetter, Changed);
|
1848 |
| - addArgumentAttrs(Nodes.SCCNodes, Changed); |
| 2103 | + addArgumentAttrs(Nodes.SCCNodes, Changed, /*SkipInitializes=*/false); |
1849 | 2104 | inferConvergent(Nodes.SCCNodes, Changed);
|
1850 | 2105 | addNoReturnAttrs(Nodes.SCCNodes, Changed);
|
1851 | 2106 | addWillReturn(Nodes.SCCNodes, Changed);
|
|
0 commit comments