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 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; } }