1 //===------ DeLICM.cpp -----------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // Undo the effect of Loop Invariant Code Motion (LICM) and 11 // GVN Partial Redundancy Elimination (PRE) on SCoP-level. 12 // 13 // Namely, remove register/scalar dependencies by mapping them back to array 14 // elements. 15 // 16 // The algorithms here work on the scatter space - the image space of the 17 // schedule returned by Scop::getSchedule(). We call an element in that space a 18 // "timepoint". Timepoints are lexicographically ordered such that we can 19 // defined ranges in the scatter space. We use two flavors of such ranges: 20 // Timepoint sets and zones. A timepoint set is simply a subset of the scatter 21 // space and is directly stored as isl_set. 22 // 23 // Zones are used to describe the space between timepoints as open sets, i.e. 24 // they do not contain the extrema. Using isl rational sets to express these 25 // would be overkill. We also cannot store them as the integer timepoints they 26 // contain; the (nonempty) zone between 1 and 2 would be empty and 27 // indistinguishable from e.g. the zone between 3 and 4. Also, we cannot store 28 // the integer set including the extrema; the set ]1,2[ + ]3,4[ could be 29 // coalesced to ]1,3[, although we defined the range [2,3] to be not in the set. 30 // Instead, we store the "half-open" integer extrema, including the lower bound, 31 // but excluding the upper bound. Examples: 32 // 33 // * The set { [i] : 1 <= i <= 3 } represents the zone ]0,3[ (which contains the 34 // integer points 1 and 2, but not 0 or 3) 35 // 36 // * { [1] } represents the zone ]0,1[ 37 // 38 // * { [i] : i = 1 or i = 3 } represents the zone ]0,1[ + ]2,3[ 39 // 40 // Therefore, an integer i in the set represents the zone ]i-1,i[, i.e. strictly 41 // speaking the integer points never belong to the zone. However, depending an 42 // the interpretation, one might want to include them. Part of the 43 // interpretation may not be known when the zone is constructed. 44 // 45 // Reads are assumed to always take place before writes, hence we can think of 46 // reads taking place at the beginning of a timepoint and writes at the end. 47 // 48 // Let's assume that the zone represents the lifetime of a variable. That is, 49 // the zone begins with a write that defines the value during its lifetime and 50 // ends with the last read of that value. In the following we consider whether a 51 // read/write at the beginning/ending of the lifetime zone should be within the 52 // zone or outside of it. 53 // 54 // * A read at the timepoint that starts the live-range loads the previous 55 // value. Hence, exclude the timepoint starting the zone. 56 // 57 // * A write at the timepoint that starts the live-range is not defined whether 58 // it occurs before or after the write that starts the lifetime. We do not 59 // allow this situation to occur. Hence, we include the timepoint starting the 60 // zone to determine whether they are conflicting. 61 // 62 // * A read at the timepoint that ends the live-range reads the same variable. 63 // We include the timepoint at the end of the zone to include that read into 64 // the live-range. Doing otherwise would mean that the two reads access 65 // different values, which would mean that the value they read are both alive 66 // at the same time but occupy the same variable. 67 // 68 // * A write at the timepoint that ends the live-range starts a new live-range. 69 // It must not be included in the live-range of the previous definition. 70 // 71 // All combinations of reads and writes at the endpoints are possible, but most 72 // of the time only the write->read (for instance, a live-range from definition 73 // to last use) and read->write (for instance, an unused range from last use to 74 // overwrite) and combinations are interesting (half-open ranges). write->write 75 // zones might be useful as well in some context to represent 76 // output-dependencies. 77 // 78 // @see convertZoneToTimepoints 79 // 80 // 81 // The code makes use of maps and sets in many different spaces. To not loose 82 // track in which space a set or map is expected to be in, variables holding an 83 // isl reference are usually annotated in the comments. They roughly follow isl 84 // syntax for spaces, but only the tuples, not the dimensions. The tuples have a 85 // meaning as follows: 86 // 87 // * Space[] - An unspecified tuple. Used for function parameters such that the 88 // function caller can use it for anything they like. 89 // 90 // * Domain[] - A statement instance as returned by ScopStmt::getDomain() 91 // isl_id_get_name: Stmt_<NameOfBasicBlock> 92 // isl_id_get_user: Pointer to ScopStmt 93 // 94 // * Element[] - An array element as in the range part of 95 // MemoryAccess::getAccessRelation() 96 // isl_id_get_name: MemRef_<NameOfArrayVariable> 97 // isl_id_get_user: Pointer to ScopArrayInfo 98 // 99 // * Scatter[] - Scatter space or space of timepoints 100 // Has no tuple id 101 // 102 // * Zone[] - Range between timepoints as described above 103 // Has no tuple id 104 // 105 // * ValInst[] - An llvm::Value as defined at a specific timepoint. 106 // 107 // A ValInst[] itself can be structured as one of: 108 // 109 // * [] - An unknown value. 110 // Always zero dimensions 111 // Has no tuple id 112 // 113 // * Value[] - An llvm::Value that is read-only in the SCoP, i.e. its 114 // runtime content does not depend on the timepoint. 115 // Always zero dimensions 116 // isl_id_get_name: Val_<NameOfValue> 117 // isl_id_get_user: A pointer to an llvm::Value 118 // 119 // * SCEV[...] - A synthesizable llvm::SCEV Expression. 120 // In contrast to a Value[] is has at least one dimension per 121 // SCEVAddRecExpr in the SCEV. 122 // 123 // * [Domain[] -> Value[]] - An llvm::Value that may change during the 124 // Scop's execution. 125 // The tuple itself has no id, but it wraps a map space holding a 126 // statement instance which defines the llvm::Value as the map's domain 127 // and llvm::Value itself as range. 128 // 129 // @see makeValInst() 130 // 131 // An annotation "{ Domain[] -> Scatter[] }" therefore means: A map from a 132 // statement instance to a timepoint, aka a schedule. There is only one scatter 133 // space, but most of the time multiple statements are processed in one set. 134 // This is why most of the time isl_union_map has to be used. 135 // 136 // The basic algorithm works as follows: 137 // At first we verify that the SCoP is compatible with this technique. For 138 // instance, two writes cannot write to the same location at the same statement 139 // instance because we cannot determine within the polyhedral model which one 140 // comes first. Once this was verified, we compute zones at which an array 141 // element is unused. This computation can fail if it takes too long. Then the 142 // main algorithm is executed. Because every store potentially trails an unused 143 // zone, we start at stores. We search for a scalar (MemoryKind::Value or 144 // MemoryKind::PHI) that we can map to the array element overwritten by the 145 // store, preferably one that is used by the store or at least the ScopStmt. 146 // When it does not conflict with the lifetime of the values in the array 147 // element, the map is applied and the unused zone updated as it is now used. We 148 // continue to try to map scalars to the array element until there are no more 149 // candidates to map. The algorithm is greedy in the sense that the first scalar 150 // not conflicting will be mapped. Other scalars processed later that could have 151 // fit the same unused zone will be rejected. As such the result depends on the 152 // processing order. 153 // 154 //===----------------------------------------------------------------------===// 155 156 #include "polly/DeLICM.h" 157 #include "polly/Options.h" 158 #include "polly/ScopInfo.h" 159 #include "polly/ScopPass.h" 160 #include "polly/Support/ISLOStream.h" 161 #include "polly/Support/ISLTools.h" 162 #include "polly/Support/VirtualInstruction.h" 163 #include "llvm/ADT/Statistic.h" 164 #define DEBUG_TYPE "polly-delicm" 165 166 using namespace polly; 167 using namespace llvm; 168 169 namespace { 170 171 cl::opt<int> 172 DelicmMaxOps("polly-delicm-max-ops", 173 cl::desc("Maximum number of isl operations to invest for " 174 "lifetime analysis; 0=no limit"), 175 cl::init(1000000), cl::cat(PollyCategory)); 176 177 cl::opt<bool> DelicmOverapproximateWrites( 178 "polly-delicm-overapproximate-writes", 179 cl::desc( 180 "Do more PHI writes than necessary in order to avoid partial accesses"), 181 cl::init(false), cl::Hidden, cl::cat(PollyCategory)); 182 183 cl::opt<bool> DelicmPartialWrites("polly-delicm-partial-writes", 184 cl::desc("Allow partial writes"), 185 cl::init(false), cl::Hidden, 186 cl::cat(PollyCategory)); 187 188 cl::opt<bool> 189 DelicmComputeKnown("polly-delicm-compute-known", 190 cl::desc("Compute known content of array elements"), 191 cl::init(true), cl::Hidden, cl::cat(PollyCategory)); 192 193 STATISTIC(DeLICMAnalyzed, "Number of successfully analyzed SCoPs"); 194 STATISTIC(DeLICMOutOfQuota, 195 "Analyses aborted because max_operations was reached"); 196 STATISTIC(DeLICMIncompatible, "Number of SCoPs incompatible for analysis"); 197 STATISTIC(MappedValueScalars, "Number of mapped Value scalars"); 198 STATISTIC(MappedPHIScalars, "Number of mapped PHI scalars"); 199 STATISTIC(TargetsMapped, "Number of stores used for at least one mapping"); 200 STATISTIC(DeLICMScopsModified, "Number of SCoPs optimized"); 201 202 /// Class for keeping track of scalar def-use chains in the polyhedral 203 /// representation. 204 /// 205 /// MemoryKind::Value: 206 /// There is one definition per llvm::Value or zero (read-only values defined 207 /// before the SCoP) and an arbitrary number of reads. 208 /// 209 /// MemoryKind::PHI, MemoryKind::ExitPHI: 210 /// There is at least one write (the incoming blocks/stmts) and one 211 /// (MemoryKind::PHI) or zero (MemoryKind::ExitPHI) reads per llvm::PHINode. 212 class ScalarDefUseChains { 213 private: 214 /// The definitions (i.e. write MemoryAccess) of a MemoryKind::Value scalar. 215 DenseMap<const ScopArrayInfo *, MemoryAccess *> ValueDefAccs; 216 217 /// List of all uses (i.e. read MemoryAccesses) for a MemoryKind::Value 218 /// scalar. 219 DenseMap<const ScopArrayInfo *, SmallVector<MemoryAccess *, 4>> ValueUseAccs; 220 221 /// The receiving part (i.e. read MemoryAccess) of a MemoryKind::PHI scalar. 222 DenseMap<const ScopArrayInfo *, MemoryAccess *> PHIReadAccs; 223 224 /// List of all incoming values (write MemoryAccess) of a MemoryKind::PHI or 225 /// MemoryKind::ExitPHI scalar. 226 DenseMap<const ScopArrayInfo *, SmallVector<MemoryAccess *, 4>> 227 PHIIncomingAccs; 228 229 public: 230 /// Find the MemoryAccesses that access the ScopArrayInfo-represented memory. 231 /// 232 /// @param S The SCoP to analyze. 233 void compute(Scop *S) { 234 // Purge any previous result. 235 reset(); 236 237 for (auto &Stmt : *S) { 238 for (auto *MA : Stmt) { 239 if (MA->isOriginalValueKind() && MA->isWrite()) { 240 auto *SAI = MA->getScopArrayInfo(); 241 assert(!ValueDefAccs.count(SAI) && 242 "There can be at most one " 243 "definition per MemoryKind::Value scalar"); 244 ValueDefAccs[SAI] = MA; 245 } 246 247 if (MA->isOriginalValueKind() && MA->isRead()) 248 ValueUseAccs[MA->getScopArrayInfo()].push_back(MA); 249 250 if (MA->isOriginalAnyPHIKind() && MA->isRead()) { 251 auto *SAI = MA->getScopArrayInfo(); 252 assert(!PHIReadAccs.count(SAI) && 253 "There must be exactly one read " 254 "per PHI (that's where the PHINode is)"); 255 PHIReadAccs[SAI] = MA; 256 } 257 258 if (MA->isOriginalAnyPHIKind() && MA->isWrite()) 259 PHIIncomingAccs[MA->getScopArrayInfo()].push_back(MA); 260 } 261 } 262 } 263 264 /// Free all memory used by the analysis. 265 void reset() { 266 ValueDefAccs.clear(); 267 ValueUseAccs.clear(); 268 PHIReadAccs.clear(); 269 PHIIncomingAccs.clear(); 270 } 271 272 MemoryAccess *getValueDef(const ScopArrayInfo *SAI) const { 273 return ValueDefAccs.lookup(SAI); 274 } 275 276 ArrayRef<MemoryAccess *> getValueUses(const ScopArrayInfo *SAI) const { 277 auto It = ValueUseAccs.find(SAI); 278 if (It == ValueUseAccs.end()) 279 return {}; 280 return It->second; 281 } 282 283 MemoryAccess *getPHIRead(const ScopArrayInfo *SAI) const { 284 return PHIReadAccs.lookup(SAI); 285 } 286 287 ArrayRef<MemoryAccess *> getPHIIncomings(const ScopArrayInfo *SAI) const { 288 auto It = PHIIncomingAccs.find(SAI); 289 if (It == PHIIncomingAccs.end()) 290 return {}; 291 return It->second; 292 } 293 }; 294 295 isl::union_map computeReachingDefinition(isl::union_map Schedule, 296 isl::union_map Writes, bool InclDef, 297 bool InclRedef) { 298 return computeReachingWrite(Schedule, Writes, false, InclDef, InclRedef); 299 } 300 301 isl::union_map computeReachingOverwrite(isl::union_map Schedule, 302 isl::union_map Writes, 303 bool InclPrevWrite, 304 bool InclOverwrite) { 305 return computeReachingWrite(Schedule, Writes, true, InclPrevWrite, 306 InclOverwrite); 307 } 308 309 /// Compute the next overwrite for a scalar. 310 /// 311 /// @param Schedule { DomainWrite[] -> Scatter[] } 312 /// Schedule of (at least) all writes. Instances not in @p 313 /// Writes are ignored. 314 /// @param Writes { DomainWrite[] } 315 /// The element instances that write to the scalar. 316 /// @param InclPrevWrite Whether to extend the timepoints to include 317 /// the timepoint where the previous write happens. 318 /// @param InclOverwrite Whether the reaching overwrite includes the timepoint 319 /// of the overwrite itself. 320 /// 321 /// @return { Scatter[] -> DomainDef[] } 322 isl::union_map computeScalarReachingOverwrite(isl::union_map Schedule, 323 isl::union_set Writes, 324 bool InclPrevWrite, 325 bool InclOverwrite) { 326 327 // { DomainWrite[] } 328 auto WritesMap = give(isl_union_map_from_domain(Writes.take())); 329 330 // { [Element[] -> Scatter[]] -> DomainWrite[] } 331 auto Result = computeReachingOverwrite( 332 std::move(Schedule), std::move(WritesMap), InclPrevWrite, InclOverwrite); 333 334 return give(isl_union_map_domain_factor_range(Result.take())); 335 } 336 337 /// Overload of computeScalarReachingOverwrite, with only one writing statement. 338 /// Consequently, the result consists of only one map space. 339 /// 340 /// @param Schedule { DomainWrite[] -> Scatter[] } 341 /// @param Writes { DomainWrite[] } 342 /// @param InclPrevWrite Include the previous write to result. 343 /// @param InclOverwrite Include the overwrite to the result. 344 /// 345 /// @return { Scatter[] -> DomainWrite[] } 346 isl::map computeScalarReachingOverwrite(isl::union_map Schedule, 347 isl::set Writes, bool InclPrevWrite, 348 bool InclOverwrite) { 349 auto ScatterSpace = getScatterSpace(Schedule); 350 auto DomSpace = give(isl_set_get_space(Writes.keep())); 351 352 auto ReachOverwrite = computeScalarReachingOverwrite( 353 Schedule, give(isl_union_set_from_set(Writes.take())), InclPrevWrite, 354 InclOverwrite); 355 356 auto ResultSpace = give(isl_space_map_from_domain_and_range( 357 ScatterSpace.take(), DomSpace.take())); 358 return singleton(std::move(ReachOverwrite), ResultSpace); 359 } 360 361 /// Compute the reaching definition of a scalar. 362 /// 363 /// Compared to computeReachingDefinition, there is just one element which is 364 /// accessed and therefore only a set if instances that accesses that element is 365 /// required. 366 /// 367 /// @param Schedule { DomainWrite[] -> Scatter[] } 368 /// @param Writes { DomainWrite[] } 369 /// @param InclDef Include the timepoint of the definition to the result. 370 /// @param InclRedef Include the timepoint of the overwrite into the result. 371 /// 372 /// @return { Scatter[] -> DomainWrite[] } 373 isl::union_map computeScalarReachingDefinition(isl::union_map Schedule, 374 isl::union_set Writes, 375 bool InclDef, bool InclRedef) { 376 377 // { DomainWrite[] -> Element[] } 378 auto Defs = give(isl_union_map_from_domain(Writes.take())); 379 380 // { [Element[] -> Scatter[]] -> DomainWrite[] } 381 auto ReachDefs = 382 computeReachingDefinition(Schedule, Defs, InclDef, InclRedef); 383 384 // { Scatter[] -> DomainWrite[] } 385 return give(isl_union_set_unwrap( 386 isl_union_map_range(isl_union_map_curry(ReachDefs.take())))); 387 } 388 389 /// Compute the reaching definition of a scalar. 390 /// 391 /// This overload accepts only a single writing statement as an isl_map, 392 /// consequently the result also is only a single isl_map. 393 /// 394 /// @param Schedule { DomainWrite[] -> Scatter[] } 395 /// @param Writes { DomainWrite[] } 396 /// @param InclDef Include the timepoint of the definition to the result. 397 /// @param InclRedef Include the timepoint of the overwrite into the result. 398 /// 399 /// @return { Scatter[] -> DomainWrite[] } 400 isl::map computeScalarReachingDefinition( // { Domain[] -> Zone[] } 401 isl::union_map Schedule, isl::set Writes, bool InclDef, bool InclRedef) { 402 auto DomainSpace = give(isl_set_get_space(Writes.keep())); 403 auto ScatterSpace = getScatterSpace(Schedule); 404 405 // { Scatter[] -> DomainWrite[] } 406 auto UMap = computeScalarReachingDefinition( 407 Schedule, give(isl_union_set_from_set(Writes.take())), InclDef, 408 InclRedef); 409 410 auto ResultSpace = give(isl_space_map_from_domain_and_range( 411 ScatterSpace.take(), DomainSpace.take())); 412 return singleton(UMap, ResultSpace); 413 } 414 415 /// Create a domain-to-unknown value mapping. 416 /// 417 /// Value instances that do not represent a specific value are represented by an 418 /// unnamed tuple of 0 dimensions. Its meaning depends on the context. It can 419 /// either mean a specific but unknown value which cannot be represented by 420 /// other means. It conflicts with itself because those two unknown ValInsts may 421 /// have different concrete values at runtime. 422 /// 423 /// The other meaning is an arbitrary or wildcard value that can be chosen 424 /// freely, like LLVM's undef. If matched with an unknown ValInst, there is no 425 /// conflict. 426 /// 427 /// @param Domain { Domain[] } 428 /// 429 /// @return { Domain[] -> ValInst[] } 430 isl::union_map makeUnknownForDomain(isl::union_set Domain) { 431 return give(isl_union_map_from_domain(Domain.take())); 432 } 433 434 /// Create a domain-to-unknown value mapping. 435 /// 436 /// @see makeUnknownForDomain(isl::union_set) 437 /// 438 /// @param Domain { Domain[] } 439 /// 440 /// @return { Domain[] -> ValInst[] } 441 isl::map makeUnknownForDomain(isl::set Domain) { 442 return give(isl_map_from_domain(Domain.take())); 443 } 444 445 /// Return whether @p Map maps to an unknown value. 446 /// 447 /// @param { [] -> ValInst[] } 448 bool isMapToUnknown(const isl::map &Map) { 449 auto Space = give(isl_space_range(isl_map_get_space(Map.keep()))); 450 return !isl_map_has_tuple_id(Map.keep(), isl_dim_set) && 451 !isl_space_is_wrapping(Space.keep()) && 452 isl_map_dim(Map.keep(), isl_dim_out) == 0; 453 } 454 455 /// Return only the mappings that map to known values. 456 /// 457 /// @param UMap { [] -> ValInst[] } 458 /// 459 /// @return { [] -> ValInst[] } 460 isl::union_map filterKnownValInst(const isl::union_map &UMap) { 461 auto Result = give(isl_union_map_empty(isl_union_map_get_space(UMap.keep()))); 462 UMap.foreach_map([=, &Result](isl::map Map) -> isl::stat { 463 if (!isMapToUnknown(Map)) 464 Result = give(isl_union_map_add_map(Result.take(), Map.take())); 465 return isl::stat::ok; 466 }); 467 return Result; 468 } 469 470 /// Try to find a 'natural' extension of a mapped to elements outside its 471 /// domain. 472 /// 473 /// @param Relevant The map with mapping that may not be modified. 474 /// @param Universe The domain to which @p Relevant needs to be extended. 475 /// 476 /// @return A map with that associates the domain elements of @p Relevant to the 477 /// same elements and in addition the elements of @p Universe to some 478 /// undefined elements. The function prefers to return simple maps. 479 isl::union_map expandMapping(isl::union_map Relevant, isl::union_set Universe) { 480 Relevant = give(isl_union_map_coalesce(Relevant.take())); 481 auto RelevantDomain = give(isl_union_map_domain(Relevant.copy())); 482 auto Simplified = 483 give(isl_union_map_gist_domain(Relevant.take(), RelevantDomain.take())); 484 Simplified = give(isl_union_map_coalesce(Simplified.take())); 485 return give( 486 isl_union_map_intersect_domain(Simplified.take(), Universe.take())); 487 } 488 489 /// Represent the knowledge of the contents of any array elements in any zone or 490 /// the knowledge we would add when mapping a scalar to an array element. 491 /// 492 /// Every array element at every zone unit has one of two states: 493 /// 494 /// - Unused: Not occupied by any value so a transformation can change it to 495 /// other values. 496 /// 497 /// - Occupied: The element contains a value that is still needed. 498 /// 499 /// The union of Unused and Unknown zones forms the universe, the set of all 500 /// elements at every timepoint. The universe can easily be derived from the 501 /// array elements that are accessed someway. Arrays that are never accessed 502 /// also never play a role in any computation and can hence be ignored. With a 503 /// given universe, only one of the sets needs to stored implicitly. Computing 504 /// the complement is also an expensive operation, hence this class has been 505 /// designed that only one of sets is needed while the other is assumed to be 506 /// implicit. It can still be given, but is mostly ignored. 507 /// 508 /// There are two use cases for the Knowledge class: 509 /// 510 /// 1) To represent the knowledge of the current state of ScopInfo. The unused 511 /// state means that an element is currently unused: there is no read of it 512 /// before the next overwrite. Also called 'Existing'. 513 /// 514 /// 2) To represent the requirements for mapping a scalar to array elements. The 515 /// unused state means that there is no change/requirement. Also called 516 /// 'Proposed'. 517 /// 518 /// In addition to these states at unit zones, Knowledge needs to know when 519 /// values are written. This is because written values may have no lifetime (one 520 /// reason is that the value is never read). Such writes would therefore never 521 /// conflict, but overwrite values that might still be required. Another source 522 /// of problems are multiple writes to the same element at the same timepoint, 523 /// because their order is undefined. 524 class Knowledge { 525 private: 526 /// { [Element[] -> Zone[]] } 527 /// Set of array elements and when they are alive. 528 /// Can contain a nullptr; in this case the set is implicitly defined as the 529 /// complement of #Unused. 530 /// 531 /// The set of alive array elements is represented as zone, as the set of live 532 /// values can differ depending on how the elements are interpreted. 533 /// Assuming a value X is written at timestep [0] and read at timestep [1] 534 /// without being used at any later point, then the value is alive in the 535 /// interval ]0,1[. This interval cannot be represented by an integer set, as 536 /// it does not contain any integer point. Zones allow us to represent this 537 /// interval and can be converted to sets of timepoints when needed (e.g., in 538 /// isConflicting when comparing to the write sets). 539 /// @see convertZoneToTimepoints and this file's comment for more details. 540 isl::union_set Occupied; 541 542 /// { [Element[] -> Zone[]] } 543 /// Set of array elements when they are not alive, i.e. their memory can be 544 /// used for other purposed. Can contain a nullptr; in this case the set is 545 /// implicitly defined as the complement of #Occupied. 546 isl::union_set Unused; 547 548 /// { [Element[] -> Zone[]] -> ValInst[] } 549 /// Maps to the known content for each array element at any interval. 550 /// 551 /// Any element/interval can map to multiple known elements. This is due to 552 /// multiple llvm::Value referring to the same content. Examples are 553 /// 554 /// - A value stored and loaded again. The LoadInst represents the same value 555 /// as the StoreInst's value operand. 556 /// 557 /// - A PHINode is equal to any one of the incoming values. In case of 558 /// LCSSA-form, it is always equal to its single incoming value. 559 /// 560 /// Two Knowledges are considered not conflicting if at least one of the known 561 /// values match. Not known values are not stored as an unnamed tuple (as 562 /// #Written does), but maps to nothing. 563 /// 564 /// Known values are usually just defined for #Occupied elements. Knowing 565 /// #Unused contents has no advantage as it can be overwritten. 566 isl::union_map Known; 567 568 /// { [Element[] -> Scatter[]] -> ValInst[] } 569 /// The write actions currently in the scop or that would be added when 570 /// mapping a scalar. Maps to the value that is written. 571 /// 572 /// Written values that cannot be identified are represented by an unknown 573 /// ValInst[] (an unnamed tuple of 0 dimension). It conflicts with itself. 574 isl::union_map Written; 575 576 /// Check whether this Knowledge object is well-formed. 577 void checkConsistency() const { 578 #ifndef NDEBUG 579 // Default-initialized object 580 if (!Occupied && !Unused && !Known && !Written) 581 return; 582 583 assert(Occupied || Unused); 584 assert(Known); 585 assert(Written); 586 587 // If not all fields are defined, we cannot derived the universe. 588 if (!Occupied || !Unused) 589 return; 590 591 assert(isl_union_set_is_disjoint(Occupied.keep(), Unused.keep()) == 592 isl_bool_true); 593 auto Universe = give(isl_union_set_union(Occupied.copy(), Unused.copy())); 594 595 assert(!Known.domain().is_subset(Universe).is_false()); 596 assert(!Written.domain().is_subset(Universe).is_false()); 597 #endif 598 } 599 600 public: 601 /// Initialize a nullptr-Knowledge. This is only provided for convenience; do 602 /// not use such an object. 603 Knowledge() {} 604 605 /// Create a new object with the given members. 606 Knowledge(isl::union_set Occupied, isl::union_set Unused, 607 isl::union_map Known, isl::union_map Written) 608 : Occupied(std::move(Occupied)), Unused(std::move(Unused)), 609 Known(std::move(Known)), Written(std::move(Written)) { 610 checkConsistency(); 611 } 612 613 /// Return whether this object was not default-constructed. 614 bool isUsable() const { return (Occupied || Unused) && Known && Written; } 615 616 /// Print the content of this object to @p OS. 617 void print(llvm::raw_ostream &OS, unsigned Indent = 0) const { 618 if (isUsable()) { 619 if (Occupied) 620 OS.indent(Indent) << "Occupied: " << Occupied << "\n"; 621 else 622 OS.indent(Indent) << "Occupied: <Everything else not in Unused>\n"; 623 if (Unused) 624 OS.indent(Indent) << "Unused: " << Unused << "\n"; 625 else 626 OS.indent(Indent) << "Unused: <Everything else not in Occupied>\n"; 627 OS.indent(Indent) << "Known: " << Known << "\n"; 628 OS.indent(Indent) << "Written : " << Written << '\n'; 629 } else { 630 OS.indent(Indent) << "Invalid knowledge\n"; 631 } 632 } 633 634 /// Combine two knowledges, this and @p That. 635 void learnFrom(Knowledge That) { 636 assert(!isConflicting(*this, That)); 637 assert(Unused && That.Occupied); 638 assert( 639 !That.Unused && 640 "This function is only prepared to learn occupied elements from That"); 641 assert(!Occupied && "This function does not implement " 642 "`this->Occupied = " 643 "give(isl_union_set_union(this->Occupied.take(), " 644 "That.Occupied.copy()));`"); 645 646 Unused = give(isl_union_set_subtract(Unused.take(), That.Occupied.copy())); 647 Known = give(isl_union_map_union(Known.take(), That.Known.copy())); 648 Written = give(isl_union_map_union(Written.take(), That.Written.take())); 649 650 checkConsistency(); 651 } 652 653 /// Determine whether two Knowledges conflict with each other. 654 /// 655 /// In theory @p Existing and @p Proposed are symmetric, but the 656 /// implementation is constrained by the implicit interpretation. That is, @p 657 /// Existing must have #Unused defined (use case 1) and @p Proposed must have 658 /// #Occupied defined (use case 1). 659 /// 660 /// A conflict is defined as non-preserved semantics when they are merged. For 661 /// instance, when for the same array and zone they assume different 662 /// llvm::Values. 663 /// 664 /// @param Existing One of the knowledges with #Unused defined. 665 /// @param Proposed One of the knowledges with #Occupied defined. 666 /// @param OS Dump the conflict reason to this output stream; use 667 /// nullptr to not output anything. 668 /// @param Indent Indention for the conflict reason. 669 /// 670 /// @return True, iff the two knowledges are conflicting. 671 static bool isConflicting(const Knowledge &Existing, 672 const Knowledge &Proposed, 673 llvm::raw_ostream *OS = nullptr, 674 unsigned Indent = 0) { 675 assert(Existing.Unused); 676 assert(Proposed.Occupied); 677 678 #ifndef NDEBUG 679 if (Existing.Occupied && Proposed.Unused) { 680 auto ExistingUniverse = give(isl_union_set_union(Existing.Occupied.copy(), 681 Existing.Unused.copy())); 682 auto ProposedUniverse = give(isl_union_set_union(Proposed.Occupied.copy(), 683 Proposed.Unused.copy())); 684 assert(isl_union_set_is_equal(ExistingUniverse.keep(), 685 ProposedUniverse.keep()) == isl_bool_true && 686 "Both inputs' Knowledges must be over the same universe"); 687 } 688 #endif 689 690 // Do the Existing and Proposed lifetimes conflict? 691 // 692 // Lifetimes are described as the cross-product of array elements and zone 693 // intervals in which they are alive (the space { [Element[] -> Zone[]] }). 694 // In the following we call this "element/lifetime interval". 695 // 696 // In order to not conflict, one of the following conditions must apply for 697 // each element/lifetime interval: 698 // 699 // 1. If occupied in one of the knowledges, it is unused in the other. 700 // 701 // - or - 702 // 703 // 2. Both contain the same value. 704 // 705 // Instead of partitioning the element/lifetime intervals into a part that 706 // both Knowledges occupy (which requires an expensive subtraction) and for 707 // these to check whether they are known to be the same value, we check only 708 // the second condition and ensure that it also applies when then first 709 // condition is true. This is done by adding a wildcard value to 710 // Proposed.Known and Existing.Unused such that they match as a common known 711 // value. We use the "unknown ValInst" for this purpose. Every 712 // Existing.Unused may match with an unknown Proposed.Occupied because these 713 // never are in conflict with each other. 714 auto ProposedOccupiedAnyVal = makeUnknownForDomain(Proposed.Occupied); 715 auto ProposedValues = Proposed.Known.unite(ProposedOccupiedAnyVal); 716 717 auto ExistingUnusedAnyVal = makeUnknownForDomain(Existing.Unused); 718 auto ExistingValues = Existing.Known.unite(ExistingUnusedAnyVal); 719 720 auto MatchingVals = ExistingValues.intersect(ProposedValues); 721 auto Matches = MatchingVals.domain(); 722 723 // Any Proposed.Occupied must either have a match between the known values 724 // of Existing and Occupied, or be in Existing.Unused. In the latter case, 725 // the previously added "AnyVal" will match each other. 726 if (!Proposed.Occupied.is_subset(Matches)) { 727 if (OS) { 728 auto Conflicting = Proposed.Occupied.subtract(Matches); 729 auto ExistingConflictingKnown = 730 Existing.Known.intersect_domain(Conflicting); 731 auto ProposedConflictingKnown = 732 Proposed.Known.intersect_domain(Conflicting); 733 734 OS->indent(Indent) << "Proposed lifetime conflicting with Existing's\n"; 735 OS->indent(Indent) << "Conflicting occupied: " << Conflicting << "\n"; 736 if (!ExistingConflictingKnown.is_empty()) 737 OS->indent(Indent) 738 << "Existing Known: " << ExistingConflictingKnown << "\n"; 739 if (!ProposedConflictingKnown.is_empty()) 740 OS->indent(Indent) 741 << "Proposed Known: " << ProposedConflictingKnown << "\n"; 742 } 743 return true; 744 } 745 746 // Do the writes in Existing conflict with occupied values in Proposed? 747 // 748 // In order to not conflict, it must either write to unused lifetime or 749 // write the same value. To check, we remove the writes that write into 750 // Proposed.Unused (they never conflict) and then see whether the written 751 // value is already in Proposed.Known. If there are multiple known values 752 // and a written value is known under different names, it is enough when one 753 // of the written values (assuming that they are the same value under 754 // different names, e.g. a PHINode and one of the incoming values) matches 755 // one of the known names. 756 // 757 // We convert here the set of lifetimes to actual timepoints. A lifetime is 758 // in conflict with a set of write timepoints, if either a live timepoint is 759 // clearly within the lifetime or if a write happens at the beginning of the 760 // lifetime (where it would conflict with the value that actually writes the 761 // value alive). There is no conflict at the end of a lifetime, as the alive 762 // value will always be read, before it is overwritten again. The last 763 // property holds in Polly for all scalar values and we expect all users of 764 // Knowledge to check this property also for accesses to MemoryKind::Array. 765 auto ProposedFixedDefs = 766 convertZoneToTimepoints(Proposed.Occupied, true, false); 767 auto ProposedFixedKnown = 768 convertZoneToTimepoints(Proposed.Known, isl::dim::in, true, false); 769 770 auto ExistingConflictingWrites = 771 Existing.Written.intersect_domain(ProposedFixedDefs); 772 auto ExistingConflictingWritesDomain = ExistingConflictingWrites.domain(); 773 774 auto CommonWrittenVal = 775 ProposedFixedKnown.intersect(ExistingConflictingWrites); 776 auto CommonWrittenValDomain = CommonWrittenVal.domain(); 777 778 if (!ExistingConflictingWritesDomain.is_subset(CommonWrittenValDomain)) { 779 if (OS) { 780 auto ExistingConflictingWritten = 781 ExistingConflictingWrites.subtract_domain(CommonWrittenValDomain); 782 auto ProposedConflictingKnown = ProposedFixedKnown.subtract_domain( 783 ExistingConflictingWritten.domain()); 784 785 OS->indent(Indent) 786 << "Proposed a lifetime where there is an Existing write into it\n"; 787 OS->indent(Indent) << "Existing conflicting writes: " 788 << ExistingConflictingWritten << "\n"; 789 if (!ProposedConflictingKnown.is_empty()) 790 OS->indent(Indent) 791 << "Proposed conflicting known: " << ProposedConflictingKnown 792 << "\n"; 793 } 794 return true; 795 } 796 797 // Do the writes in Proposed conflict with occupied values in Existing? 798 auto ExistingAvailableDefs = 799 convertZoneToTimepoints(Existing.Unused, true, false); 800 auto ExistingKnownDefs = 801 convertZoneToTimepoints(Existing.Known, isl::dim::in, true, false); 802 803 auto ProposedWrittenDomain = Proposed.Written.domain(); 804 auto KnownIdentical = ExistingKnownDefs.intersect(Proposed.Written); 805 auto IdenticalOrUnused = 806 ExistingAvailableDefs.unite(KnownIdentical.domain()); 807 if (!ProposedWrittenDomain.is_subset(IdenticalOrUnused)) { 808 if (OS) { 809 auto Conflicting = ProposedWrittenDomain.subtract(IdenticalOrUnused); 810 auto ExistingConflictingKnown = 811 ExistingKnownDefs.intersect_domain(Conflicting); 812 auto ProposedConflictingWritten = 813 Proposed.Written.intersect_domain(Conflicting); 814 815 OS->indent(Indent) << "Proposed writes into range used by Existing\n"; 816 OS->indent(Indent) << "Proposed conflicting writes: " 817 << ProposedConflictingWritten << "\n"; 818 if (!ExistingConflictingKnown.is_empty()) 819 OS->indent(Indent) 820 << "Existing conflicting known: " << ExistingConflictingKnown 821 << "\n"; 822 } 823 return true; 824 } 825 826 // Does Proposed write at the same time as Existing already does (order of 827 // writes is undefined)? Writing the same value is permitted. 828 auto ExistingWrittenDomain = 829 isl::manage(isl_union_map_domain(Existing.Written.copy())); 830 auto BothWritten = 831 Existing.Written.domain().intersect(Proposed.Written.domain()); 832 auto ExistingKnownWritten = filterKnownValInst(Existing.Written); 833 auto ProposedKnownWritten = filterKnownValInst(Proposed.Written); 834 auto CommonWritten = 835 ExistingKnownWritten.intersect(ProposedKnownWritten).domain(); 836 837 if (!BothWritten.is_subset(CommonWritten)) { 838 if (OS) { 839 auto Conflicting = BothWritten.subtract(CommonWritten); 840 auto ExistingConflictingWritten = 841 Existing.Written.intersect_domain(Conflicting); 842 auto ProposedConflictingWritten = 843 Proposed.Written.intersect_domain(Conflicting); 844 845 OS->indent(Indent) << "Proposed writes at the same time as an already " 846 "Existing write\n"; 847 OS->indent(Indent) << "Conflicting writes: " << Conflicting << "\n"; 848 if (!ExistingConflictingWritten.is_empty()) 849 OS->indent(Indent) 850 << "Exiting write: " << ExistingConflictingWritten << "\n"; 851 if (!ProposedConflictingWritten.is_empty()) 852 OS->indent(Indent) 853 << "Proposed write: " << ProposedConflictingWritten << "\n"; 854 } 855 return true; 856 } 857 858 return false; 859 } 860 }; 861 862 std::string printIntruction(Instruction *Instr, bool IsForDebug = false) { 863 std::string Result; 864 raw_string_ostream OS(Result); 865 Instr->print(OS, IsForDebug); 866 OS.flush(); 867 size_t i = 0; 868 while (i < Result.size() && Result[i] == ' ') 869 i += 1; 870 return Result.substr(i); 871 } 872 873 /// Base class for algorithms based on zones, like DeLICM. 874 class ZoneAlgorithm { 875 protected: 876 /// Hold a reference to the isl_ctx to avoid it being freed before we released 877 /// all of the isl objects. 878 /// 879 /// This must be declared before any other member that holds an isl object. 880 /// This guarantees that the shared_ptr and its isl_ctx is destructed last, 881 /// after all other members free'd the isl objects they were holding. 882 std::shared_ptr<isl_ctx> IslCtx; 883 884 /// Cached reaching definitions for each ScopStmt. 885 /// 886 /// Use getScalarReachingDefinition() to get its contents. 887 DenseMap<ScopStmt *, isl::map> ScalarReachDefZone; 888 889 /// The analyzed Scop. 890 Scop *S; 891 892 /// LoopInfo analysis used to determine whether values are synthesizable. 893 LoopInfo *LI; 894 895 /// Parameter space that does not need realignment. 896 isl::space ParamSpace; 897 898 /// Space the schedule maps to. 899 isl::space ScatterSpace; 900 901 /// Cached version of the schedule and domains. 902 isl::union_map Schedule; 903 904 /// Combined access relations of all MemoryKind::Array READ accesses. 905 /// { DomainRead[] -> Element[] } 906 isl::union_map AllReads; 907 908 /// Combined access relations of all MemoryKind::Array, MAY_WRITE accesses. 909 /// { DomainMayWrite[] -> Element[] } 910 isl::union_map AllMayWrites; 911 912 /// Combined access relations of all MemoryKind::Array, MUST_WRITE accesses. 913 /// { DomainMustWrite[] -> Element[] } 914 isl::union_map AllMustWrites; 915 916 /// The value instances written to array elements of all write accesses. 917 /// { [Element[] -> DomainWrite[]] -> ValInst[] } 918 isl::union_map AllWriteValInst; 919 920 /// All reaching definitions for MemoryKind::Array writes. 921 /// { [Element[] -> Zone[]] -> DomainWrite[] } 922 isl::union_map WriteReachDefZone; 923 924 /// Map llvm::Values to an isl identifier. 925 /// Used with -polly-use-llvm-names=false as an alternative method to get 926 /// unique ids that do not depend on pointer values. 927 DenseMap<Value *, isl::id> ValueIds; 928 929 /// Prepare the object before computing the zones of @p S. 930 ZoneAlgorithm(Scop *S, LoopInfo *LI) 931 : IslCtx(S->getSharedIslCtx()), S(S), LI(LI), 932 Schedule(give(S->getSchedule())) { 933 934 auto Domains = give(S->getDomains()); 935 936 Schedule = 937 give(isl_union_map_intersect_domain(Schedule.take(), Domains.take())); 938 ParamSpace = give(isl_union_map_get_space(Schedule.keep())); 939 ScatterSpace = getScatterSpace(Schedule); 940 } 941 942 private: 943 /// Check whether @p Stmt can be accurately analyzed by zones. 944 /// 945 /// What violates our assumptions: 946 /// - A load after a write of the same location; we assume that all reads 947 /// occur before the writes. 948 /// - Two writes to the same location; we cannot model the order in which 949 /// these occur. 950 /// 951 /// Scalar reads implicitly always occur before other accesses therefore never 952 /// violate the first condition. There is also at most one write to a scalar, 953 /// satisfying the second condition. 954 bool isCompatibleStmt(ScopStmt *Stmt) { 955 auto Stores = makeEmptyUnionMap(); 956 auto Loads = makeEmptyUnionMap(); 957 958 // This assumes that the MemoryKind::Array MemoryAccesses are iterated in 959 // order. 960 for (auto *MA : *Stmt) { 961 if (!MA->isLatestArrayKind()) 962 continue; 963 964 auto AccRel = 965 give(isl_union_map_from_map(getAccessRelationFor(MA).take())); 966 967 if (MA->isRead()) { 968 // Reject load after store to same location. 969 if (!isl_union_map_is_disjoint(Stores.keep(), AccRel.keep())) { 970 OptimizationRemarkMissed R(DEBUG_TYPE, "LoadAfterStore", 971 MA->getAccessInstruction()); 972 R << "load after store of same element in same statement"; 973 R << " (previous stores: " << Stores; 974 R << ", loading: " << AccRel << ")"; 975 S->getFunction().getContext().diagnose(R); 976 return false; 977 } 978 979 Loads = give(isl_union_map_union(Loads.take(), AccRel.take())); 980 981 continue; 982 } 983 984 if (!isa<StoreInst>(MA->getAccessInstruction())) { 985 DEBUG(dbgs() << "WRITE that is not a StoreInst not supported\n"); 986 OptimizationRemarkMissed R(DEBUG_TYPE, "UnusualStore", 987 MA->getAccessInstruction()); 988 R << "encountered write that is not a StoreInst: " 989 << printIntruction(MA->getAccessInstruction()); 990 S->getFunction().getContext().diagnose(R); 991 return false; 992 } 993 994 // In region statements the order is less clear, eg. the load and store 995 // might be in a boxed loop. 996 if (Stmt->isRegionStmt() && 997 !isl_union_map_is_disjoint(Loads.keep(), AccRel.keep())) { 998 OptimizationRemarkMissed R(DEBUG_TYPE, "StoreInSubregion", 999 MA->getAccessInstruction()); 1000 R << "store is in a non-affine subregion"; 1001 S->getFunction().getContext().diagnose(R); 1002 return false; 1003 } 1004 1005 // Do not allow more than one store to the same location. 1006 if (!isl_union_map_is_disjoint(Stores.keep(), AccRel.keep())) { 1007 OptimizationRemarkMissed R(DEBUG_TYPE, "StoreAfterStore", 1008 MA->getAccessInstruction()); 1009 R << "store after store of same element in same statement"; 1010 R << " (previous stores: " << Stores; 1011 R << ", storing: " << AccRel << ")"; 1012 S->getFunction().getContext().diagnose(R); 1013 return false; 1014 } 1015 1016 Stores = give(isl_union_map_union(Stores.take(), AccRel.take())); 1017 } 1018 1019 return true; 1020 } 1021 1022 void addArrayReadAccess(MemoryAccess *MA) { 1023 assert(MA->isLatestArrayKind()); 1024 assert(MA->isRead()); 1025 1026 // { DomainRead[] -> Element[] } 1027 auto AccRel = getAccessRelationFor(MA); 1028 AllReads = give(isl_union_map_add_map(AllReads.take(), AccRel.copy())); 1029 } 1030 1031 void addArrayWriteAccess(MemoryAccess *MA) { 1032 assert(MA->isLatestArrayKind()); 1033 assert(MA->isWrite()); 1034 auto *Stmt = MA->getStatement(); 1035 1036 // { Domain[] -> Element[] } 1037 auto AccRel = getAccessRelationFor(MA); 1038 1039 if (MA->isMustWrite()) 1040 AllMustWrites = 1041 give(isl_union_map_add_map(AllMustWrites.take(), AccRel.copy())); 1042 1043 if (MA->isMayWrite()) 1044 AllMayWrites = 1045 give(isl_union_map_add_map(AllMayWrites.take(), AccRel.copy())); 1046 1047 // { Domain[] -> ValInst[] } 1048 auto WriteValInstance = 1049 makeValInst(MA->getAccessValue(), Stmt, 1050 LI->getLoopFor(MA->getAccessInstruction()->getParent()), 1051 MA->isMustWrite()); 1052 1053 // { Domain[] -> [Element[] -> Domain[]] } 1054 auto IncludeElement = 1055 give(isl_map_curry(isl_map_domain_map(AccRel.copy()))); 1056 1057 // { [Element[] -> DomainWrite[]] -> ValInst[] } 1058 auto EltWriteValInst = give( 1059 isl_map_apply_domain(WriteValInstance.take(), IncludeElement.take())); 1060 1061 AllWriteValInst = give( 1062 isl_union_map_add_map(AllWriteValInst.take(), EltWriteValInst.take())); 1063 } 1064 1065 protected: 1066 isl::union_set makeEmptyUnionSet() const { 1067 return give(isl_union_set_empty(ParamSpace.copy())); 1068 } 1069 1070 isl::union_map makeEmptyUnionMap() const { 1071 return give(isl_union_map_empty(ParamSpace.copy())); 1072 } 1073 1074 /// Check whether @p S can be accurately analyzed by zones. 1075 bool isCompatibleScop() { 1076 for (auto &Stmt : *S) { 1077 if (!isCompatibleStmt(&Stmt)) 1078 return false; 1079 } 1080 return true; 1081 } 1082 1083 /// Get the schedule for @p Stmt. 1084 /// 1085 /// The domain of the result is as narrow as possible. 1086 isl::map getScatterFor(ScopStmt *Stmt) const { 1087 auto ResultSpace = give(isl_space_map_from_domain_and_range( 1088 Stmt->getDomainSpace(), ScatterSpace.copy())); 1089 return give(isl_union_map_extract_map(Schedule.keep(), ResultSpace.take())); 1090 } 1091 1092 /// Get the schedule of @p MA's parent statement. 1093 isl::map getScatterFor(MemoryAccess *MA) const { 1094 return getScatterFor(MA->getStatement()); 1095 } 1096 1097 /// Get the schedule for the statement instances of @p Domain. 1098 isl::union_map getScatterFor(isl::union_set Domain) const { 1099 return give(isl_union_map_intersect_domain(Schedule.copy(), Domain.take())); 1100 } 1101 1102 /// Get the schedule for the statement instances of @p Domain. 1103 isl::map getScatterFor(isl::set Domain) const { 1104 auto ResultSpace = give(isl_space_map_from_domain_and_range( 1105 isl_set_get_space(Domain.keep()), ScatterSpace.copy())); 1106 auto UDomain = give(isl_union_set_from_set(Domain.copy())); 1107 auto UResult = getScatterFor(std::move(UDomain)); 1108 auto Result = singleton(std::move(UResult), std::move(ResultSpace)); 1109 assert(!Result || 1110 isl_set_is_equal(give(isl_map_domain(Result.copy())).keep(), 1111 Domain.keep()) == isl_bool_true); 1112 return Result; 1113 } 1114 1115 /// Get the domain of @p Stmt. 1116 isl::set getDomainFor(ScopStmt *Stmt) const { 1117 return give(isl_set_remove_redundancies(Stmt->getDomain())); 1118 } 1119 1120 /// Get the domain @p MA's parent statement. 1121 isl::set getDomainFor(MemoryAccess *MA) const { 1122 return getDomainFor(MA->getStatement()); 1123 } 1124 1125 /// Get the access relation of @p MA. 1126 /// 1127 /// The domain of the result is as narrow as possible. 1128 isl::map getAccessRelationFor(MemoryAccess *MA) const { 1129 auto Domain = getDomainFor(MA); 1130 auto AccRel = give(MA->getLatestAccessRelation()); 1131 return give(isl_map_intersect_domain(AccRel.take(), Domain.take())); 1132 } 1133 1134 /// Get the reaching definition of a scalar defined in @p Stmt. 1135 /// 1136 /// Note that this does not depend on the llvm::Instruction, only on the 1137 /// statement it is defined in. Therefore the same computation can be reused. 1138 /// 1139 /// @param Stmt The statement in which a scalar is defined. 1140 /// 1141 /// @return { Scatter[] -> DomainDef[] } 1142 isl::map getScalarReachingDefinition(ScopStmt *Stmt) { 1143 auto &Result = ScalarReachDefZone[Stmt]; 1144 if (Result) 1145 return Result; 1146 1147 auto Domain = getDomainFor(Stmt); 1148 Result = computeScalarReachingDefinition(Schedule, Domain, false, true); 1149 simplify(Result); 1150 1151 return Result; 1152 } 1153 1154 /// Get the reaching definition of a scalar defined in @p DefDomain. 1155 /// 1156 /// @param DomainDef { DomainDef[] } 1157 /// The write statements to get the reaching definition for. 1158 /// 1159 /// @return { Scatter[] -> DomainDef[] } 1160 isl::map getScalarReachingDefinition(isl::set DomainDef) { 1161 auto DomId = give(isl_set_get_tuple_id(DomainDef.keep())); 1162 auto *Stmt = static_cast<ScopStmt *>(isl_id_get_user(DomId.keep())); 1163 1164 auto StmtResult = getScalarReachingDefinition(Stmt); 1165 1166 return give(isl_map_intersect_range(StmtResult.take(), DomainDef.take())); 1167 } 1168 1169 /// Create a statement-to-unknown value mapping. 1170 /// 1171 /// @param Stmt The statement whose instances are mapped to unknown. 1172 /// 1173 /// @return { Domain[] -> ValInst[] } 1174 isl::map makeUnknownForDomain(ScopStmt *Stmt) const { 1175 return ::makeUnknownForDomain(getDomainFor(Stmt)); 1176 } 1177 1178 /// Create an isl_id that represents @p V. 1179 isl::id makeValueId(Value *V) { 1180 if (!V) 1181 return nullptr; 1182 1183 auto &Id = ValueIds[V]; 1184 if (Id.is_null()) { 1185 auto Name = getIslCompatibleName("Val_", V, ValueIds.size() - 1, 1186 std::string(), UseInstructionNames); 1187 Id = give(isl_id_alloc(IslCtx.get(), Name.c_str(), V)); 1188 } 1189 return Id; 1190 } 1191 1192 /// Create the space for an llvm::Value that is available everywhere. 1193 isl::space makeValueSpace(Value *V) { 1194 auto Result = give(isl_space_set_from_params(ParamSpace.copy())); 1195 return give(isl_space_set_tuple_id(Result.take(), isl_dim_set, 1196 makeValueId(V).take())); 1197 } 1198 1199 /// Create a set with the llvm::Value @p V which is available everywhere. 1200 isl::set makeValueSet(Value *V) { 1201 auto Space = makeValueSpace(V); 1202 return give(isl_set_universe(Space.take())); 1203 } 1204 1205 /// Create a mapping from a statement instance to the instance of an 1206 /// llvm::Value that can be used in there. 1207 /// 1208 /// Although LLVM IR uses single static assignment, llvm::Values can have 1209 /// different contents in loops, when they get redefined in the last 1210 /// iteration. This function tries to get the statement instance of the 1211 /// previous definition, relative to a user. 1212 /// 1213 /// Example: 1214 /// for (int i = 0; i < N; i += 1) { 1215 /// DEF: 1216 /// int v = A[i]; 1217 /// USE: 1218 /// use(v); 1219 /// } 1220 /// 1221 /// The value instance used by statement instance USE[i] is DEF[i]. Hence, 1222 /// makeValInst returns: 1223 /// 1224 /// { USE[i] -> [DEF[i] -> v[]] : 0 <= i < N } 1225 /// 1226 /// @param Val The value to get the instance of. 1227 /// @param UserStmt The statement that uses @p Val. Can be nullptr. 1228 /// @param Scope Loop the using instruction resides in. 1229 /// @param IsCertain Pass true if the definition of @p Val is a 1230 /// MUST_WRITE or false if the write is conditional. 1231 /// 1232 /// @return { DomainUse[] -> ValInst[] } 1233 isl::map makeValInst(Value *Val, ScopStmt *UserStmt, Loop *Scope, 1234 bool IsCertain = true) { 1235 // When known knowledge is disabled, just return the unknown value. It will 1236 // either get filtered out or conflict with itself. 1237 if (!DelicmComputeKnown) 1238 return makeUnknownForDomain(UserStmt); 1239 1240 // If the definition/write is conditional, the value at the location could 1241 // be either the written value or the old value. Since we cannot know which 1242 // one, consider the value to be unknown. 1243 if (!IsCertain) 1244 return makeUnknownForDomain(UserStmt); 1245 1246 auto DomainUse = getDomainFor(UserStmt); 1247 auto VUse = VirtualUse::create(S, UserStmt, Scope, Val, true); 1248 switch (VUse.getKind()) { 1249 case VirtualUse::Constant: 1250 case VirtualUse::Block: 1251 case VirtualUse::Hoisted: 1252 case VirtualUse::ReadOnly: { 1253 // The definition does not depend on the statement which uses it. 1254 auto ValSet = makeValueSet(Val); 1255 return give( 1256 isl_map_from_domain_and_range(DomainUse.take(), ValSet.take())); 1257 } 1258 1259 case VirtualUse::Synthesizable: { 1260 auto *ScevExpr = VUse.getScevExpr(); 1261 auto UseDomainSpace = give(isl_set_get_space(DomainUse.keep())); 1262 1263 // Construct the SCEV space. 1264 // TODO: Add only the induction variables referenced in SCEVAddRecExpr 1265 // expressions, not just all of them. 1266 auto ScevId = give(isl_id_alloc(UseDomainSpace.get_ctx().get(), nullptr, 1267 const_cast<SCEV *>(ScevExpr))); 1268 auto ScevSpace = 1269 give(isl_space_drop_dims(UseDomainSpace.copy(), isl_dim_set, 0, 0)); 1270 ScevSpace = give( 1271 isl_space_set_tuple_id(ScevSpace.take(), isl_dim_set, ScevId.copy())); 1272 1273 // { DomainUse[] -> ScevExpr[] } 1274 auto ValInst = give(isl_map_identity(isl_space_map_from_domain_and_range( 1275 UseDomainSpace.copy(), ScevSpace.copy()))); 1276 return ValInst; 1277 } 1278 1279 case VirtualUse::Intra: { 1280 // Definition and use is in the same statement. We do not need to compute 1281 // a reaching definition. 1282 1283 // { llvm::Value } 1284 auto ValSet = makeValueSet(Val); 1285 1286 // { UserDomain[] -> llvm::Value } 1287 auto ValInstSet = 1288 give(isl_map_from_domain_and_range(DomainUse.take(), ValSet.take())); 1289 1290 // { UserDomain[] -> [UserDomain[] - >llvm::Value] } 1291 auto Result = 1292 give(isl_map_reverse(isl_map_domain_map(ValInstSet.take()))); 1293 simplify(Result); 1294 return Result; 1295 } 1296 1297 case VirtualUse::Inter: { 1298 // The value is defined in a different statement. 1299 1300 auto *Inst = cast<Instruction>(Val); 1301 auto *ValStmt = S->getStmtFor(Inst); 1302 1303 // If the llvm::Value is defined in a removed Stmt, we cannot derive its 1304 // domain. We could use an arbitrary statement, but this could result in 1305 // different ValInst[] for the same llvm::Value. 1306 if (!ValStmt) 1307 return ::makeUnknownForDomain(DomainUse); 1308 1309 // { DomainDef[] } 1310 auto DomainDef = getDomainFor(ValStmt); 1311 1312 // { Scatter[] -> DomainDef[] } 1313 auto ReachDef = getScalarReachingDefinition(DomainDef); 1314 1315 // { DomainUse[] -> Scatter[] } 1316 auto UserSched = getScatterFor(DomainUse); 1317 1318 // { DomainUse[] -> DomainDef[] } 1319 auto UsedInstance = 1320 give(isl_map_apply_range(UserSched.take(), ReachDef.take())); 1321 1322 // { llvm::Value } 1323 auto ValSet = makeValueSet(Val); 1324 1325 // { DomainUse[] -> llvm::Value[] } 1326 auto ValInstSet = 1327 give(isl_map_from_domain_and_range(DomainUse.take(), ValSet.take())); 1328 1329 // { DomainUse[] -> [DomainDef[] -> llvm::Value] } 1330 auto Result = 1331 give(isl_map_range_product(UsedInstance.take(), ValInstSet.take())); 1332 1333 simplify(Result); 1334 return Result; 1335 } 1336 } 1337 llvm_unreachable("Unhandled use type"); 1338 } 1339 1340 /// Compute the different zones. 1341 void computeCommon() { 1342 AllReads = makeEmptyUnionMap(); 1343 AllMayWrites = makeEmptyUnionMap(); 1344 AllMustWrites = makeEmptyUnionMap(); 1345 AllWriteValInst = makeEmptyUnionMap(); 1346 1347 for (auto &Stmt : *S) { 1348 for (auto *MA : Stmt) { 1349 if (!MA->isLatestArrayKind()) 1350 continue; 1351 1352 if (MA->isRead()) 1353 addArrayReadAccess(MA); 1354 1355 if (MA->isWrite()) 1356 addArrayWriteAccess(MA); 1357 } 1358 } 1359 1360 // { DomainWrite[] -> Element[] } 1361 auto AllWrites = 1362 give(isl_union_map_union(AllMustWrites.copy(), AllMayWrites.copy())); 1363 1364 // { [Element[] -> Zone[]] -> DomainWrite[] } 1365 WriteReachDefZone = 1366 computeReachingDefinition(Schedule, AllWrites, false, true); 1367 simplify(WriteReachDefZone); 1368 } 1369 1370 /// Print the current state of all MemoryAccesses to @p. 1371 void printAccesses(llvm::raw_ostream &OS, int Indent = 0) const { 1372 OS.indent(Indent) << "After accesses {\n"; 1373 for (auto &Stmt : *S) { 1374 OS.indent(Indent + 4) << Stmt.getBaseName() << "\n"; 1375 for (auto *MA : Stmt) 1376 MA->print(OS); 1377 } 1378 OS.indent(Indent) << "}\n"; 1379 } 1380 1381 public: 1382 /// Return the SCoP this object is analyzing. 1383 Scop *getScop() const { return S; } 1384 }; 1385 1386 /// Implementation of the DeLICM/DePRE transformation. 1387 class DeLICMImpl : public ZoneAlgorithm { 1388 private: 1389 /// Knowledge before any transformation took place. 1390 Knowledge OriginalZone; 1391 1392 /// Current knowledge of the SCoP including all already applied 1393 /// transformations. 1394 Knowledge Zone; 1395 1396 /// For getting the MemoryAccesses that write or read a given scalar. 1397 ScalarDefUseChains DefUse; 1398 1399 /// Number of StoreInsts something can be mapped to. 1400 int NumberOfCompatibleTargets = 0; 1401 1402 /// The number of StoreInsts to which at least one value or PHI has been 1403 /// mapped to. 1404 int NumberOfTargetsMapped = 0; 1405 1406 /// The number of llvm::Value mapped to some array element. 1407 int NumberOfMappedValueScalars = 0; 1408 1409 /// The number of PHIs mapped to some array element. 1410 int NumberOfMappedPHIScalars = 0; 1411 1412 /// Determine whether two knowledges are conflicting with each other. 1413 /// 1414 /// @see Knowledge::isConflicting 1415 bool isConflicting(const Knowledge &Proposed) { 1416 raw_ostream *OS = nullptr; 1417 DEBUG(OS = &llvm::dbgs()); 1418 return Knowledge::isConflicting(Zone, Proposed, OS, 4); 1419 } 1420 1421 /// Determine whether @p SAI is a scalar that can be mapped to an array 1422 /// element. 1423 bool isMappable(const ScopArrayInfo *SAI) { 1424 assert(SAI); 1425 1426 if (SAI->isValueKind()) { 1427 auto *MA = DefUse.getValueDef(SAI); 1428 if (!MA) { 1429 DEBUG(dbgs() 1430 << " Reject because value is read-only within the scop\n"); 1431 return false; 1432 } 1433 1434 // Mapping if value is used after scop is not supported. The code 1435 // generator would need to reload the scalar after the scop, but it 1436 // does not have the information to where it is mapped to. Only the 1437 // MemoryAccesses have that information, not the ScopArrayInfo. 1438 auto Inst = MA->getAccessInstruction(); 1439 for (auto User : Inst->users()) { 1440 if (!isa<Instruction>(User)) 1441 return false; 1442 auto UserInst = cast<Instruction>(User); 1443 1444 if (!S->contains(UserInst)) { 1445 DEBUG(dbgs() << " Reject because value is escaping\n"); 1446 return false; 1447 } 1448 } 1449 1450 return true; 1451 } 1452 1453 if (SAI->isPHIKind()) { 1454 auto *MA = DefUse.getPHIRead(SAI); 1455 assert(MA); 1456 1457 // Mapping of an incoming block from before the SCoP is not supported by 1458 // the code generator. 1459 auto PHI = cast<PHINode>(MA->getAccessInstruction()); 1460 for (auto Incoming : PHI->blocks()) { 1461 if (!S->contains(Incoming)) { 1462 DEBUG(dbgs() << " Reject because at least one incoming block is " 1463 "not in the scop region\n"); 1464 return false; 1465 } 1466 } 1467 1468 return true; 1469 } 1470 1471 DEBUG(dbgs() << " Reject ExitPHI or other non-value\n"); 1472 return false; 1473 } 1474 1475 /// Compute the uses of a MemoryKind::Value and its lifetime (from its 1476 /// definition to the last use). 1477 /// 1478 /// @param SAI The ScopArrayInfo representing the value's storage. 1479 /// 1480 /// @return { DomainDef[] -> DomainUse[] }, { DomainDef[] -> Zone[] } 1481 /// First element is the set of uses for each definition. 1482 /// The second is the lifetime of each definition. 1483 std::tuple<isl::union_map, isl::map> 1484 computeValueUses(const ScopArrayInfo *SAI) { 1485 assert(SAI->isValueKind()); 1486 1487 // { DomainRead[] } 1488 auto Reads = makeEmptyUnionSet(); 1489 1490 // Find all uses. 1491 for (auto *MA : DefUse.getValueUses(SAI)) 1492 Reads = 1493 give(isl_union_set_add_set(Reads.take(), getDomainFor(MA).take())); 1494 1495 // { DomainRead[] -> Scatter[] } 1496 auto ReadSchedule = getScatterFor(Reads); 1497 1498 auto *DefMA = DefUse.getValueDef(SAI); 1499 assert(DefMA); 1500 1501 // { DomainDef[] } 1502 auto Writes = getDomainFor(DefMA); 1503 1504 // { DomainDef[] -> Scatter[] } 1505 auto WriteScatter = getScatterFor(Writes); 1506 1507 // { Scatter[] -> DomainDef[] } 1508 auto ReachDef = getScalarReachingDefinition(DefMA->getStatement()); 1509 1510 // { [DomainDef[] -> Scatter[]] -> DomainUse[] } 1511 auto Uses = give( 1512 isl_union_map_apply_range(isl_union_map_from_map(isl_map_range_map( 1513 isl_map_reverse(ReachDef.take()))), 1514 isl_union_map_reverse(ReadSchedule.take()))); 1515 1516 // { DomainDef[] -> Scatter[] } 1517 auto UseScatter = 1518 singleton(give(isl_union_set_unwrap(isl_union_map_domain(Uses.copy()))), 1519 give(isl_space_map_from_domain_and_range( 1520 isl_set_get_space(Writes.keep()), ScatterSpace.copy()))); 1521 1522 // { DomainDef[] -> Zone[] } 1523 auto Lifetime = betweenScatter(WriteScatter, UseScatter, false, true); 1524 1525 // { DomainDef[] -> DomainRead[] } 1526 auto DefUses = give(isl_union_map_domain_factor_domain(Uses.take())); 1527 1528 return std::make_pair(DefUses, Lifetime); 1529 } 1530 1531 /// For each 'execution' of a PHINode, get the incoming block that was 1532 /// executed before. 1533 /// 1534 /// For each PHI instance we can directly determine which was the incoming 1535 /// block, and hence derive which value the PHI has. 1536 /// 1537 /// @param SAI The ScopArrayInfo representing the PHI's storage. 1538 /// 1539 /// @return { DomainPHIRead[] -> DomainPHIWrite[] } 1540 isl::union_map computePerPHI(const ScopArrayInfo *SAI) { 1541 assert(SAI->isPHIKind()); 1542 1543 // { DomainPHIWrite[] -> Scatter[] } 1544 auto PHIWriteScatter = makeEmptyUnionMap(); 1545 1546 // Collect all incoming block timepoint. 1547 for (auto *MA : DefUse.getPHIIncomings(SAI)) { 1548 auto Scatter = getScatterFor(MA); 1549 PHIWriteScatter = 1550 give(isl_union_map_add_map(PHIWriteScatter.take(), Scatter.take())); 1551 } 1552 1553 // { DomainPHIRead[] -> Scatter[] } 1554 auto PHIReadScatter = getScatterFor(DefUse.getPHIRead(SAI)); 1555 1556 // { DomainPHIRead[] -> Scatter[] } 1557 auto BeforeRead = beforeScatter(PHIReadScatter, true); 1558 1559 // { Scatter[] } 1560 auto WriteTimes = singleton( 1561 give(isl_union_map_range(PHIWriteScatter.copy())), ScatterSpace); 1562 1563 // { DomainPHIRead[] -> Scatter[] } 1564 auto PHIWriteTimes = 1565 give(isl_map_intersect_range(BeforeRead.take(), WriteTimes.take())); 1566 auto LastPerPHIWrites = give(isl_map_lexmax(PHIWriteTimes.take())); 1567 1568 // { DomainPHIRead[] -> DomainPHIWrite[] } 1569 auto Result = give(isl_union_map_apply_range( 1570 isl_union_map_from_map(LastPerPHIWrites.take()), 1571 isl_union_map_reverse(PHIWriteScatter.take()))); 1572 assert(isl_union_map_is_single_valued(Result.keep()) == isl_bool_true); 1573 assert(isl_union_map_is_injective(Result.keep()) == isl_bool_true); 1574 return Result; 1575 } 1576 1577 /// Try to map a MemoryKind::Value to a given array element. 1578 /// 1579 /// @param SAI Representation of the scalar's memory to map. 1580 /// @param TargetElt { Scatter[] -> Element[] } 1581 /// Suggestion where to map a scalar to when at a timepoint. 1582 /// 1583 /// @return true if the scalar was successfully mapped. 1584 bool tryMapValue(const ScopArrayInfo *SAI, isl::map TargetElt) { 1585 assert(SAI->isValueKind()); 1586 1587 auto *DefMA = DefUse.getValueDef(SAI); 1588 assert(DefMA->isValueKind()); 1589 assert(DefMA->isMustWrite()); 1590 auto *V = DefMA->getAccessValue(); 1591 auto *DefInst = DefMA->getAccessInstruction(); 1592 1593 // Stop if the scalar has already been mapped. 1594 if (!DefMA->getLatestScopArrayInfo()->isValueKind()) 1595 return false; 1596 1597 // { DomainDef[] -> Scatter[] } 1598 auto DefSched = getScatterFor(DefMA); 1599 1600 // Where each write is mapped to, according to the suggestion. 1601 // { DomainDef[] -> Element[] } 1602 auto DefTarget = give(isl_map_apply_domain( 1603 TargetElt.copy(), isl_map_reverse(DefSched.copy()))); 1604 simplify(DefTarget); 1605 DEBUG(dbgs() << " Def Mapping: " << DefTarget << '\n'); 1606 1607 auto OrigDomain = getDomainFor(DefMA); 1608 auto MappedDomain = give(isl_map_domain(DefTarget.copy())); 1609 if (!isl_set_is_subset(OrigDomain.keep(), MappedDomain.keep())) { 1610 DEBUG(dbgs() 1611 << " Reject because mapping does not encompass all instances\n"); 1612 return false; 1613 } 1614 1615 // { DomainDef[] -> Zone[] } 1616 isl::map Lifetime; 1617 1618 // { DomainDef[] -> DomainUse[] } 1619 isl::union_map DefUses; 1620 1621 std::tie(DefUses, Lifetime) = computeValueUses(SAI); 1622 DEBUG(dbgs() << " Lifetime: " << Lifetime << '\n'); 1623 1624 /// { [Element[] -> Zone[]] } 1625 auto EltZone = give( 1626 isl_map_wrap(isl_map_apply_domain(Lifetime.copy(), DefTarget.copy()))); 1627 simplify(EltZone); 1628 1629 // { DomainDef[] -> ValInst[] } 1630 auto ValInst = makeValInst(V, DefMA->getStatement(), 1631 LI->getLoopFor(DefInst->getParent())); 1632 1633 // { DomainDef[] -> [Element[] -> Zone[]] } 1634 auto EltKnownTranslator = 1635 give(isl_map_range_product(DefTarget.copy(), Lifetime.copy())); 1636 1637 // { [Element[] -> Zone[]] -> ValInst[] } 1638 auto EltKnown = 1639 give(isl_map_apply_domain(ValInst.copy(), EltKnownTranslator.take())); 1640 simplify(EltKnown); 1641 1642 // { DomainDef[] -> [Element[] -> Scatter[]] } 1643 auto WrittenTranslator = 1644 give(isl_map_range_product(DefTarget.copy(), DefSched.take())); 1645 1646 // { [Element[] -> Scatter[]] -> ValInst[] } 1647 auto DefEltSched = 1648 give(isl_map_apply_domain(ValInst.copy(), WrittenTranslator.take())); 1649 simplify(DefEltSched); 1650 1651 Knowledge Proposed(EltZone, nullptr, filterKnownValInst(EltKnown), 1652 DefEltSched); 1653 if (isConflicting(Proposed)) 1654 return false; 1655 1656 // { DomainUse[] -> Element[] } 1657 auto UseTarget = give( 1658 isl_union_map_apply_range(isl_union_map_reverse(DefUses.take()), 1659 isl_union_map_from_map(DefTarget.copy()))); 1660 1661 mapValue(SAI, std::move(DefTarget), std::move(UseTarget), 1662 std::move(Lifetime), std::move(Proposed)); 1663 return true; 1664 } 1665 1666 /// After a scalar has been mapped, update the global knowledge. 1667 void applyLifetime(Knowledge Proposed) { 1668 Zone.learnFrom(std::move(Proposed)); 1669 } 1670 1671 /// Map a MemoryKind::Value scalar to an array element. 1672 /// 1673 /// Callers must have ensured that the mapping is valid and not conflicting. 1674 /// 1675 /// @param SAI The ScopArrayInfo representing the scalar's memory to 1676 /// map. 1677 /// @param DefTarget { DomainDef[] -> Element[] } 1678 /// The array element to map the scalar to. 1679 /// @param UseTarget { DomainUse[] -> Element[] } 1680 /// The array elements the uses are mapped to. 1681 /// @param Lifetime { DomainDef[] -> Zone[] } 1682 /// The lifetime of each llvm::Value definition for 1683 /// reporting. 1684 /// @param Proposed Mapping constraints for reporting. 1685 void mapValue(const ScopArrayInfo *SAI, isl::map DefTarget, 1686 isl::union_map UseTarget, isl::map Lifetime, 1687 Knowledge Proposed) { 1688 // Redirect the read accesses. 1689 for (auto *MA : DefUse.getValueUses(SAI)) { 1690 // { DomainUse[] } 1691 auto Domain = getDomainFor(MA); 1692 1693 // { DomainUse[] -> Element[] } 1694 auto NewAccRel = give(isl_union_map_intersect_domain( 1695 UseTarget.copy(), isl_union_set_from_set(Domain.take()))); 1696 simplify(NewAccRel); 1697 1698 assert(isl_union_map_n_map(NewAccRel.keep()) == 1); 1699 MA->setNewAccessRelation(isl_map_from_union_map(NewAccRel.take())); 1700 } 1701 1702 auto *WA = DefUse.getValueDef(SAI); 1703 WA->setNewAccessRelation(DefTarget.copy()); 1704 applyLifetime(Proposed); 1705 1706 MappedValueScalars++; 1707 NumberOfMappedValueScalars += 1; 1708 } 1709 1710 /// Express the incoming values of a PHI for each incoming statement in an 1711 /// isl::union_map. 1712 /// 1713 /// @param SAI The PHI scalar represented by a ScopArrayInfo. 1714 /// 1715 /// @return { PHIWriteDomain[] -> ValInst[] } 1716 isl::union_map determinePHIWrittenValues(const ScopArrayInfo *SAI) { 1717 auto Result = makeEmptyUnionMap(); 1718 1719 // Collect the incoming values. 1720 for (auto *MA : DefUse.getPHIIncomings(SAI)) { 1721 // { DomainWrite[] -> ValInst[] } 1722 isl::union_map ValInst; 1723 auto *WriteStmt = MA->getStatement(); 1724 1725 auto Incoming = MA->getIncoming(); 1726 assert(!Incoming.empty()); 1727 if (Incoming.size() == 1) { 1728 ValInst = makeValInst(Incoming[0].second, WriteStmt, 1729 LI->getLoopFor(Incoming[0].first)); 1730 } else { 1731 // If the PHI is in a subregion's exit node it can have multiple 1732 // incoming values (+ maybe another incoming edge from an unrelated 1733 // block). We cannot directly represent it as a single llvm::Value. 1734 // We currently model it as unknown value, but modeling as the PHIInst 1735 // itself could be OK, too. 1736 ValInst = makeUnknownForDomain(WriteStmt); 1737 } 1738 1739 Result = give(isl_union_map_union(Result.take(), ValInst.take())); 1740 } 1741 1742 assert(isl_union_map_is_single_valued(Result.keep()) == isl_bool_true && 1743 "Cannot have multiple incoming values for same incoming statement"); 1744 return Result; 1745 } 1746 1747 /// Try to map a MemoryKind::PHI scalar to a given array element. 1748 /// 1749 /// @param SAI Representation of the scalar's memory to map. 1750 /// @param TargetElt { Scatter[] -> Element[] } 1751 /// Suggestion where to map the scalar to when at a 1752 /// timepoint. 1753 /// 1754 /// @return true if the PHI scalar has been mapped. 1755 bool tryMapPHI(const ScopArrayInfo *SAI, isl::map TargetElt) { 1756 auto *PHIRead = DefUse.getPHIRead(SAI); 1757 assert(PHIRead->isPHIKind()); 1758 assert(PHIRead->isRead()); 1759 1760 // Skip if already been mapped. 1761 if (!PHIRead->getLatestScopArrayInfo()->isPHIKind()) 1762 return false; 1763 1764 // { DomainRead[] -> Scatter[] } 1765 auto PHISched = getScatterFor(PHIRead); 1766 1767 // { DomainRead[] -> Element[] } 1768 auto PHITarget = 1769 give(isl_map_apply_range(PHISched.copy(), TargetElt.copy())); 1770 simplify(PHITarget); 1771 DEBUG(dbgs() << " Mapping: " << PHITarget << '\n'); 1772 1773 auto OrigDomain = getDomainFor(PHIRead); 1774 auto MappedDomain = give(isl_map_domain(PHITarget.copy())); 1775 if (!isl_set_is_subset(OrigDomain.keep(), MappedDomain.keep())) { 1776 DEBUG(dbgs() 1777 << " Reject because mapping does not encompass all instances\n"); 1778 return false; 1779 } 1780 1781 // { DomainRead[] -> DomainWrite[] } 1782 auto PerPHIWrites = computePerPHI(SAI); 1783 1784 // { DomainWrite[] -> Element[] } 1785 auto WritesTarget = give(isl_union_map_reverse(isl_union_map_apply_domain( 1786 PerPHIWrites.copy(), isl_union_map_from_map(PHITarget.copy())))); 1787 simplify(WritesTarget); 1788 1789 // { DomainWrite[] } 1790 auto UniverseWritesDom = give(isl_union_set_empty(ParamSpace.copy())); 1791 1792 for (auto *MA : DefUse.getPHIIncomings(SAI)) 1793 UniverseWritesDom = give(isl_union_set_add_set(UniverseWritesDom.take(), 1794 getDomainFor(MA).take())); 1795 1796 auto RelevantWritesTarget = WritesTarget; 1797 if (DelicmOverapproximateWrites) 1798 WritesTarget = expandMapping(WritesTarget, UniverseWritesDom); 1799 1800 auto ExpandedWritesDom = give(isl_union_map_domain(WritesTarget.copy())); 1801 if (!DelicmPartialWrites && 1802 !isl_union_set_is_subset(UniverseWritesDom.keep(), 1803 ExpandedWritesDom.keep())) { 1804 DEBUG(dbgs() << " Reject because did not find PHI write mapping for " 1805 "all instances\n"); 1806 if (DelicmOverapproximateWrites) 1807 DEBUG(dbgs() << " Relevant Mapping: " << RelevantWritesTarget 1808 << '\n'); 1809 DEBUG(dbgs() << " Deduced Mapping: " << WritesTarget << '\n'); 1810 DEBUG(dbgs() << " Missing instances: " 1811 << give(isl_union_set_subtract(UniverseWritesDom.copy(), 1812 ExpandedWritesDom.copy())) 1813 << '\n'); 1814 return false; 1815 } 1816 1817 // { DomainRead[] -> Scatter[] } 1818 auto PerPHIWriteScatter = give(isl_map_from_union_map( 1819 isl_union_map_apply_range(PerPHIWrites.copy(), Schedule.copy()))); 1820 1821 // { DomainRead[] -> Zone[] } 1822 auto Lifetime = betweenScatter(PerPHIWriteScatter, PHISched, false, true); 1823 simplify(Lifetime); 1824 DEBUG(dbgs() << " Lifetime: " << Lifetime << "\n"); 1825 1826 // { DomainWrite[] -> Zone[] } 1827 auto WriteLifetime = give(isl_union_map_apply_domain( 1828 isl_union_map_from_map(Lifetime.copy()), PerPHIWrites.copy())); 1829 1830 // { DomainWrite[] -> ValInst[] } 1831 auto WrittenValue = determinePHIWrittenValues(SAI); 1832 1833 // { DomainWrite[] -> [Element[] -> Scatter[]] } 1834 auto WrittenTranslator = 1835 give(isl_union_map_range_product(WritesTarget.copy(), Schedule.copy())); 1836 1837 // { [Element[] -> Scatter[]] -> ValInst[] } 1838 auto Written = give(isl_union_map_apply_domain(WrittenValue.copy(), 1839 WrittenTranslator.copy())); 1840 simplify(Written); 1841 1842 // { DomainWrite[] -> [Element[] -> Zone[]] } 1843 auto LifetimeTranslator = give( 1844 isl_union_map_range_product(WritesTarget.copy(), WriteLifetime.copy())); 1845 1846 // { DomainWrite[] -> ValInst[] } 1847 auto WrittenKnownValue = filterKnownValInst(WrittenValue); 1848 1849 // { [Element[] -> Zone[]] -> ValInst[] } 1850 auto EltLifetimeInst = give(isl_union_map_apply_domain( 1851 WrittenKnownValue.copy(), LifetimeTranslator.copy())); 1852 simplify(EltLifetimeInst); 1853 1854 // { [Element[] -> Zone[] } 1855 auto Occupied = give(isl_union_map_range(LifetimeTranslator.copy())); 1856 simplify(Occupied); 1857 1858 Knowledge Proposed(Occupied, nullptr, EltLifetimeInst, Written); 1859 if (isConflicting(Proposed)) 1860 return false; 1861 1862 mapPHI(SAI, std::move(PHITarget), std::move(WritesTarget), 1863 std::move(Lifetime), std::move(Proposed)); 1864 return true; 1865 } 1866 1867 /// Map a MemoryKind::PHI scalar to an array element. 1868 /// 1869 /// Callers must have ensured that the mapping is valid and not conflicting 1870 /// with the common knowledge. 1871 /// 1872 /// @param SAI The ScopArrayInfo representing the scalar's memory to 1873 /// map. 1874 /// @param ReadTarget { DomainRead[] -> Element[] } 1875 /// The array element to map the scalar to. 1876 /// @param WriteTarget { DomainWrite[] -> Element[] } 1877 /// New access target for each PHI incoming write. 1878 /// @param Lifetime { DomainRead[] -> Zone[] } 1879 /// The lifetime of each PHI for reporting. 1880 /// @param Proposed Mapping constraints for reporting. 1881 void mapPHI(const ScopArrayInfo *SAI, isl::map ReadTarget, 1882 isl::union_map WriteTarget, isl::map Lifetime, 1883 Knowledge Proposed) { 1884 // Redirect the PHI incoming writes. 1885 for (auto *MA : DefUse.getPHIIncomings(SAI)) { 1886 // { DomainWrite[] } 1887 auto Domain = getDomainFor(MA); 1888 1889 // { DomainWrite[] -> Element[] } 1890 auto NewAccRel = give(isl_union_map_intersect_domain( 1891 WriteTarget.copy(), isl_union_set_from_set(Domain.take()))); 1892 simplify(NewAccRel); 1893 1894 assert(isl_union_map_n_map(NewAccRel.keep()) == 1); 1895 MA->setNewAccessRelation(isl_map_from_union_map(NewAccRel.take())); 1896 } 1897 1898 // Redirect the PHI read. 1899 auto *PHIRead = DefUse.getPHIRead(SAI); 1900 PHIRead->setNewAccessRelation(ReadTarget.copy()); 1901 applyLifetime(Proposed); 1902 1903 MappedPHIScalars++; 1904 NumberOfMappedPHIScalars++; 1905 } 1906 1907 /// Search and map scalars to memory overwritten by @p TargetStoreMA. 1908 /// 1909 /// Start trying to map scalars that are used in the same statement as the 1910 /// store. For every successful mapping, try to also map scalars of the 1911 /// statements where those are written. Repeat, until no more mapping 1912 /// opportunity is found. 1913 /// 1914 /// There is currently no preference in which order scalars are tried. 1915 /// Ideally, we would direct it towards a load instruction of the same array 1916 /// element. 1917 bool collapseScalarsToStore(MemoryAccess *TargetStoreMA) { 1918 assert(TargetStoreMA->isLatestArrayKind()); 1919 assert(TargetStoreMA->isMustWrite()); 1920 1921 auto TargetStmt = TargetStoreMA->getStatement(); 1922 1923 // { DomTarget[] } 1924 auto TargetDom = getDomainFor(TargetStmt); 1925 1926 // { DomTarget[] -> Element[] } 1927 auto TargetAccRel = getAccessRelationFor(TargetStoreMA); 1928 1929 // { Zone[] -> DomTarget[] } 1930 // For each point in time, find the next target store instance. 1931 auto Target = 1932 computeScalarReachingOverwrite(Schedule, TargetDom, false, true); 1933 1934 // { Zone[] -> Element[] } 1935 // Use the target store's write location as a suggestion to map scalars to. 1936 auto EltTarget = 1937 give(isl_map_apply_range(Target.take(), TargetAccRel.take())); 1938 simplify(EltTarget); 1939 DEBUG(dbgs() << " Target mapping is " << EltTarget << '\n'); 1940 1941 // Stack of elements not yet processed. 1942 SmallVector<MemoryAccess *, 16> Worklist; 1943 1944 // Set of scalars already tested. 1945 SmallPtrSet<const ScopArrayInfo *, 16> Closed; 1946 1947 // Lambda to add all scalar reads to the work list. 1948 auto ProcessAllIncoming = [&](ScopStmt *Stmt) { 1949 for (auto *MA : *Stmt) { 1950 if (!MA->isLatestScalarKind()) 1951 continue; 1952 if (!MA->isRead()) 1953 continue; 1954 1955 Worklist.push_back(MA); 1956 } 1957 }; 1958 1959 auto *WrittenVal = TargetStoreMA->getAccessInstruction()->getOperand(0); 1960 if (auto *WrittenValInputMA = TargetStmt->lookupInputAccessOf(WrittenVal)) 1961 Worklist.push_back(WrittenValInputMA); 1962 else 1963 ProcessAllIncoming(TargetStmt); 1964 1965 auto AnyMapped = false; 1966 auto &DL = S->getRegion().getEntry()->getModule()->getDataLayout(); 1967 auto StoreSize = 1968 DL.getTypeAllocSize(TargetStoreMA->getAccessValue()->getType()); 1969 1970 while (!Worklist.empty()) { 1971 auto *MA = Worklist.pop_back_val(); 1972 1973 auto *SAI = MA->getScopArrayInfo(); 1974 if (Closed.count(SAI)) 1975 continue; 1976 Closed.insert(SAI); 1977 DEBUG(dbgs() << "\n Trying to map " << MA << " (SAI: " << SAI 1978 << ")\n"); 1979 1980 // Skip non-mappable scalars. 1981 if (!isMappable(SAI)) 1982 continue; 1983 1984 auto MASize = DL.getTypeAllocSize(MA->getAccessValue()->getType()); 1985 if (MASize > StoreSize) { 1986 DEBUG(dbgs() << " Reject because storage size is insufficient\n"); 1987 continue; 1988 } 1989 1990 // Try to map MemoryKind::Value scalars. 1991 if (SAI->isValueKind()) { 1992 if (!tryMapValue(SAI, EltTarget)) 1993 continue; 1994 1995 auto *DefAcc = DefUse.getValueDef(SAI); 1996 ProcessAllIncoming(DefAcc->getStatement()); 1997 1998 AnyMapped = true; 1999 continue; 2000 } 2001 2002 // Try to map MemoryKind::PHI scalars. 2003 if (SAI->isPHIKind()) { 2004 if (!tryMapPHI(SAI, EltTarget)) 2005 continue; 2006 // Add inputs of all incoming statements to the worklist. Prefer the 2007 // input accesses of the incoming blocks. 2008 for (auto *PHIWrite : DefUse.getPHIIncomings(SAI)) { 2009 auto *PHIWriteStmt = PHIWrite->getStatement(); 2010 bool FoundAny = false; 2011 for (auto Incoming : PHIWrite->getIncoming()) { 2012 auto *IncomingInputMA = 2013 PHIWriteStmt->lookupInputAccessOf(Incoming.second); 2014 if (!IncomingInputMA) 2015 continue; 2016 2017 Worklist.push_back(IncomingInputMA); 2018 FoundAny = true; 2019 } 2020 2021 if (!FoundAny) 2022 ProcessAllIncoming(PHIWrite->getStatement()); 2023 } 2024 2025 AnyMapped = true; 2026 continue; 2027 } 2028 } 2029 2030 if (AnyMapped) { 2031 TargetsMapped++; 2032 NumberOfTargetsMapped++; 2033 } 2034 return AnyMapped; 2035 } 2036 2037 /// Compute when an array element is unused. 2038 /// 2039 /// @return { [Element[] -> Zone[]] } 2040 isl::union_set computeLifetime() const { 2041 // { Element[] -> Zone[] } 2042 auto ArrayUnused = computeArrayUnused(Schedule, AllMustWrites, AllReads, 2043 false, false, true); 2044 2045 auto Result = give(isl_union_map_wrap(ArrayUnused.copy())); 2046 2047 simplify(Result); 2048 return Result; 2049 } 2050 2051 /// Compute which value an array element stores at every instant. 2052 /// 2053 /// @return { [Element[] -> Zone[]] -> ValInst[] } 2054 isl::union_map computeKnown() const { 2055 // { [Element[] -> Zone[]] -> [Element[] -> DomainWrite[]] } 2056 auto EltReachdDef = 2057 distributeDomain(give(isl_union_map_curry(WriteReachDefZone.copy()))); 2058 2059 // { [Element[] -> DomainWrite[]] -> ValInst[] } 2060 auto AllKnownWriteValInst = filterKnownValInst(AllWriteValInst); 2061 2062 // { [Element[] -> Zone[]] -> ValInst[] } 2063 return EltReachdDef.apply_range(AllKnownWriteValInst); 2064 } 2065 2066 /// Determine when an array element is written to, and which value instance is 2067 /// written. 2068 /// 2069 /// @return { [Element[] -> Scatter[]] -> ValInst[] } 2070 isl::union_map computeWritten() const { 2071 // { [Element[] -> Scatter[]] -> ValInst[] } 2072 auto EltWritten = applyDomainRange(AllWriteValInst, Schedule); 2073 2074 simplify(EltWritten); 2075 return EltWritten; 2076 } 2077 2078 /// Determine whether an access touches at most one element. 2079 /// 2080 /// The accessed element could be a scalar or accessing an array with constant 2081 /// subscript, such that all instances access only that element. 2082 /// 2083 /// @param MA The access to test. 2084 /// 2085 /// @return True, if zero or one elements are accessed; False if at least two 2086 /// different elements are accessed. 2087 bool isScalarAccess(MemoryAccess *MA) { 2088 auto Map = getAccessRelationFor(MA); 2089 auto Set = give(isl_map_range(Map.take())); 2090 return isl_set_is_singleton(Set.keep()) == isl_bool_true; 2091 } 2092 2093 /// Print mapping statistics to @p OS. 2094 void printStatistics(llvm::raw_ostream &OS, int Indent = 0) const { 2095 OS.indent(Indent) << "Statistics {\n"; 2096 OS.indent(Indent + 4) << "Compatible overwrites: " 2097 << NumberOfCompatibleTargets << "\n"; 2098 OS.indent(Indent + 4) << "Overwrites mapped to: " << NumberOfTargetsMapped 2099 << '\n'; 2100 OS.indent(Indent + 4) << "Value scalars mapped: " 2101 << NumberOfMappedValueScalars << '\n'; 2102 OS.indent(Indent + 4) << "PHI scalars mapped: " 2103 << NumberOfMappedPHIScalars << '\n'; 2104 OS.indent(Indent) << "}\n"; 2105 } 2106 2107 /// Return whether at least one transformation been applied. 2108 bool isModified() const { return NumberOfTargetsMapped > 0; } 2109 2110 public: 2111 DeLICMImpl(Scop *S, LoopInfo *LI) : ZoneAlgorithm(S, LI) {} 2112 2113 /// Calculate the lifetime (definition to last use) of every array element. 2114 /// 2115 /// @return True if the computed lifetimes (#Zone) is usable. 2116 bool computeZone() { 2117 // Check that nothing strange occurs. 2118 if (!isCompatibleScop()) { 2119 DeLICMIncompatible++; 2120 return false; 2121 } 2122 2123 DefUse.compute(S); 2124 isl::union_set EltUnused; 2125 isl::union_map EltKnown, EltWritten; 2126 2127 { 2128 IslMaxOperationsGuard MaxOpGuard(IslCtx.get(), DelicmMaxOps); 2129 2130 computeCommon(); 2131 2132 EltUnused = computeLifetime(); 2133 EltKnown = computeKnown(); 2134 EltWritten = computeWritten(); 2135 } 2136 DeLICMAnalyzed++; 2137 2138 if (!EltUnused || !EltKnown || !EltWritten) { 2139 assert(isl_ctx_last_error(IslCtx.get()) == isl_error_quota && 2140 "The only reason that these things have not been computed should " 2141 "be if the max-operations limit hit"); 2142 DeLICMOutOfQuota++; 2143 DEBUG(dbgs() << "DeLICM analysis exceeded max_operations\n"); 2144 DebugLoc Begin, End; 2145 getDebugLocations(getBBPairForRegion(&S->getRegion()), Begin, End); 2146 OptimizationRemarkAnalysis R(DEBUG_TYPE, "OutOfQuota", Begin, 2147 S->getEntry()); 2148 R << "maximal number of operations exceeded during zone analysis"; 2149 S->getFunction().getContext().diagnose(R); 2150 return false; 2151 } 2152 2153 Zone = OriginalZone = Knowledge(nullptr, EltUnused, EltKnown, EltWritten); 2154 DEBUG(dbgs() << "Computed Zone:\n"; OriginalZone.print(dbgs(), 4)); 2155 2156 assert(Zone.isUsable() && OriginalZone.isUsable()); 2157 return true; 2158 } 2159 2160 /// Try to map as many scalars to unused array elements as possible. 2161 /// 2162 /// Multiple scalars might be mappable to intersecting unused array element 2163 /// zones, but we can only chose one. This is a greedy algorithm, therefore 2164 /// the first processed element claims it. 2165 void greedyCollapse() { 2166 bool Modified = false; 2167 2168 for (auto &Stmt : *S) { 2169 for (auto *MA : Stmt) { 2170 if (!MA->isLatestArrayKind()) 2171 continue; 2172 if (!MA->isWrite()) 2173 continue; 2174 2175 if (MA->isMayWrite()) { 2176 DEBUG(dbgs() << "Access " << MA 2177 << " pruned because it is a MAY_WRITE\n"); 2178 OptimizationRemarkMissed R(DEBUG_TYPE, "TargetMayWrite", 2179 MA->getAccessInstruction()); 2180 R << "Skipped possible mapping target because it is not an " 2181 "unconditional overwrite"; 2182 S->getFunction().getContext().diagnose(R); 2183 continue; 2184 } 2185 2186 if (Stmt.getNumIterators() == 0) { 2187 DEBUG(dbgs() << "Access " << MA 2188 << " pruned because it is not in a loop\n"); 2189 OptimizationRemarkMissed R(DEBUG_TYPE, "WriteNotInLoop", 2190 MA->getAccessInstruction()); 2191 R << "skipped possible mapping target because it is not in a loop"; 2192 S->getFunction().getContext().diagnose(R); 2193 continue; 2194 } 2195 2196 if (isScalarAccess(MA)) { 2197 DEBUG(dbgs() << "Access " << MA 2198 << " pruned because it writes only a single element\n"); 2199 OptimizationRemarkMissed R(DEBUG_TYPE, "ScalarWrite", 2200 MA->getAccessInstruction()); 2201 R << "skipped possible mapping target because the memory location " 2202 "written to does not depend on its outer loop"; 2203 S->getFunction().getContext().diagnose(R); 2204 continue; 2205 } 2206 2207 NumberOfCompatibleTargets++; 2208 DEBUG(dbgs() << "Analyzing target access " << MA << "\n"); 2209 if (collapseScalarsToStore(MA)) 2210 Modified = true; 2211 } 2212 } 2213 2214 if (Modified) 2215 DeLICMScopsModified++; 2216 } 2217 2218 /// Dump the internal information about a performed DeLICM to @p OS. 2219 void print(llvm::raw_ostream &OS, int Indent = 0) { 2220 if (!Zone.isUsable()) { 2221 OS.indent(Indent) << "Zone not computed\n"; 2222 return; 2223 } 2224 2225 printStatistics(OS, Indent); 2226 if (!isModified()) { 2227 OS.indent(Indent) << "No modification has been made\n"; 2228 return; 2229 } 2230 printAccesses(OS, Indent); 2231 } 2232 }; 2233 2234 class DeLICM : public ScopPass { 2235 private: 2236 DeLICM(const DeLICM &) = delete; 2237 const DeLICM &operator=(const DeLICM &) = delete; 2238 2239 /// The pass implementation, also holding per-scop data. 2240 std::unique_ptr<DeLICMImpl> Impl; 2241 2242 void collapseToUnused(Scop &S) { 2243 auto &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo(); 2244 Impl = make_unique<DeLICMImpl>(&S, &LI); 2245 2246 if (!Impl->computeZone()) { 2247 DEBUG(dbgs() << "Abort because cannot reliably compute lifetimes\n"); 2248 return; 2249 } 2250 2251 DEBUG(dbgs() << "Collapsing scalars to unused array elements...\n"); 2252 Impl->greedyCollapse(); 2253 2254 DEBUG(dbgs() << "\nFinal Scop:\n"); 2255 DEBUG(S.print(dbgs())); 2256 } 2257 2258 public: 2259 static char ID; 2260 explicit DeLICM() : ScopPass(ID) {} 2261 2262 virtual void getAnalysisUsage(AnalysisUsage &AU) const override { 2263 AU.addRequiredTransitive<ScopInfoRegionPass>(); 2264 AU.addRequired<LoopInfoWrapperPass>(); 2265 AU.setPreservesAll(); 2266 } 2267 2268 virtual bool runOnScop(Scop &S) override { 2269 // Free resources for previous scop's computation, if not yet done. 2270 releaseMemory(); 2271 2272 collapseToUnused(S); 2273 2274 return false; 2275 } 2276 2277 virtual void printScop(raw_ostream &OS, Scop &S) const override { 2278 if (!Impl) 2279 return; 2280 assert(Impl->getScop() == &S); 2281 2282 OS << "DeLICM result:\n"; 2283 Impl->print(OS); 2284 } 2285 2286 virtual void releaseMemory() override { Impl.reset(); } 2287 }; 2288 2289 char DeLICM::ID; 2290 } // anonymous namespace 2291 2292 Pass *polly::createDeLICMPass() { return new DeLICM(); } 2293 2294 INITIALIZE_PASS_BEGIN(DeLICM, "polly-delicm", "Polly - DeLICM/DePRE", false, 2295 false) 2296 INITIALIZE_PASS_DEPENDENCY(ScopInfoWrapperPass) 2297 INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) 2298 INITIALIZE_PASS_END(DeLICM, "polly-delicm", "Polly - DeLICM/DePRE", false, 2299 false) 2300 2301 bool polly::isConflicting( 2302 isl::union_set ExistingOccupied, isl::union_set ExistingUnused, 2303 isl::union_map ExistingKnown, isl::union_map ExistingWrites, 2304 isl::union_set ProposedOccupied, isl::union_set ProposedUnused, 2305 isl::union_map ProposedKnown, isl::union_map ProposedWrites, 2306 llvm::raw_ostream *OS, unsigned Indent) { 2307 Knowledge Existing(std::move(ExistingOccupied), std::move(ExistingUnused), 2308 std::move(ExistingKnown), std::move(ExistingWrites)); 2309 Knowledge Proposed(std::move(ProposedOccupied), std::move(ProposedUnused), 2310 std::move(ProposedKnown), std::move(ProposedWrites)); 2311 2312 return Knowledge::isConflicting(Existing, Proposed, OS, Indent); 2313 } 2314