/* * Copyright 2019 Texas A&M University * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. 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. * * 3. Neither the name of the copyright holder 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 * HOLDER 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. * * Author: Daniel A. Jiménez * Adapted to gem5 by: Javier Bueno Hedo * */ /* * Multiperspective Perceptron Predictor (by Daniel A. Jiménez) */ #ifndef __CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_HH__ #define __CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_HH__ #include #include #include "cpu/pred/bpred_unit.hh" #include "params/MultiperspectivePerceptron.hh" class MultiperspectivePerceptron : public BPredUnit { protected: /** * Branch information data */ class MPPBranchInfo { /** pc of the branch */ const unsigned int pc; /** pc of the branch, shifted 2 bits to the right */ const unsigned short int pc2; /** pc of the branch, hashed */ const unsigned short int hpc; /** Whether this is a conditional branch */ const bool condBranch; /** * PC Hash functions */ static inline unsigned int hash1(unsigned int a) { a = (a ^ 0xdeadbeef) + (a<<4); a = a ^ (a>>10); a = a + (a<<7); a = a ^ (a>>13); return a; } static inline unsigned int hash2(unsigned int key) { int c2 = 0x27d4eb2d; // a prime or an odd constant key = (key ^ 61) ^ (key >> 16); key = key + (key << 3); key = key ^ (key >> 4); key = key * c2; key = key ^ (key >> 15); return key; } static inline unsigned int hash(unsigned int key, unsigned int i) { return hash2(key) * i + hash1(key); } static inline unsigned int hashPC(unsigned int pc, int pcshift) { if (pcshift < 0) { return hash(pc, -pcshift); } else if (pcshift < 11) { unsigned int x = pc; x ^= (pc >> pcshift); return x; } else { return pc >> (pcshift-11); } } public: /** Whether this branch has been filtered by the prefetcher */ bool filtered; /** Result of the prediction (true is taken) */ bool prediction; /** Score of the perceptron */ int yout; MPPBranchInfo(Addr _pc, int pcshift, bool cb) : pc((unsigned int)_pc), pc2(pc >> 2), hpc(hashPC(pc, pcshift)), condBranch(cb), filtered(false), prediction(false), yout(0) { } unsigned int getPC() const { return pc; } unsigned short int getPC2() const { return pc2; } unsigned short int getHPC() const { return hpc; } unsigned int getHashFilter(bool last_ghist_bit) const { return last_ghist_bit ^ hpc; } bool isUnconditional() const { return !condBranch; } }; /** * Entry of the branch filter */ struct FilterEntry { /** Has this branch been taken at least once? */ bool seenTaken; /** Has this branch been not taken at least once? */ bool seenUntaken; FilterEntry() : seenTaken(false), seenUntaken(false) {} /** Whether this branch has always been observed as not taken */ bool alwaysNotTakenSoFar() const { return seenUntaken & !seenTaken; } /** Whether this branch has always been observed as taken */ bool alwaysTakenSoFar() const { return seenTaken & !seenUntaken; } /** Whether this branch has been observed before */ bool neverSeen() const { return !seenTaken && !seenUntaken; } }; /** * Local history entries, each enty contains the history of directions * taken by a given branch. */ class LocalHistories { /** The array of histories */ std::vector localHistories; /** Size in bits of each history entry */ const int localHistoryLength; /** Index function given the pc of the branch */ unsigned int index(Addr pc) const { return (pc >> 2) % localHistories.size(); } public: LocalHistories(int nlocal_histories, int histo_len) : localHistories(nlocal_histories), localHistoryLength(histo_len) {} /** Obtains the local history entry of a given branch */ unsigned int operator[](Addr pc) const { return localHistories[index(pc)]; } /** Adds a history bit to the local history entry of a given branch */ void update(Addr pc, bool value) { assert(localHistories.size() > 0); unsigned int &pos = localHistories[index(pc)]; pos <<= 1; pos |= value; pos &= ((1<> &blurrypath_bits, int path_length, int ghist_length, int block_size, const std::vector>> &acyclic_bits, const std::vector &modhist_indices, const std::vector &modhist_lengths, const std::vector &modpath_indices, const std::vector &modpath_lengths, const std::vector &table_sizes, int n_sign_bits); std::vector filterTable; std::vector> acyclic_histories; std::vector> acyclic2_histories; void updateAcyclic(bool hashed_taken, unsigned int hpc) { for (int i = 0; i < acyclic_histories.size(); i += 1) { if (acyclic_histories[i].size() > 0) { acyclic_histories[i][hpc%(i+2)] = hashed_taken; acyclic2_histories[i][hpc%(i+2)] = hpc; } } } std::vector> blurrypath_histories; std::vector ghist_words; std::vector> modpath_histories; std::vector> mod_histories; std::vector path_history; std::vector imli_counter; LocalHistories localHistories; std::vector recency_stack; void insertRecency(unsigned int pc, int assoc) { int i = 0; for (i = 0; i < assoc; i += 1) { if (recency_stack[i] == pc) { break; } } if (i == assoc) { i = assoc-1; recency_stack[i] = pc; } int j; unsigned int b = recency_stack[i]; for (j = i; j >= 1; j -= 1) { recency_stack[j] = recency_stack[j-1]; } recency_stack[0] = b; } bool last_ghist_bit; int occupancy; std::vector mpreds; std::vector> tables; std::vector>> sign_bits; }; std::vector threadData; /** Predictor tables */ std::vector specs; std::vector table_sizes; /** runtime values and data used to count the size in bits */ bool doing_local; bool doing_recency; int assoc; int ghist_length; int modghist_length; int path_length; int thresholdCounter; int theta; int extrabits; std::vector imli_counter_bits; std::vector modhist_indices; std::vector modhist_lengths; std::vector modpath_indices; std::vector modpath_lengths; std::vector> blurrypath_bits; std::vector>> acyclic_bits; /** Auxiliary function for MODHIST and GHISTMODPATH features */ void insertModhistSpec(int p1, int p2) { int j = insert(modhist_indices, p1); if (modhist_lengths.size() < (j + 1)) { modhist_lengths.resize(j + 1); } if (modhist_lengths[j] < p2 + 1) { modhist_lengths[j] = p2 + 1; } if (p2 >= modghist_length) { modghist_length = p2 + 1; } } /** Auxiliary function for MODPATH and GHISTMODPATH features */ void insertModpathSpec(int p1, int p2) { int j = insert(modpath_indices, p1); if (modpath_lengths.size() < (j + 1)) { modpath_lengths.resize(j + 1); } if (modpath_lengths[j] < p2 + 1) { modpath_lengths[j] = p2 + 1; } if (p2 >= path_length) { path_length = p2 + 1; } } /** Auxiliary function used by insertModhistSpec and insertModpathSpec*/ int insert(std::vector &v, int x) { for (int i = 0; i < v.size(); i += 1) { if (v[i] == x) { return i; } } v.push_back(x); return v.size()-1; } /** * Computes the size in bits of the structures needed to keep track * of the history and the predictor tables and assigns the sizes of * those tables that did not had their size specified. * @param num_filter_entries number of entries of the filter * @param nlocal_histories number of local history entries * @param local_history_length size of each local history entry * @param ignore_path_size ignore the path length storage */ void computeBits(int num_filter_entries, int nlocal_histories, int local_history_length, bool ignore_path_size); /** * Creates the tables of the predictor */ virtual void createSpecs() = 0; /** * Get the position index of a predictor table * @param tid Thread ID of the branch * @param bi branch informaiton data * @param spec predictor table * @param index integer index of the predictor table * @result index to access the predictor table */ unsigned int getIndex(ThreadID tid, const MPPBranchInfo &bi, const HistorySpec &spec, int index) const; /** * Finds the best subset of features to use in case of a low-confidence * branch, returns the result as an ordered vector of the indices to the * predictor tables * @param tid Thread ID of the branch * @param vector to write the ordered list of indices of the best tables */ void findBest(ThreadID tid, std::vector &best_preds) const; /** * Computes the output of the predictor for a given branch and the * resulting best value in case the prediction has low confidence * @param tid Thread ID of the branch * @param bi branch informaiton data * @return resulting sum for low-confidence branch */ int computeOutput(ThreadID tid, MPPBranchInfo &bi); /** * Trains the branch predictor with the given branch and direction * @param tid Thread ID of the branch * @param bi branch informaiton data * @param taken whether the branch was taken */ void train(ThreadID tid, MPPBranchInfo &bi, bool taken); /** * Auxiliary function to increase a table counter depending on the * direction of the branch * @param taken whether the branch was taken * @param sign current sign of the table * @param c current value of the table * @param max_weight maximum value of the counter */ void satIncDec(bool taken, bool &sign, int &c, int max_weight) const; /** Add a table spec to the prefetcher */ void addSpec(HistorySpec *spec) { specs.push_back(spec); } /** Available features */ class GHIST : public HistorySpec { public: GHIST(int p1, int p2, double coeff, int size, int width, MultiperspectivePerceptron &mpp) : HistorySpec(p1, p2, 0, coeff, size, width, mpp) {} unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const override { return hash(mpp.threadData[tid]->ghist_words, mpp.blockSize, p1, p2); } static unsigned int hash(const std::vector &ghist_words, int block_size, int start_pos, int end_pos) { int a = start_pos; int b = end_pos; unsigned int x = 0; // am is the next multiple of block_size after a int am = (((a/block_size)*block_size)+block_size); // bm is the previous multiple of block_size before b int bm = (b/block_size)*block_size; // the 0th bit of ghist_words[a/block_size] is the most recent bit. // so the number of bits between a and am is the number to shift // right? // start out x as remainder bits from the beginning: // x = [ . . . . . b b b b b ] x += ghist_words[a / block_size] >> (a-am); // add in bits from the middle for (int i=am; i> &acyclic_histories = mpp.threadData[tid]->acyclic_histories; std::vector> &acyclic2_histories = mpp.threadData[tid]->acyclic2_histories; unsigned int x = 0; if (style == -1) { unsigned int k = 0; for (int i = 0; i < a + 2; i += 1) { x ^= acyclic_histories[a][i] << k; k += 1; k %= mpp.blockSize; } } else { for (int i = 0; i < a + 2; i += 1) { x <<= shift; x += acyclic2_histories[a][i]; } } return x; } void setBitRequirements() const override { if (mpp.acyclic_bits.size() < (p1 + 1)) { mpp.acyclic_bits.resize(p1 + 1); } if (mpp.acyclic_bits[p1].size() < (p1 + 2)) { mpp.acyclic_bits[p1].resize(p1 + 2, std::vector(2)); } for (int j = 0; j < p1 + 2; j += 1) { mpp.acyclic_bits[p1][j][!p3] = true; } } }; class MODHIST : public HistorySpec { public: MODHIST(int p1, int p2, double coeff, int size, int width, MultiperspectivePerceptron &mpp) : HistorySpec(p1, p2, 0, coeff, size, width, mpp) {} unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const override { int a = p1; int b = p2; std::vector> &mod_histories = mpp.threadData[tid]->mod_histories; unsigned int x = 0, k = 0; for (int i = 0; i < b; i += 1) { x ^= mod_histories[a][i] << k; k += 1; k %= mpp.blockSize; } return x; } void setBitRequirements() const override { mpp.insertModhistSpec(p1, p2); } }; class BIAS : public HistorySpec { public: BIAS(double coeff, int size, int width, MultiperspectivePerceptron &mpp) : HistorySpec(0, 0, 0, coeff, size, width, mpp) {} unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const override { return 0; } }; class RECENCY : public HistorySpec { public: RECENCY(int p1, int p2, int p3, double coeff, int size, int width, MultiperspectivePerceptron &mpp) : HistorySpec(p1, p2, p3, coeff, size, width, mpp) {} unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const override { int depth = p1; int shift = p2; int style = p3; std::vector &recency_stack = mpp.threadData[tid]->recency_stack; if (style == -1) { unsigned int x = 0; for (int i = 0; i < depth; i += 1) { x <<= shift; x += recency_stack[i]; } return x; } else { unsigned int x = 0, k = 0; for (int i = 0; i < depth; i += 1) { x ^= (!!(recency_stack[i] & (1 << shift))) << k; k += 1; k %= mpp.blockSize; } return x; } } void setBitRequirements() const override { if (mpp.assoc < p1) { mpp.assoc = p1; } mpp.doing_recency = true; } }; class IMLI : public HistorySpec { public: IMLI(int p1, double coeff, int size, int width, MultiperspectivePerceptron &mpp) : HistorySpec(p1, 0, 0, coeff, size, width, mpp) {} unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const override { assert(p1 >= 1); assert(p1 <= 4); return mpp.threadData[tid]->imli_counter[p1-1]; } void setBitRequirements() const override { mpp.imli_counter_bits[p1 - 1] = 32; } }; class PATH : public HistorySpec { public: PATH(int p1, int p2, int p3, double coeff, int size, int width, MultiperspectivePerceptron &mpp) : HistorySpec(p1, p2, p3, coeff, size, width, mpp) {} unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const override { int depth = p1; int shift = p2; int style = p3; std::vector &path_history = mpp.threadData[tid]->path_history; if (style == -1) { unsigned int x = 0; for (int i = 0; i < depth; i += 1) { x <<= shift; x += path_history[i]; } return x; } else { unsigned int x = 0; int bm = (depth / mpp.blockSize) * mpp.blockSize; for (int i = 0; i < bm; i += mpp.blockSize) { for (int j = 0; j < mpp.blockSize; j += 1) { x ^= (!!(path_history[i + j] & (1 << shift))) << j; } } int k = 0; for (int i = bm; i < depth; i += 1) { x ^= (!!(path_history[i] & (1 << shift))) << k++; } return x; } } void setBitRequirements() const override { if (mpp.path_length <= p1) { mpp.path_length = p1 + 1; } } }; class LOCAL : public HistorySpec { public: LOCAL(int p1, double coeff, int size, int width, MultiperspectivePerceptron &mpp) : HistorySpec(p1, 0, 0, coeff, size, width, mpp) {} unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const override { unsigned int x = mpp.threadData[tid]->localHistories[pc]; if (p1 != -1) { x &= ((1 << p1) - 1); } return x; } void setBitRequirements() const override { mpp.doing_local = true; } }; class MODPATH : public HistorySpec { public: MODPATH(int p1, int p2, int p3, double coeff, int size, int width, MultiperspectivePerceptron &mpp) : HistorySpec(p1, p2, p3, coeff, size, width, mpp) {} unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const override { int a = p1; int depth = p2; int shift = p3; unsigned int x = 0; for (int i=0; imodpath_histories[a][i]; } return x; } void setBitRequirements() const override { mpp.insertModpathSpec(p1, p2); } }; class GHISTPATH : public HistorySpec { public: GHISTPATH(int p1, int p2, int p3, double coeff, int size, int width, MultiperspectivePerceptron &mpp) : HistorySpec(p1, p2, p3, coeff, size, width, mpp) {} unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const override { int depth = p1; int shift = p2; int style = p3; std::vector &ghist_words = mpp.threadData[tid]->ghist_words; std::vector &path_history = mpp.threadData[tid]->path_history; if (style == -1) { unsigned int x = 0; int bm = (depth / mpp.blockSize) * mpp.blockSize; unsigned int w; for (int i = 0; i < bm; i += mpp.blockSize) { w = ghist_words[i / mpp.blockSize]; for (int j = 0; j < mpp.blockSize; j += 1) { x <<= shift; x += (path_history[i + j] << 1) | (w & 1); w >>= 1; } } w = ghist_words[bm / mpp.blockSize]; for (int i = bm; i < depth; i += 1) { x <<= shift; x += (path_history[i] << 1) | (w & 1); w >>= 1; } return x; } else { unsigned int x = 0; int bm = (depth / mpp.blockSize) * mpp.blockSize; unsigned int w = 0; for (int i = 0; i < bm; i += mpp.blockSize) { w = ghist_words[i / mpp.blockSize]; for (int j = 0; j < mpp.blockSize; j += 1) { x ^= (!!(path_history[i + j] & (1 << shift))) << j; x ^= (w & 1) << j; w >>= 1; } } w = ghist_words[bm/mpp.blockSize]; int k = 0; for (int i = bm; i < depth; i += 1) { x ^= (!!(path_history[i] & (1 << shift))) << k; x ^= (w & 1) << k; w >>= 1; k += 1; } return x; } } void setBitRequirements() const override { if (mpp.ghist_length <= p1) { mpp.ghist_length = p1 + 1; } if (mpp.path_length <= p1) { mpp.path_length = p1 + 1; } } }; class GHISTMODPATH : public HistorySpec { public: GHISTMODPATH(int p1, int p2, int p3, double coeff, int size, int width, MultiperspectivePerceptron &mpp) : HistorySpec(p1, p2, p3, coeff, size, width, mpp) {} unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const override { int a = p1; int depth = p2; int shift = p3; std::vector> &modpath_histories = mpp.threadData[tid]->modpath_histories; std::vector> &mod_histories = mpp.threadData[tid]->mod_histories; unsigned int x = 0; for (int i = 0; i < depth; i += 1) { x <<= shift; x += (modpath_histories[a][i] << 1) | mod_histories[a][i]; } return x; } void setBitRequirements() const override { mpp.insertModhistSpec(p1, p2); mpp.insertModpathSpec(p1, p2); } }; class BLURRYPATH : public HistorySpec { public: BLURRYPATH(int p1, int p2, int p3, double coeff, int size, int width, MultiperspectivePerceptron &mpp) : HistorySpec(p1, p2, p3, coeff, size, width, mpp) {} unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const override { int scale = p1; int depth = p2; int shiftdelta = p3; if (shiftdelta == -1) shiftdelta = 0; int sdint = shiftdelta >> 2; int sdfrac = shiftdelta & 3; unsigned int x = 0; int shift = 0; int count = 0; for (int i = 0; i < depth; i += 1) { x += mpp.threadData[tid]->blurrypath_histories[scale][i] >> shift; count += 1; if (count == sdfrac) { shift += sdint; count = 0; } } return x; } void setBitRequirements() const override { if (mpp.blurrypath_bits.size() < (p1 + 1)) { mpp.blurrypath_bits.resize(p1 + 1); } if (mpp.blurrypath_bits[p1].size() < p2) { mpp.blurrypath_bits[p1].resize(p2); } for (int j = 0; j < p2; j += 1) { mpp.blurrypath_bits[p1][j] = 32 - p1; } } }; class RECENCYPOS : public HistorySpec { public: RECENCYPOS(int p1, double coeff, int size, int width, MultiperspectivePerceptron &mpp) : HistorySpec(p1, 0, 0, coeff, size, width, mpp) {} unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const override { return hash(mpp.threadData[tid]->recency_stack, mpp.table_sizes, pc2, p1, t); } static unsigned int hash( const std::vector &recency_stack, const std::vector &table_sizes, unsigned short int pc, int l, int t) { // search for the PC for (int i = 0; i < l; i += 1) { if (recency_stack[i] == pc) { return i * table_sizes[t] / l; } } // return last index in table on a miss return table_sizes[t] - 1; } void setBitRequirements() const override { if (mpp.assoc < p1) { mpp.assoc = p1; } mpp.doing_recency = true; } }; class SGHISTPATH : public HistorySpec { public: SGHISTPATH(int p1, int p2, int p3, double coeff, int size, int width, MultiperspectivePerceptron &mpp) : HistorySpec(p1, p2, p3, coeff, size, width, mpp) {} unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const override { int a = p1; int b = p2; int shift = p3; std::vector &ghist_words = mpp.threadData[tid]->ghist_words; std::vector &path_history = mpp.threadData[tid]->path_history; unsigned int x = 0; int bm = (b / mpp.blockSize) * mpp.blockSize; unsigned int w; for (int i = a; i < bm; i += mpp.blockSize) { w = ghist_words[i / mpp.blockSize]; for (int j = 0; j < mpp.blockSize; j += 1) { x <<= shift; x += (path_history[i+j] << 1) | (w & 1); w >>= 1; } } w = ghist_words[bm / mpp.blockSize]; for (int i = bm; i < b; i += 1) { x <<= shift; x += (path_history[i] << 1) | (w & 1); w >>= 1; } return x; } }; public: MultiperspectivePerceptron(const MultiperspectivePerceptronParams *params); /** * Sets the starting number of storage bits to compute the number of * table entries * @param bits number of bits used */ void setExtraBits(int bits); void init() override; void uncondBranch(ThreadID tid, Addr pc, void * &bp_history) override; void squash(ThreadID tid, void *bp_history) override; bool lookup(ThreadID tid, Addr instPC, void * &bp_history) override; void update(ThreadID tid, Addr instPC, bool taken, void *bp_history, bool squashed, const StaticInstPtr & inst, Addr corrTarget = MaxAddr) override; void btbUpdate(ThreadID tid, Addr branch_addr, void* &bp_history) override; }; #endif//__CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_HH__