52
52
#include " llvm/IR/Argument.h"
53
53
#include " llvm/IR/BasicBlock.h"
54
54
#include " llvm/IR/Constant.h"
55
+ #include " llvm/IR/ConstantRangeList.h"
55
56
#include " llvm/IR/Constants.h"
56
57
#include " llvm/IR/DataLayout.h"
57
58
#include " llvm/IR/DebugInfo.h"
@@ -164,6 +165,10 @@ static cl::opt<bool>
164
165
OptimizeMemorySSA (" dse-optimize-memoryssa" , cl::init(true ), cl::Hidden,
165
166
cl::desc(" Allow DSE to optimize memory accesses." ));
166
167
168
+ static cl::opt<bool > EnableInitializesImprovement (
169
+ " enable-dse-initializes-attr-improvement" , cl::init(false ), cl::Hidden,
170
+ cl::desc(" Enable the initializes attr improvement in DSE" ));
171
+
167
172
// ===----------------------------------------------------------------------===//
168
173
// Helper functions
169
174
// ===----------------------------------------------------------------------===//
@@ -809,8 +814,10 @@ bool canSkipDef(MemoryDef *D, bool DefVisibleToCaller) {
809
814
// A memory location wrapper that represents a MemoryLocation, `MemLoc`,
810
815
// defined by `MemDef`.
811
816
struct MemoryLocationWrapper {
812
- MemoryLocationWrapper (MemoryLocation MemLoc, MemoryDef *MemDef)
813
- : MemLoc(MemLoc), MemDef(MemDef) {
817
+ MemoryLocationWrapper (MemoryLocation MemLoc, MemoryDef *MemDef,
818
+ bool DefByInitializesAttr)
819
+ : MemLoc(MemLoc), MemDef(MemDef),
820
+ DefByInitializesAttr (DefByInitializesAttr) {
814
821
assert (MemLoc.Ptr && " MemLoc should be not null" );
815
822
UnderlyingObject = getUnderlyingObject (MemLoc.Ptr );
816
823
DefInst = MemDef->getMemoryInst ();
@@ -820,20 +827,121 @@ struct MemoryLocationWrapper {
820
827
const Value *UnderlyingObject;
821
828
MemoryDef *MemDef;
822
829
Instruction *DefInst;
830
+ bool DefByInitializesAttr = false ;
823
831
};
824
832
825
833
// A memory def wrapper that represents a MemoryDef and the MemoryLocation(s)
826
834
// defined by this MemoryDef.
827
835
struct MemoryDefWrapper {
828
- MemoryDefWrapper (MemoryDef *MemDef, std::optional<MemoryLocation> MemLoc) {
836
+ MemoryDefWrapper (
837
+ MemoryDef *MemDef,
838
+ const SmallVectorImpl<std::pair<MemoryLocation, bool >> &MemLocations) {
829
839
DefInst = MemDef->getMemoryInst ();
830
- if (MemLoc.has_value ())
831
- DefinedLocation = MemoryLocationWrapper (*MemLoc, MemDef);
840
+ for (auto &[MemLoc, DefByInitializesAttr] : MemLocations)
841
+ DefinedLocations.push_back (
842
+ MemoryLocationWrapper (MemLoc, MemDef, DefByInitializesAttr));
832
843
}
833
844
Instruction *DefInst;
834
- std::optional<MemoryLocationWrapper> DefinedLocation = std::nullopt;
845
+ SmallVector<MemoryLocationWrapper, 1 > DefinedLocations;
846
+ };
847
+
848
+ bool HasInitializesAttr (Instruction *I) {
849
+ CallBase *CB = dyn_cast<CallBase>(I);
850
+ if (!CB)
851
+ return false ;
852
+
853
+ for (size_t Idx = 0 ; Idx < CB->arg_size (); Idx++)
854
+ if (CB->paramHasAttr (Idx, Attribute::Initializes))
855
+ return true ;
856
+ return false ;
857
+ }
858
+
859
+ struct ArgumentInitInfo {
860
+ size_t Idx = -1 ;
861
+ ConstantRangeList Inits;
862
+ bool HasDeadOnUnwindAttr = false ;
863
+ bool FuncHasNoUnwindAttr = false ;
835
864
};
836
865
866
+ ConstantRangeList
867
+ GetMergedInitAttr (const SmallVectorImpl<ArgumentInitInfo> &Args) {
868
+ if (Args.empty ())
869
+ return {};
870
+
871
+ // To address unwind, the function should have nounwind attribute or the
872
+ // arguments have dead_on_unwind attribute. Otherwise, return empty.
873
+ for (const auto &Arg : Args) {
874
+ if (!Arg.FuncHasNoUnwindAttr && !Arg.HasDeadOnUnwindAttr )
875
+ return {};
876
+ if (Arg.Inits .empty ())
877
+ return {};
878
+ }
879
+
880
+ if (Args.size () == 1 )
881
+ return Args[0 ].Inits ;
882
+
883
+ ConstantRangeList MergedIntervals = Args[0 ].Inits ;
884
+ for (size_t i = 1 ; i < Args.size (); i++) {
885
+ MergedIntervals = MergedIntervals.intersectWith (Args[i].Inits );
886
+ }
887
+ return MergedIntervals;
888
+ }
889
+
890
+ // Return the locations wrote by the initializes attribute.
891
+ // Note that this function considers:
892
+ // 1. Unwind edge: apply "initializes" attribute only if the callee has
893
+ // "nounwind" attribute or the argument has "dead_on_unwind" attribute.
894
+ // 2. Argument alias: for aliasing arguments, the "initializes" attribute is
895
+ // the merged range list of their "initializes" attributes.
896
+ SmallVector<MemoryLocation, 1 >
897
+ GetInitializesArgMemLoc (const Instruction *I, BatchAAResults &BatchAA) {
898
+ const CallBase *CB = dyn_cast<CallBase>(I);
899
+ if (!CB)
900
+ return {};
901
+
902
+ bool HasNoUnwindAttr = CB->hasFnAttr (Attribute::NoUnwind);
903
+ SmallMapVector<Value *, SmallVector<ArgumentInitInfo, 2 >, 2 > Arguments;
904
+ for (size_t Idx = 0 ; Idx < CB->arg_size (); Idx++) {
905
+ bool HasDeadOnUnwindAttr = CB->paramHasAttr (Idx, Attribute::DeadOnUnwind);
906
+
907
+ ConstantRangeList Inits;
908
+ if (CB->paramHasAttr (Idx, Attribute::Initializes))
909
+ Inits = CB->getParamAttr (Idx, Attribute::Initializes)
910
+ .getValueAsConstantRangeList ();
911
+
912
+ ArgumentInitInfo InitInfo{Idx, Inits, HasDeadOnUnwindAttr, HasNoUnwindAttr};
913
+ Value *CurArg = CB->getArgOperand (Idx);
914
+ bool FoundAliasing = false ;
915
+ for (auto &[Arg, AliasList] : Arguments) {
916
+ if (BatchAA.isMustAlias (Arg, CurArg)) {
917
+ FoundAliasing = true ;
918
+ AliasList.push_back (InitInfo);
919
+ }
920
+ }
921
+ if (!FoundAliasing)
922
+ Arguments[CurArg] = {InitInfo};
923
+ }
924
+
925
+ SmallVector<MemoryLocation, 1 > Locations;
926
+ for (const auto &[_, Args] : Arguments) {
927
+ auto MergedInitAttr = GetMergedInitAttr (Args);
928
+ if (MergedInitAttr.empty ())
929
+ continue ;
930
+
931
+ for (const auto &Arg : Args) {
932
+ for (const auto &Range : MergedInitAttr) {
933
+ int64_t Start = Range.getLower ().getSExtValue ();
934
+ int64_t End = Range.getUpper ().getSExtValue ();
935
+ if (Start == 0 )
936
+ Locations.push_back (MemoryLocation (CB->getArgOperand (Arg.Idx ),
937
+ LocationSize::precise (End - Start),
938
+ CB->getAAMetadata ()));
939
+ }
940
+ }
941
+ }
942
+ return Locations;
943
+ }
944
+
837
945
struct DSEState {
838
946
Function &F;
839
947
AliasAnalysis &AA;
@@ -911,7 +1019,8 @@ struct DSEState {
911
1019
912
1020
auto *MD = dyn_cast_or_null<MemoryDef>(MA);
913
1021
if (MD && MemDefs.size () < MemorySSADefsPerBlockLimit &&
914
- (getLocForWrite (&I) || isMemTerminatorInst (&I)))
1022
+ (getLocForWrite (&I) || isMemTerminatorInst (&I) ||
1023
+ HasInitializesAttr (&I)))
915
1024
MemDefs.push_back (MD);
916
1025
}
917
1026
}
@@ -1147,13 +1256,24 @@ struct DSEState {
1147
1256
return MemoryLocation::getOrNone (I);
1148
1257
}
1149
1258
1150
- std::optional<MemoryLocation> getLocForInst (Instruction *I) {
1259
+ SmallVector<std::pair<MemoryLocation, bool >, 1 >
1260
+ getLocForInst (Instruction *I, bool consider_initializes_attr) {
1261
+ SmallVector<std::pair<MemoryLocation, bool >, 1 > Locations;
1151
1262
if (isMemTerminatorInst (I)) {
1152
- if (auto Loc = getLocForTerminator (I)) {
1153
- return Loc->first ;
1263
+ if (auto Loc = getLocForTerminator (I))
1264
+ Locations.push_back (std::make_pair (Loc->first , false ));
1265
+ return Locations;
1266
+ }
1267
+
1268
+ if (auto Loc = getLocForWrite (I))
1269
+ Locations.push_back (std::make_pair (*Loc, false ));
1270
+
1271
+ if (consider_initializes_attr) {
1272
+ for (auto &MemLoc : GetInitializesArgMemLoc (I, BatchAA)) {
1273
+ Locations.push_back (std::make_pair (MemLoc, true ));
1154
1274
}
1155
1275
}
1156
- return getLocForWrite (I) ;
1276
+ return Locations ;
1157
1277
}
1158
1278
1159
1279
// / Assuming this instruction has a dead analyzable write, can we delete
@@ -1365,7 +1485,8 @@ struct DSEState {
1365
1485
getDomMemoryDef (MemoryDef *KillingDef, MemoryAccess *StartAccess,
1366
1486
const MemoryLocation &KillingLoc, const Value *KillingUndObj,
1367
1487
unsigned &ScanLimit, unsigned &WalkerStepLimit,
1368
- bool IsMemTerm, unsigned &PartialLimit) {
1488
+ bool IsMemTerm, unsigned &PartialLimit,
1489
+ bool IsInitializesAttrMemLoc) {
1369
1490
if (ScanLimit == 0 || WalkerStepLimit == 0 ) {
1370
1491
LLVM_DEBUG (dbgs () << " \n ... hit scan limit\n " );
1371
1492
return std::nullopt;
@@ -1602,7 +1723,19 @@ struct DSEState {
1602
1723
1603
1724
// Uses which may read the original MemoryDef mean we cannot eliminate the
1604
1725
// original MD. Stop walk.
1605
- if (isReadClobber (MaybeDeadLoc, UseInst)) {
1726
+ // If KillingDef is a CallInst with "initializes" attribute, the reads in
1727
+ // Callee would be dominated by initializations, so this should be safe.
1728
+ bool IsKillingDefFromInitAttr = false ;
1729
+ if (IsInitializesAttrMemLoc) {
1730
+ if (KillingI == UseInst &&
1731
+ KillingUndObj == getUnderlyingObject (MaybeDeadLoc.Ptr )) {
1732
+ IsKillingDefFromInitAttr = true ;
1733
+ // Note that, we don't need to check aliasing arguments here since
1734
+ // aliasing has been considered at the begining.
1735
+ }
1736
+ }
1737
+
1738
+ if (isReadClobber (MaybeDeadLoc, UseInst) && !IsKillingDefFromInitAttr) {
1606
1739
LLVM_DEBUG (dbgs () << " ... found read clobber\n " );
1607
1740
return std::nullopt;
1608
1741
}
@@ -2207,7 +2340,8 @@ DSEState::eliminateDeadDefs(const MemoryLocationWrapper &KillingLocWrapper) {
2207
2340
std::optional<MemoryAccess *> MaybeDeadAccess = getDomMemoryDef (
2208
2341
KillingLocWrapper.MemDef , Current, KillingLocWrapper.MemLoc ,
2209
2342
KillingLocWrapper.UnderlyingObject , ScanLimit, WalkerStepLimit,
2210
- isMemTerminatorInst (KillingLocWrapper.DefInst ), PartialLimit);
2343
+ isMemTerminatorInst (KillingLocWrapper.DefInst ), PartialLimit,
2344
+ KillingLocWrapper.DefByInitializesAttr );
2211
2345
2212
2346
if (!MaybeDeadAccess) {
2213
2347
LLVM_DEBUG (dbgs () << " finished walk\n " );
@@ -2232,8 +2366,11 @@ DSEState::eliminateDeadDefs(const MemoryLocationWrapper &KillingLocWrapper) {
2232
2366
}
2233
2367
MemoryDefWrapper DeadDefWrapper (
2234
2368
cast<MemoryDef>(DeadAccess),
2235
- getLocForInst (cast<MemoryDef>(DeadAccess)->getMemoryInst ()));
2236
- MemoryLocationWrapper &DeadLocWrapper = *DeadDefWrapper.DefinedLocation ;
2369
+ getLocForInst (cast<MemoryDef>(DeadAccess)->getMemoryInst (),
2370
+ /* consider_initializes_attr=*/ false ));
2371
+ assert (DeadDefWrapper.DefinedLocations .size () == 1 );
2372
+ MemoryLocationWrapper &DeadLocWrapper =
2373
+ DeadDefWrapper.DefinedLocations .front ();
2237
2374
LLVM_DEBUG (dbgs () << " (" << *DeadLocWrapper.DefInst << " )\n " );
2238
2375
ToCheck.insert (DeadLocWrapper.MemDef ->getDefiningAccess ());
2239
2376
NumGetDomMemoryDefPassed++;
@@ -2311,37 +2448,41 @@ DSEState::eliminateDeadDefs(const MemoryLocationWrapper &KillingLocWrapper) {
2311
2448
}
2312
2449
2313
2450
bool DSEState::eliminateDeadDefs (const MemoryDefWrapper &KillingDefWrapper) {
2314
- if (! KillingDefWrapper.DefinedLocation . has_value ()) {
2451
+ if (KillingDefWrapper.DefinedLocations . empty ()) {
2315
2452
LLVM_DEBUG (dbgs () << " Failed to find analyzable write location for "
2316
2453
<< *KillingDefWrapper.DefInst << " \n " );
2317
2454
return false ;
2318
2455
}
2319
2456
2320
- auto &KillingLocWrapper = *KillingDefWrapper.DefinedLocation ;
2321
- LLVM_DEBUG (dbgs () << " Trying to eliminate MemoryDefs killed by "
2322
- << *KillingLocWrapper.MemDef << " ("
2323
- << *KillingLocWrapper.DefInst << " )\n " );
2324
- auto [Changed, DeletedKillingLoc] = eliminateDeadDefs (KillingLocWrapper);
2325
-
2326
- // Check if the store is a no-op.
2327
- if (!DeletedKillingLoc && storeIsNoop (KillingLocWrapper.MemDef ,
2328
- KillingLocWrapper.UnderlyingObject )) {
2329
- LLVM_DEBUG (dbgs () << " DSE: Remove No-Op Store:\n DEAD: "
2330
- << *KillingLocWrapper.DefInst << ' \n ' );
2331
- deleteDeadInstruction (KillingLocWrapper.DefInst );
2332
- NumRedundantStores++;
2333
- return true ;
2334
- }
2335
- // Can we form a calloc from a memset/malloc pair?
2336
- if (!DeletedKillingLoc &&
2337
- tryFoldIntoCalloc (KillingLocWrapper.MemDef ,
2338
- KillingLocWrapper.UnderlyingObject )) {
2339
- LLVM_DEBUG (dbgs () << " DSE: Remove memset after forming calloc:\n "
2340
- << " DEAD: " << *KillingLocWrapper.DefInst << ' \n ' );
2341
- deleteDeadInstruction (KillingLocWrapper.DefInst );
2342
- return true ;
2457
+ bool MadeChange = false ;
2458
+ for (auto &KillingLocWrapper : KillingDefWrapper.DefinedLocations ) {
2459
+ LLVM_DEBUG (dbgs () << " Trying to eliminate MemoryDefs killed by "
2460
+ << *KillingLocWrapper.MemDef << " ("
2461
+ << *KillingLocWrapper.DefInst << " )\n " );
2462
+ auto [Changed, DeletedKillingLoc] = eliminateDeadDefs (KillingLocWrapper);
2463
+
2464
+ // Check if the store is a no-op.
2465
+ if (!DeletedKillingLoc && storeIsNoop (KillingLocWrapper.MemDef ,
2466
+ KillingLocWrapper.UnderlyingObject )) {
2467
+ LLVM_DEBUG (dbgs () << " DSE: Remove No-Op Store:\n DEAD: "
2468
+ << *KillingLocWrapper.DefInst << ' \n ' );
2469
+ deleteDeadInstruction (KillingLocWrapper.DefInst );
2470
+ NumRedundantStores++;
2471
+ MadeChange = true ;
2472
+ continue ;
2473
+ }
2474
+ // Can we form a calloc from a memset/malloc pair?
2475
+ if (!DeletedKillingLoc &&
2476
+ tryFoldIntoCalloc (KillingLocWrapper.MemDef ,
2477
+ KillingLocWrapper.UnderlyingObject )) {
2478
+ LLVM_DEBUG (dbgs () << " DSE: Remove memset after forming calloc:\n "
2479
+ << " DEAD: " << *KillingLocWrapper.DefInst << ' \n ' );
2480
+ deleteDeadInstruction (KillingLocWrapper.DefInst );
2481
+ MadeChange = true ;
2482
+ continue ;
2483
+ }
2343
2484
}
2344
- return Changed ;
2485
+ return MadeChange ;
2345
2486
}
2346
2487
2347
2488
static bool eliminateDeadStores (Function &F, AliasAnalysis &AA, MemorySSA &MSSA,
@@ -2357,7 +2498,8 @@ static bool eliminateDeadStores(Function &F, AliasAnalysis &AA, MemorySSA &MSSA,
2357
2498
continue ;
2358
2499
2359
2500
MemoryDefWrapper KillingDefWrapper (
2360
- KillingDef, State.getLocForInst (KillingDef->getMemoryInst ()));
2501
+ KillingDef, State.getLocForInst (KillingDef->getMemoryInst (),
2502
+ EnableInitializesImprovement));
2361
2503
MadeChange |= State.eliminateDeadDefs (KillingDefWrapper);
2362
2504
}
2363
2505
0 commit comments