| /** |
| * Copyright (c) 2019 Metempsy Technology Consulting |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer; |
| * redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution; |
| * neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * Authors: Ivan Pizarro |
| */ |
| |
| #include "mem/cache/prefetch/pif.hh" |
| |
| #include <utility> |
| |
| #include "debug/HWPrefetch.hh" |
| #include "mem/cache/prefetch/associative_set_impl.hh" |
| #include "params/PIFPrefetcher.hh" |
| |
| PIFPrefetcher::PIFPrefetcher(const PIFPrefetcherParams *p) |
| : QueuedPrefetcher(p), |
| precSize(p->prec_spatial_region_bits), |
| succSize(p->succ_spatial_region_bits), |
| maxCompactorEntries(p->compactor_entries), |
| maxStreamAddressBufferEntries(p->stream_address_buffer_entries), |
| historyBuffer(p->history_buffer_size), |
| historyBufferTail(0), |
| index(p->index_assoc, p->index_entries, p->index_indexing_policy, |
| p->index_replacement_policy), |
| streamAddressBuffer(), listenersPC() |
| { |
| } |
| |
| PIFPrefetcher::CompactorEntry::CompactorEntry(Addr addr, |
| unsigned int prec_size, unsigned int succ_size) |
| { |
| trigger = addr; |
| prec.resize(prec_size, false); |
| succ.resize(succ_size, false); |
| } |
| |
| Addr |
| PIFPrefetcher::CompactorEntry::distanceFromTrigger(Addr target, |
| unsigned int log_blk_size) const |
| { |
| const Addr target_blk = target >> log_blk_size; |
| const Addr trigger_blk = trigger >> log_blk_size; |
| |
| return target_blk > trigger_blk ? |
| target_blk - trigger_blk : trigger_blk - target_blk; |
| } |
| |
| bool |
| PIFPrefetcher::CompactorEntry::inSameSpatialRegion(Addr pc, |
| unsigned int log_blk_size, bool update) |
| { |
| Addr blk_distance = distanceFromTrigger(pc, log_blk_size); |
| |
| bool hit = (pc > trigger) ? |
| (succ.size() >= blk_distance) : (prec.size() >= blk_distance); |
| if (hit && update) { |
| if (pc > trigger) { |
| succ[blk_distance - 1] = true; |
| } else if (pc < trigger) { |
| prec[blk_distance - 1] = true; |
| } |
| } |
| return hit; |
| } |
| |
| bool |
| PIFPrefetcher::CompactorEntry::hasAddress(Addr target, |
| unsigned int log_blk_size) const |
| { |
| Addr blk_distance = distanceFromTrigger(target, log_blk_size); |
| bool hit = false; |
| if (target > trigger) { |
| hit = blk_distance <= succ.size() && succ[blk_distance - 1]; |
| } else if (target < trigger) { |
| hit = blk_distance <= prec.size() && succ[blk_distance - 1]; |
| } else { |
| hit = true; |
| } |
| return hit; |
| } |
| |
| void |
| PIFPrefetcher::CompactorEntry::getPredictedAddresses(unsigned int log_blk_size, |
| std::vector<AddrPriority> &addresses) const |
| { |
| // Calculate the addresses of the instruction blocks that are encoded |
| // by the bit vector and issue prefetch requests for these addresses. |
| // Predictions are made by traversing the bit vector from left to right |
| // as this typically predicts the accesses in the order they will be |
| // issued in the core. |
| const Addr trigger_blk = trigger >> log_blk_size; |
| for (int i = prec.size()-1; i >= 0; i--) { |
| // Address from the preceding blocks to issue a prefetch |
| if (prec[i]) { |
| const Addr prec_addr = (trigger_blk - (i+1)) << log_blk_size; |
| addresses.push_back(AddrPriority(prec_addr, 0)); |
| } |
| } |
| for (int i = 0; i < succ.size(); i++) { |
| // Address from the succeding blocks to issue a prefetch |
| if (succ[i]) { |
| const Addr succ_addr = (trigger_blk + (i+1)) << log_blk_size; |
| addresses.push_back(AddrPriority(succ_addr, 0)); |
| } |
| } |
| } |
| |
| void |
| PIFPrefetcher::notifyRetiredInst(const Addr pc) |
| { |
| // First access to the prefetcher |
| if (temporalCompactor.size() == 0) { |
| spatialCompactor = CompactorEntry(pc, precSize, succSize); |
| } else { |
| // If the PC of the instruction retired is in the same spatial region |
| // than the last trigger address, update the bit vectors based on the |
| // distance between them |
| if (spatialCompactor.inSameSpatialRegion(pc, lBlkSize, true)) { |
| // If the PC of the instruction retired is outside the latest spatial |
| // region, check if it matches in any of the regions in the temporal |
| // compactor and update it to the MRU position |
| } else { |
| bool is_in_temporal_compactor = false; |
| |
| // Check if the PC is in the temporal compactor |
| for (auto it = temporalCompactor.begin(); |
| it != temporalCompactor.end(); it++) |
| { |
| if (it->inSameSpatialRegion(pc, lBlkSize, false)) { |
| spatialCompactor = (*it); |
| temporalCompactor.erase(it); |
| is_in_temporal_compactor = true; |
| break; |
| } |
| } |
| |
| if (temporalCompactor.size() == maxCompactorEntries) { |
| temporalCompactor.pop_front(); // Discard the LRU entry |
| } |
| |
| temporalCompactor.push_back(spatialCompactor); |
| |
| // If the compactor entry is neither the spatial or can't be |
| // found in the temporal compactor, reset the spatial compactor |
| // updating the trigger address and resetting the vector bits |
| if (!is_in_temporal_compactor) { |
| // Insert the spatial entry into the history buffer and update |
| // the 'index' table to point to the new entry |
| historyBuffer[historyBufferTail] = spatialCompactor; |
| |
| IndexEntry *idx_entry = |
| index.findEntry(spatialCompactor.trigger, false); |
| if (idx_entry != nullptr) { |
| index.accessEntry(idx_entry); |
| } else { |
| idx_entry = index.findVictim(spatialCompactor.trigger); |
| assert(idx_entry != nullptr); |
| index.insertEntry(spatialCompactor.trigger, false, |
| idx_entry); |
| } |
| idx_entry->historyIndex = historyBufferTail; |
| |
| historyBufferTail++; |
| if (historyBufferTail == historyBuffer.size()) { |
| historyBufferTail = 0; |
| } |
| |
| // Reset the spatial compactor fields with the new address |
| spatialCompactor = CompactorEntry(pc, precSize, succSize); |
| } |
| } |
| } |
| } |
| |
| void |
| PIFPrefetcher::calculatePrefetch(const PrefetchInfo &pfi, |
| std::vector<AddrPriority> &addresses) |
| { |
| const Addr addr = pfi.getAddr(); |
| |
| // First check if the access has been prefetched, this is done by |
| // comparing the access against the active Stream Address Buffers |
| for (auto &sabEntry : streamAddressBuffer) { |
| if (sabEntry->hasAddress(addr, lBlkSize)) { |
| // Advance to the next entry (first check if we have reached the |
| // end of the history buffer) |
| if (sabEntry == &(historyBuffer[historyBuffer.size() - 1])) { |
| sabEntry = &(historyBuffer[0]); |
| } else { |
| sabEntry++; |
| } |
| sabEntry->getPredictedAddresses(lBlkSize, addresses); |
| // We are done |
| return; |
| } |
| } |
| |
| // Check if a valid entry in the 'index' table is found and allocate a new |
| // active prediction stream |
| IndexEntry *idx_entry = index.findEntry(addr, /* unused */ false); |
| |
| if (idx_entry != nullptr) { |
| index.accessEntry(idx_entry); |
| // Trigger address from the 'index' table and index to the history |
| // buffer |
| const unsigned int hb_entry = idx_entry->historyIndex; |
| CompactorEntry *entry = &historyBuffer[hb_entry]; |
| |
| // Track the block in the Stream Address Buffer |
| if (streamAddressBuffer.size() == maxStreamAddressBufferEntries) { |
| streamAddressBuffer.pop_front(); |
| } |
| streamAddressBuffer.push_back(entry); |
| |
| entry->getPredictedAddresses(lBlkSize, addresses); |
| } |
| } |
| |
| void |
| PIFPrefetcher::PrefetchListenerPC::notify(const Addr& pc) |
| { |
| parent.notifyRetiredInst(pc); |
| } |
| |
| void |
| PIFPrefetcher::addEventProbeRetiredInsts(SimObject *obj, const char *name) |
| { |
| ProbeManager *pm(obj->getProbeManager()); |
| listenersPC.push_back(new PrefetchListenerPC(*this, pm, name)); |
| } |
| |
| PIFPrefetcher* |
| PIFPrefetcherParams::create() |
| { |
| return new PIFPrefetcher(this); |
| } |