bool LSLocation::reduce(LSLocation Base, SILModule *M, LSLocationSet &Locs) { // If this is a class reference type, we have reached end of the type tree. if (Base.getType(M).getClassOrBoundGenericClass()) return Locs.find(Base) != Locs.end(); // This is a leaf node. LSLocationList NextLevel; Base.getNextLevelLSLocations(NextLevel, M); if (NextLevel.empty()) return Locs.find(Base) != Locs.end(); // This is not a leaf node, try to find whether all its children are alive. bool Alive = true; for (auto &X : NextLevel) { Alive &= LSLocation::reduce(X, M, Locs); } // All next level locations are alive, create the new aggregated location. if (Alive) { for (auto &X : NextLevel) Locs.erase(X); Locs.insert(Base); } return Alive; }
void LSLocation::expand(LSLocation Base, SILModule *M, LSLocationList &Locs, TypeExpansionAnalysis *TE) { const ProjectionPath &BasePath = Base.getPath().getValue(); for (const auto &P : TE->getTypeExpansion(Base.getType(M), M)) { Locs.push_back(LSLocation(Base.getBase(), BasePath, P.getValue())); } }
void DSEContext::processRead(SILInstruction *I, SILValue Mem, DSEKind Kind) { auto *S = getBlockState(I); // Construct a LSLocation to represent the memory read by this instruction. // NOTE: The base will point to the actual object this inst is accessing, // not this particular field. // // e.g. %1 = alloc_stack $S // %2 = struct_element_addr %1, #a // %3 = load %2 : $*Int // // Base will point to %1, but not %2. Projection path will indicate which // field is accessed. // // This will make comparison between locations easier. This eases the // implementation of intersection operator in the data flow. LSLocation L; if (BaseToLocIndex.find(Mem) != BaseToLocIndex.end()) { L = BaseToLocIndex[Mem]; } else { SILValue UO = getUnderlyingObject(Mem); L = LSLocation(UO, ProjectionPath::getProjectionPath(UO, Mem)); } // If we can't figure out the Base or Projection Path for the read instruction, // process it as an unknown memory instruction for now. if (!L.isValid()) { processUnknownReadInst(I, Kind); return; } // Expand the given Mem into individual fields and process them as separate // reads. LSLocationList Locs; LSLocation::expand(L, &I->getModule(), Locs, TE); // Are we building the genset and killset. if (isBuildingGenKillSet(Kind)) { for (auto &E : Locs) { // Only building the gen and kill sets for now. processReadForGenKillSet(S, getLocationBit(E)); } return; } // Are we performing the actual DSE. if (isPerformingDSE(Kind)) { for (auto &E : Locs) { // This is the last iteration, compute BBWriteSetOut and perform DSE. processReadForDSE(S, getLocationBit(E)); } return; } llvm_unreachable("Unknown DSE compute kind"); }
void LSLocation::expand(LSLocation Base, SILModule *M, LSLocationList &Locs, TypeExpansionAnalysis *TE) { // To expand a memory location to its indivisible parts, we first get the // address projection paths from the accessed type to each indivisible field, // i.e. leaf nodes, then we append these projection paths to the Base. // // Construct the LSLocation by appending the projection path from the // accessed node to the leaf nodes. const NewProjectionPath &BasePath = Base.getPath().getValue(); for (const auto &P : TE->getTypeExpansion(Base.getType(M), M, TEKind::TELeaf)) { Locs.push_back(LSLocation(Base.getBase(), BasePath, P.getValue())); } }
SILValue RLEContext::computePredecessorLocationValue(SILBasicBlock *BB, LSLocation &L) { BBValueMap Values; llvm::DenseSet<SILBasicBlock *> HandledBBs; llvm::SmallVector<SILBasicBlock *, 8> WorkList; // Push in all the predecessors to get started. for (auto Pred : BB->getPreds()) { WorkList.push_back(Pred); } while (!WorkList.empty()) { auto *CurBB = WorkList.pop_back_val(); BlockState &Forwarder = getBlockState(CurBB); // Mark this basic block as processed. HandledBBs.insert(CurBB); // This BlockState contains concrete values for all the expanded // locations, // collect and reduce them into a single value in the current block. if (Forwarder.isConcreteValues(*this, L)) { Values[CurBB] = Forwarder.reduceValuesAtEndOfBlock(*this, L); continue; } // This BlockState does not contain concrete value for any of the expanded // locations, collect in this block's predecessors. if (Forwarder.isCoverValues(*this, L)) { for (auto Pred : CurBB->getPreds()) { if (HandledBBs.find(Pred) != HandledBBs.end()) continue; WorkList.push_back(Pred); } continue; } // This BlockState contains concrete values for some but not all the // expanded locations, recursively call gatherLocationValues to // materialize // the value that reaches this basic block. LSLocationValueMap LSValues; if (!gatherLocationValues(CurBB, L, LSValues, Forwarder.getForwardValOut())) return SILValue(); // Reduce the available values into a single SILValue we can use to // forward. SILInstruction *IPt = CurBB->getTerminator(); Values[CurBB] = LSValue::reduce(L, &BB->getModule(), LSValues, IPt, TE); } // Finally, collect all the values for the SILArgument, materialize it using // the SSAUpdater. Updater.Initialize(L.getType()); for (auto V : Values) { Updater.AddAvailableValue(V.first, V.second); } return Updater.GetValueInMiddleOfBlock(BB); }
bool LSLocation::isMustAliasLSLocation(const LSLocation &RHS, AliasAnalysis *AA) { // If the bases are not must-alias, the locations may not alias. if (!AA->isMustAlias(Base, RHS.getBase())) return false; // If projection paths are different, then the locations cannot alias. if (!hasIdenticalProjectionPath(RHS)) return false; return true; }
bool LSLocation::isMayAliasLSLocation(const LSLocation &RHS, AliasAnalysis *AA) { // If the bases do not alias, then the locations cannot alias. if (AA->isNoAlias(Base, RHS.getBase())) return false; // If one projection path is a prefix of another, then the locations // could alias. if (hasNonEmptySymmetricPathDifference(RHS)) return false; return true; }
void LSLocation::reduce(LSLocation &Base, SILModule *M, LSLocationSet &Locs, TypeExpansionAnalysis *TE) { // First, construct the LSLocation by appending the projection path from the // accessed node to the leaf nodes. LSLocationList Nodes; ProjectionPath &BasePath = Base.getPath().getValue(); for (const auto &P : TE->getTypeExpansionProjectionPaths(Base.getType(), M, TEKind::TENode)) { Nodes.push_back(LSLocation(Base.getBase(), P.getValue(), BasePath)); } // Second, go from leaf nodes to their parents. This guarantees that at the // point the parent is processed, its children have been processed already. for (auto I = Nodes.rbegin(), E = Nodes.rend(); I != E; ++I) { LSLocationList FirstLevel; I->getFirstLevelLSLocations(FirstLevel, M); // Reached the end of the projection tree, this is a leaf node. if (FirstLevel.empty()) continue; // If this is a class reference type, we have reached end of the type tree. if (I->getType().getClassOrBoundGenericClass()) continue; // This is NOT a leaf node, check whether all its first level children are // alive. bool Alive = true; for (auto &X : FirstLevel) { Alive &= Locs.find(X) != Locs.end(); } // All first level locations are alive, create the new aggregated location. if (Alive) { for (auto &X : FirstLevel) Locs.erase(X); Locs.insert(*I); } } }
SILValue LSValue::reduce(LSLocation &Base, SILModule *M, LSLocationValueMap &Values, SILInstruction *InsertPt, TypeExpansionAnalysis *TE) { // Walk bottom up the projection tree, try to reason about how to construct // a single SILValue out of all the available values for all the memory // locations. // // First, get a list of all the leaf nodes and intermediate nodes for the // Base memory location. LSLocationList ALocs; ProjectionPath &BasePath = Base.getPath().getValue(); for (const auto &P : TE->getTypeExpansionProjectionPaths(Base.getType(), M, TEKind::TENode)) { ALocs.push_back(LSLocation(Base.getBase(), P.getValue(), BasePath)); } // Second, go from leaf nodes to their parents. This guarantees that at the // point the parent is processed, its children have been processed already. for (auto I = ALocs.rbegin(), E = ALocs.rend(); I != E; ++I) { // This is a leaf node, we have a value for it. // // Reached the end of the projection tree, this is a leaf node. LSLocationList FirstLevel; I->getFirstLevelLSLocations(FirstLevel, M); if (FirstLevel.empty()) continue; // If this is a class reference type, we have reached end of the type tree. if (I->getType().getClassOrBoundGenericClass()) continue; // This is NOT a leaf node, we need to construct a value for it. // There is only 1 children node and its value's projection path is not // empty, keep stripping it. auto Iter = FirstLevel.begin(); LSValue &FirstVal = Values[*Iter]; if (FirstLevel.size() == 1 && !FirstVal.hasEmptyProjectionPath()) { Values[*I] = FirstVal.stripLastLevelProjection(); // We have a value for the parent, remove all the values for children. removeLSLocations(Values, FirstLevel); continue; } // If there are more than 1 children and all the children nodes have // LSValues with the same base and non-empty projection path. we can get // away by not extracting value for every single field. // // Simply create a new node with all the aggregated base value, i.e. // stripping off the last level projection. bool HasIdenticalValueBase = true; SILValue FirstBase = FirstVal.getBase(); Iter = std::next(Iter); for (auto EndIter = FirstLevel.end(); Iter != EndIter; ++Iter) { LSValue &V = Values[*Iter]; HasIdenticalValueBase &= (FirstBase == V.getBase()); } if (FirstLevel.size() > 1 && HasIdenticalValueBase && !FirstVal.hasEmptyProjectionPath()) { Values[*I] = FirstVal.stripLastLevelProjection(); // We have a value for the parent, remove all the values for children. removeLSLocations(Values, FirstLevel); continue; } // In 3 cases do we need aggregation. // // 1. If there is only 1 child and we cannot strip off any projections, // that means we need to create an aggregation. // // 2. There are multiple children and they have the same base, but empty // projection paths. // // 3. Children have values from different bases, We need to create // extractions and aggregation in this case. // llvm::SmallVector<SILValue, 8> Vals; for (auto &X : FirstLevel) { Vals.push_back(Values[X].materialize(InsertPt)); } SILBuilder Builder(InsertPt); // We use an auto-generated SILLocation for now. // TODO: make the sil location more precise. NullablePtr<swift::SILInstruction> AI = Projection::createAggFromFirstLevelProjections( Builder, RegularLocation::getAutoGeneratedLocation(), I->getType(), Vals); // This is the Value for the current node. ProjectionPath P; Values[*I] = LSValue(SILValue(AI.get()), P); removeLSLocations(Values, FirstLevel); // Keep iterating until we have reach the top-most level of the projection // tree. // i.e. the memory location represented by the Base. } assert(Values.size() == 1 && "Should have a single location this point"); // Finally materialize and return the forwarding SILValue. return Values.begin()->second.materialize(InsertPt); }
void LSValue::reduceInner(LSLocation &Base, SILModule *M, LSLocationValueMap &Values, SILInstruction *InsertPt) { // If this is a class reference type, we have reached end of the type tree. if (Base.getType(M).getClassOrBoundGenericClass()) return; // This is a leaf node, we must have a value for it. LSLocationList NextLevel; Base.getNextLevelLSLocations(NextLevel, M); if (NextLevel.empty()) return; // This is not a leaf node, reduce the next level node one by one. for (auto &X : NextLevel) { LSValue::reduceInner(X, M, Values, InsertPt); } // This is NOT a leaf node, we need to construct a value for it. auto Iter = NextLevel.begin(); LSValue &FirstVal = Values[*Iter]; // There is only 1 children node and its value's projection path is not // empty, keep stripping it. if (NextLevel.size() == 1 && !FirstVal.hasEmptyProjectionPath()) { Values[Base] = FirstVal.stripLastLevelProjection(); // We have a value for the parent, remove all the values for children. removeLSLocations(Values, NextLevel); return; } bool HasIdenticalBase = true; SILValue FirstBase = FirstVal.getBase(); for (auto &X : NextLevel) { HasIdenticalBase &= (FirstBase == Values[X].getBase()); } // This is NOT a leaf node and it has multiple children, but they have the // same value base. if (NextLevel.size() > 1 && HasIdenticalBase) { if (!FirstVal.hasEmptyProjectionPath()) { Values[Base] = FirstVal.stripLastLevelProjection(); // We have a value for the parent, remove all the values for children. removeLSLocations(Values, NextLevel); return; } } // In 3 cases do we need aggregation. // // 1. If there is only 1 child and we cannot strip off any projections, // that means we need to create an aggregation. // // 2. There are multiple children and they have the same base, but empty // projection paths. // // 3. Children have values from different bases, We need to create // extractions and aggregation in this case. // llvm::SmallVector<SILValue, 8> Vals; for (auto &X : NextLevel) { Vals.push_back(Values[X].materialize(InsertPt)); } SILBuilder Builder(InsertPt); Builder.setCurrentDebugScope(InsertPt->getFunction()->getDebugScope()); // We use an auto-generated SILLocation for now. NullablePtr<swift::SILInstruction> AI = Projection::createAggFromFirstLevelProjections( Builder, RegularLocation::getAutoGeneratedLocation(), Base.getType(M).getObjectType(), Vals); // This is the Value for the current base. ProjectionPath P(Base.getType(M)); Values[Base] = LSValue(SILValue(AI.get()), P); removeLSLocations(Values, NextLevel); }
void DSEContext::processWrite(SILInstruction *I, SILValue Val, SILValue Mem, DSEKind Kind) { auto *S = getBlockState(I); // Construct a LSLocation to represent the memory read by this instruction. // NOTE: The base will point to the actual object this inst is accessing, // not this particular field. // // e.g. %1 = alloc_stack $S // %2 = struct_element_addr %1, #a // store %3 to %2 : $*Int // // Base will point to %1, but not %2. Projection path will indicate which // field is accessed. // // This will make comparison between locations easier. This eases the // implementation of intersection operator in the data flow. LSLocation L; if (BaseToLocIndex.find(Mem) != BaseToLocIndex.end()) { L = BaseToLocIndex[Mem]; } else { SILValue UO = getUnderlyingObject(Mem); L = LSLocation(UO, ProjectionPath::getProjectionPath(UO, Mem)); } // If we can't figure out the Base or Projection Path for the store // instruction, simply ignore it. if (!L.isValid()) return; // Expand the given Mem into individual fields and process them as separate // writes. bool Dead = true; LSLocationList Locs; LSLocation::expand(L, Mod, Locs, TE); llvm::SmallBitVector V(Locs.size()); // Are we computing max store set. if (isComputeMaxStoreSet(Kind)) { for (auto &E : Locs) { // Update the max store set for the basic block. processWriteForMaxStoreSet(S, getLocationBit(E)); } return; } // Are we computing genset and killset. if (isBuildingGenKillSet(Kind)) { for (auto &E : Locs) { // Only building the gen and kill sets here. processWriteForGenKillSet(S, getLocationBit(E)); } // Data flow has not stabilized, do not perform the DSE just yet. return; } // We are doing the actual DSE. assert(isPerformingDSE(Kind) && "Invalid computation kind"); unsigned idx = 0; for (auto &E : Locs) { // This is the last iteration, compute BBWriteSetOut and perform the dead // store elimination. if (processWriteForDSE(S, getLocationBit(E))) V.set(idx); Dead &= V.test(idx); ++idx; } // Fully dead store - stores to all the components are dead, therefore this // instruction is dead. if (Dead) { DEBUG(llvm::dbgs() << "Instruction Dead: " << *I << "\n"); S->DeadStores.insert(I); ++NumDeadStores; return; } // Partial dead store - stores to some locations are dead, but not all. This // is a partially dead store. Also at this point we know what locations are // dead. llvm::DenseSet<LSLocation> Alives; if (V.any()) { // Take out locations that are dead. for (unsigned i = 0; i < V.size(); ++i) { if (V.test(i)) continue; // This location is alive. Alives.insert(Locs[i]); } // Try to create as few aggregated stores as possible out of the locations. LSLocation::reduce(L, Mod, Alives); // Oops, we have too many smaller stores generated, bail out. if (Alives.size() > MaxPartialStoreCount) return; // At this point, we are performing a partial dead store elimination. // // Locations here have a projection path from their Base, but this // particular instruction may not be accessing the base, so we need to // *rebase* the locations w.r.t. to the current instruction. SILValue B = Locs[0].getBase(); Optional<ProjectionPath> BP = ProjectionPath::getProjectionPath(B, Mem); // Strip off the projection path from base to the accessed field. for (auto &X : Alives) { X.removePathPrefix(BP); } // We merely setup the remaining live stores, but do not materialize in IR // yet, These stores will be materialized before the algorithm exits. for (auto &X : Alives) { SILValue Value = X.getPath()->createExtract(Val, I, true); SILValue Addr = X.getPath()->createExtract(Mem, I, false); S->LiveAddr.insert(Addr); S->LiveStores[Addr] = Value; } // Lastly, mark the old store as dead. DEBUG(llvm::dbgs() << "Instruction Partially Dead: " << *I << "\n"); S->DeadStores.insert(I); ++NumPartialDeadStores; } }