From b68735f9fad5879e606849e78e1a4ad3f77f1463 Mon Sep 17 00:00:00 2001 From: Javier Bueno Date: Thu, 14 Feb 2019 00:23:25 +0100 Subject: [PATCH] cpu: Added the Multiperspective Perceptron Predictor (8KB and 64KB) MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Described by the following article: Jiménez, D. "Multiperspective perceptron predictor." Championship Branch Prediction (CBP-5) (2016). Change-Id: Iaa68ead7696e0b6ba05b4417d0322e8053e10d30 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/15495 Maintainer: Andreas Sandberg Reviewed-by: Ilias Vougioukas Tested-by: kokoro --- src/cpu/pred/BranchPredictor.py | 82 ++ src/cpu/pred/SConscript | 3 + src/cpu/pred/multiperspective_perceptron.cc | 808 +++++++++++++ src/cpu/pred/multiperspective_perceptron.hh | 1027 +++++++++++++++++ .../pred/multiperspective_perceptron_64KB.cc | 93 ++ .../pred/multiperspective_perceptron_64KB.hh | 53 + .../pred/multiperspective_perceptron_8KB.cc | 72 ++ .../pred/multiperspective_perceptron_8KB.hh | 53 + 8 files changed, 2191 insertions(+) create mode 100644 src/cpu/pred/multiperspective_perceptron.cc create mode 100644 src/cpu/pred/multiperspective_perceptron.hh create mode 100644 src/cpu/pred/multiperspective_perceptron_64KB.cc create mode 100644 src/cpu/pred/multiperspective_perceptron_64KB.hh create mode 100644 src/cpu/pred/multiperspective_perceptron_8KB.cc create mode 100644 src/cpu/pred/multiperspective_perceptron_8KB.hh diff --git a/src/cpu/pred/BranchPredictor.py b/src/cpu/pred/BranchPredictor.py index 9c59574ff..967489a16 100644 --- a/src/cpu/pred/BranchPredictor.py +++ b/src/cpu/pred/BranchPredictor.py @@ -475,3 +475,85 @@ class TAGE_SC_L_8KB(TAGE_SC_L): tage = TAGE_SC_L_TAGE_8KB() loop_predictor = TAGE_SC_L_8KB_LoopPredictor() statistical_corrector = TAGE_SC_L_8KB_StatisticalCorrector() + +class MultiperspectivePerceptron(BranchPredictor): + type = 'MultiperspectivePerceptron' + cxx_class = 'MultiperspectivePerceptron' + cxx_header = 'cpu/pred/multiperspective_perceptron.hh' + abstract = True + + num_filter_entries = Param.Int("Number of filter entries") + num_local_histories = Param.Int("Number of local history entries") + local_history_length = Param.Int(11, + "Length in bits of each history entry") + + block_size = Param.Int(21, + "number of ghist bits in a 'block'; this is the width of an initial " + "hash of ghist") + pcshift = Param.Int(-10, "Shift for hashing PC") + threshold = Param.Int(1, "Threshold for deciding low/high confidence") + bias0 = Param.Int(-5, + "Bias perceptron output this much on all-bits-zero local history") + bias1 = Param.Int(5, + "Bias perceptron output this much on all-bits-one local history") + biasmostly0 = Param.Int(-1, + "Bias perceptron output this much on almost-all-bits-zero local " + "history") + biasmostly1 = Param.Int(1, + "Bias perceptron output this much on almost-all-bits-one local " + "history") + nbest = Param.Int(20, + "Use this many of the top performing tables on a low-confidence " + "branch") + tunebits = Param.Int(24, "Number of bits in misprediction counters") + hshift = Param.Int(-6, + "How much to shift initial feauture hash before XORing with PC bits") + imli_mask1 = Param.UInt64( + "Which tables should have their indices hashed with the first IMLI " + "counter") + imli_mask4 = Param.UInt64( + "Which tables should have their indices hashed with the fourth IMLI " + "counter") + recencypos_mask = Param.UInt64( + "Which tables should have their indices hashed with the recency " + "position") + fudge = Param.Float(0.245, "Fudge factor to multiply by perceptron output") + n_sign_bits = Param.Int(2, "Number of sign bits per magnitude") + pcbit = Param.Int(2, "Bit from the PC to use for hashing global history") + decay = Param.Int(0, "Whether and how often to decay a random weight") + record_mask = Param.Int(191, + "Which histories are updated with filtered branch outcomes") + hash_taken = Param.Bool(False, + "Hash the taken/not taken value with a PC bit") + tuneonly = Param.Bool(True, + "If true, only count mispredictions of low-confidence branches") + extra_rounds = Param.Int(1, + "Number of extra rounds of training a single weight on a " + "low-confidence prediction") + speed = Param.Int(9, "Adaptive theta learning speed") + initial_theta = Param.Int(10, "Initial theta") + budgetbits = Param.Int("Hardware budget in bits") + speculative_update = Param.Bool(False, + "Use speculative update for histories") + +class MultiperspectivePerceptron8KB(MultiperspectivePerceptron): + type = 'MultiperspectivePerceptron8KB' + cxx_class = 'MultiperspectivePerceptron8KB' + cxx_header = 'cpu/pred/multiperspective_perceptron_8KB.hh' + budgetbits = 8192 * 8 + 2048 + num_local_histories = 48 + num_filter_entries = 0 + imli_mask1 = 0x6 + imli_mask4 = 0x4400 + recencypos_mask = 0x100000090 + +class MultiperspectivePerceptron64KB(MultiperspectivePerceptron): + type = 'MultiperspectivePerceptron64KB' + cxx_class = 'MultiperspectivePerceptron64KB' + cxx_header = 'cpu/pred/multiperspective_perceptron_64KB.hh' + budgetbits = 65536 * 8 + 2048 + num_local_histories = 510 + num_filter_entries = 18025 + imli_mask1 = 0xc1000 + imli_mask4 = 0x80008000 + recencypos_mask = 0x100000090 diff --git a/src/cpu/pred/SConscript b/src/cpu/pred/SConscript index 736586197..27821a4aa 100644 --- a/src/cpu/pred/SConscript +++ b/src/cpu/pred/SConscript @@ -48,6 +48,9 @@ Source('tage_base.cc') Source('tage.cc') Source('loop_predictor.cc') Source('ltage.cc') +Source('multiperspective_perceptron.cc') +Source('multiperspective_perceptron_8KB.cc') +Source('multiperspective_perceptron_64KB.cc') Source('statistical_corrector.cc') Source('tage_sc_l.cc') Source('tage_sc_l_8KB.cc') diff --git a/src/cpu/pred/multiperspective_perceptron.cc b/src/cpu/pred/multiperspective_perceptron.cc new file mode 100644 index 000000000..47bbb0212 --- /dev/null +++ b/src/cpu/pred/multiperspective_perceptron.cc @@ -0,0 +1,808 @@ +/* + * 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) + */ + +#include "cpu/pred/multiperspective_perceptron.hh" + +#include "debug/Branch.hh" + +int +MultiperspectivePerceptron::xlat[] = + {1,3,4,5,7,8,9,11,12,14,15,17,19,21,23,25,27,29,32,34,37,41,45,49,53,58,63, + 69,76,85,94,106,}; +int +MultiperspectivePerceptron::xlat4[] = + {0,4,5,7,9,11,12,14,16,17,19,22,28,33,39,45,}; + +MultiperspectivePerceptron::ThreadData::ThreadData(int num_filters, + int n_local_histories, int local_history_length, int assoc, + const std::vector> &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) + : filterTable(num_filters), acyclic_histories(acyclic_bits.size()), + acyclic2_histories(acyclic_bits.size()), + blurrypath_histories(blurrypath_bits.size()), + ghist_words(ghist_length/block_size+1, 0), + path_history(path_length, 0), imli_counter(4,0), + localHistories(n_local_histories, local_history_length), + recency_stack(assoc), last_ghist_bit(false), occupancy(0) +{ + for (int i = 0; i < blurrypath_bits.size(); i+= 1) { + blurrypath_histories[i].resize(blurrypath_bits[i].size()); + } + + for (int i = 0; i < acyclic_bits.size(); i += 1) { + acyclic_histories[i].resize(acyclic_bits[i].size()); + acyclic2_histories[i].resize(acyclic_bits[i].size()); + } + + int max_modhist_idx = -1; + for (auto &elem : modhist_indices) { + max_modhist_idx = (max_modhist_idx < elem) ? elem : max_modhist_idx; + } + if (max_modhist_idx >= 0) { + mod_histories.resize(max_modhist_idx + 1); + } + for (int i = 0; i < modhist_lengths.size(); i+= 1) { + mod_histories[modhist_indices[i]].resize(modhist_lengths[i]); + } + + int max_modpath_idx = -1; + for (auto &elem : modpath_indices) { + max_modpath_idx = (max_modpath_idx < elem) ? elem : max_modpath_idx; + } + if (max_modpath_idx >= 0) { + modpath_histories.resize(max_modpath_idx + 1); + } + for (int i = 0; i < modpath_lengths.size(); i+= 1) { + modpath_histories[modpath_indices[i]].resize(modpath_lengths[i]); + } + + for (int i = 0; i < table_sizes.size(); i += 1) { + mpreds.push_back(0); + tables.push_back(std::vector(table_sizes[i])); + sign_bits.push_back(std::vector>(table_sizes[i])); + for (int j = 0; j < table_sizes[i]; j += 1) { + for (int k = 0; k < n_sign_bits; k += 1) { + sign_bits[i][j][k] = (i & 1) | (k & 1); + } + } + } +} + +MultiperspectivePerceptron::MultiperspectivePerceptron( + const MultiperspectivePerceptronParams *p) : BPredUnit(p), + blockSize(p->block_size), pcshift(p->pcshift), threshold(p->threshold), + bias0(p->bias0), bias1(p->bias1), biasmostly0(p->biasmostly0), + biasmostly1(p->biasmostly1), nbest(p->nbest), tunebits(p->tunebits), + hshift(p->hshift), imli_mask1(p->imli_mask1), imli_mask4(p->imli_mask4), + recencypos_mask(p->recencypos_mask), fudge(p->fudge), + n_sign_bits(p->n_sign_bits), pcbit(p->pcbit), decay(p->decay), + record_mask(p->record_mask), hash_taken(p->hash_taken), + tuneonly(p->tuneonly), extra_rounds(p->extra_rounds), speed(p->speed), + budgetbits(p->budgetbits), speculative_update(p->speculative_update), + threadData(p->numThreads, nullptr), doing_local(false), + doing_recency(false), assoc(0), ghist_length(1), modghist_length(1), + path_length(1), randSeed(0xdeadbeef), thresholdCounter(0), + theta(p->initial_theta), imli_counter_bits(4), modhist_indices(), + modhist_lengths(), modpath_indices(), modpath_lengths() +{ + fatal_if(speculative_update, "Speculative update not implemented"); +} + +void +MultiperspectivePerceptron::init() +{ + createSpecs(); + + for (auto &spec : specs) { + // initial assignation of values + table_sizes.push_back(spec->size); + } + + // Update bit requirements and runtime values + for (auto &spec : specs) { + spec->setBitRequirements(); + } + const MultiperspectivePerceptronParams *p = + static_cast(params()); + + computeBits(p->num_filter_entries, p->num_local_histories, + p->local_history_length); + + for (int i = 0; i < threadData.size(); i += 1) { + threadData[i] = new ThreadData(p->num_filter_entries, + p->num_local_histories, + p->local_history_length, assoc, + blurrypath_bits, path_length, + ghist_length, blockSize, acyclic_bits, + modhist_indices, modhist_lengths, + modpath_indices, modpath_lengths, + table_sizes, n_sign_bits); + } +} + +void +MultiperspectivePerceptron::computeBits(int num_filter_entries, + int nlocal_histories, int local_history_length) { + int totalbits = 0; + for (auto &imli_bits : imli_counter_bits) { + totalbits += imli_bits; + } + totalbits += ghist_length; + totalbits += path_length * 16; + totalbits += (threshold >= 0) ? (tunebits * specs.size()) : 0; + for (auto &len : modhist_lengths) { + totalbits += len; + } + for (auto &len : modpath_lengths) { + totalbits += 16 * len; + } + totalbits += doing_local ? (nlocal_histories * local_history_length) : 0; + totalbits += doing_recency ? (assoc * 16) : 0; + + for (auto &bv : blurrypath_bits) { + for (auto &bve : bv) { + totalbits += bve; + } + } + totalbits += num_filter_entries * 2; + + for (auto &abi : acyclic_bits) { + for (auto &abj : abi) { + for (auto abk : abj) { + totalbits += abk; + } + } + } + + int remaining = budgetbits - totalbits; + + // count the tables that have already been assigned sizes + int num_sized = 0; + for (int i = 0; i < specs.size(); i +=1) { + if (table_sizes[i] != 0) { + int sz = table_sizes[i] * (specs[i]->width + (n_sign_bits - 1)); + totalbits += sz; + remaining -= sz; + num_sized += 1; + } + } + + // whatever is left, we divide among the rest of the tables + int table_size_bits = (remaining / (specs.size()-num_sized)); + for (int i = 0; i < specs.size(); i += 1) { + // if a table doesn't have a size yet, give it one and count those bits + if (!table_sizes[i]) { + int my_table_size = table_size_bits / + (specs[i]->width + (n_sign_bits - 1)); // extra sign bits + table_sizes[i] = my_table_size; + totalbits += my_table_size * (specs[i]->width + (n_sign_bits - 1)); + } + } + + DPRINTF(Branch, "%d bits of metadata so far, %d left out of " + "%d total budget\n", totalbits, remaining, budgetbits); + DPRINTF(Branch, "table size is %d bits, %d entries for 5 bit, %d entries " + "for 6 bit\n", table_size_bits, + table_size_bits / (5 + (n_sign_bits - 1)), + table_size_bits / (6 + (n_sign_bits - 1))); + DPRINTF(Branch, "%d total bits (%0.2fKB)\n", totalbits, + totalbits / 8192.0); +} + +void +MultiperspectivePerceptron::findBest(ThreadID tid, + std::vector &best_preds) const +{ + if (threshold < 0) { + return; + } + struct BestPair { + int index; + int mpreds; + bool operator<(BestPair const &bp) const + { + return mpreds < bp.mpreds; + } + }; + std::vector pairs(best_preds.size()); + for (int i = 0; i < best_preds.size(); i += 1) { + pairs[i].index = i; + pairs[i].mpreds = threadData[tid]->mpreds[i]; + } + std::sort(pairs.begin(), pairs.end()); + for (int i = 0; i < (std::min(nbest, (int) best_preds.size())); i += 1) { + best_preds[i] = pairs[i].index; + } +} + +unsigned int +MultiperspectivePerceptron::getIndex(ThreadID tid, const MPPBranchInfo &bi, + const HistorySpec &spec, int index) const +{ + unsigned int g = spec.getHash(tid, bi.getPC(), bi.getPC2(), index); + unsigned long long int h = g; + // shift the hash from the feature to xor with the hashed PC + if (hshift < 0) { + h <<= -hshift; + h ^= bi.getPC2(); + } else { + h <<= hshift; + h ^= bi.getHPC(); + } + // xor in the imli counter(s) and/or recency position based on the masks + if ((1ull<imli_counter[0]; + } + if ((1ull<imli_counter[3]; + } + if (doing_recency) { + if ((1ull<recency_stack, table_sizes, + bi.getPC2(), 31, index); + } + } + h %= table_sizes[index]; + return h; +} + +int +MultiperspectivePerceptron::computeOutput(ThreadID tid, MPPBranchInfo &bi) +{ + // list of best predictors + std::vector best_preds(specs.size(), -1); + + // initialize sum + bi.yout = 0; + + // bias the prediction by whether the local history is + // one of four distinctive patterns + int lhist = threadData[tid]->localHistories[bi.getPC()]; + int history_len = threadData[tid]->localHistories.getLocalHistoryLength(); + if (lhist == 0) { + bi.yout = bias0; + } else if (lhist == ((1<tables[i][hashed_idx]; + // get the sign + bool sign = + threadData[tid]->sign_bits[i][hashed_idx][bi.getHPC() % n_sign_bits]; + // apply the transfer function and multiply by a coefficient + int weight = spec.coeff * ((spec.width == 5) ? + xlat4[counter] : xlat[counter]); + // apply the sign + int val = sign ? -weight : weight; + // add the value + bi.yout += val; + // if this is one of those good features, add the value to bestval + if (threshold >= 0) { + for (int j = 0; + j < std::min(nbest, (int) best_preds.size()); + j += 1) + { + if (best_preds[j] == i) { + bestval += val; + break; + } + } + } + } + // apply a fudge factor to affect when training is triggered + bi.yout *= fudge; + return bestval; +} + +void +MultiperspectivePerceptron::satIncDec(bool taken, bool &sign, int &counter, + int max_weight) const +{ + if (taken) { + // increment sign/magnitude + if (sign) { + // go toward 0 away from negative max weight + if (counter == 0) { + sign = false; // moved to positive 0 + } else { + counter -= 1; + } + } else { + // go toward max weight away from 0 + if (counter < max_weight) { + counter += 1; + } + } + } else { + // decrement sign/magnitude + if (sign) { + // go toward negative max weight down from 0 + if (counter < max_weight) { + counter += 1; + } + } else { + // go toward 0 away from max weight + if (counter == 0) { + sign = true; // negative 0 + } else { + counter -= 1; + } + } + } +} + +void +MultiperspectivePerceptron::train(ThreadID tid, MPPBranchInfo &bi, bool taken) +{ + std::vector> &tables = threadData[tid]->tables; + std::vector>> &sign_bits = + threadData[tid]->sign_bits; + std::vector &mpreds = threadData[tid]->mpreds; + // was the prediction correct? + bool correct = (bi.yout >= 1) == taken; + // what is the magnitude of yout? + int abs_yout = abs(bi.yout); + // keep track of mispredictions per table + if (threshold >= 0) if (!tuneonly || (abs_yout <= threshold)) { + bool halve = false; + + // for each table, figure out if there was a misprediction + for (int i = 0; i < specs.size(); i += 1) { + HistorySpec const &spec = *specs[i]; + // get the hash to index the table + unsigned int hashed_idx = getIndex(tid, bi, spec, i); + bool sign = sign_bits[i][hashed_idx][bi.getHPC() % n_sign_bits]; + int counter = tables[i][hashed_idx]; + int weight = spec.coeff * ((spec.width == 5) ? + xlat4[counter] : xlat[counter]); + if (sign) weight = -weight; + bool pred = weight >= 1; + if (pred != taken) { + mpreds[i] += 1; + if (mpreds[i] == (1 << tunebits) - 1) { + halve = true; + } + } + } + // if we reach the maximum counter value, halve all the counters + if (halve) { + for (int i = 0; i < specs.size(); i += 1) { + mpreds[i] /= 2; + } + } + } + // if the branch was predicted incorrectly or the correct + // prediction was weak, update the weights + bool do_train = !correct || (abs_yout <= theta); + if (!do_train) return; + + // adaptive theta training, adapted from O-GEHL + if (!correct) { + thresholdCounter += 1; + if (thresholdCounter >= speed) { + theta += 1; + thresholdCounter = 0; + } + } + if (correct && abs_yout < theta) { + thresholdCounter -= 1; + if (thresholdCounter <= -speed) { + theta -= 1; + thresholdCounter = 0; + } + } + + // train the weights, computing what the value of yout + // would have been if these updates had been applied before + int newyout = 0; + for (int i = 0; i < specs.size(); i += 1) { + HistorySpec const &spec = *specs[i]; + // get the magnitude + unsigned int hashed_idx = getIndex(tid, bi, spec, i); + int counter = tables[i][hashed_idx]; + // get the sign + bool sign = sign_bits[i][hashed_idx][bi.getHPC() % n_sign_bits]; + // increment/decrement if taken/not taken + satIncDec(taken, sign, counter, (1 << (spec.width - 1)) - 1); + // update the magnitude and sign + tables[i][hashed_idx] = counter; + sign_bits[i][hashed_idx][bi.getHPC() % n_sign_bits] = sign; + int weight = ((spec.width == 5) ? xlat4[counter] : xlat[counter]); + // update the new version of yout + if (sign) { + newyout -= weight; + } else { + newyout += weight; + } + } + + // if the prediction still would have been incorrect even + // with the updated weights, update some more weights to + // try to fix the problem + if ((newyout >= 1) != taken) { + if (extra_rounds != -1) { + int round_counter = 0; + bool found; + do { + // udpate a random weight + int besti = -1; + int nrand = rand_r(&randSeed) % specs.size(); + int pout; + found = false; + for (int j = 0; j < specs.size(); j += 1) { + int i = (nrand + j) % specs.size(); + HistorySpec const &spec = *specs[i]; + unsigned int hashed_idx = getIndex(tid, bi, spec, i); + int counter = tables[i][hashed_idx]; + bool sign = + sign_bits[i][hashed_idx][bi.getHPC() % n_sign_bits]; + int weight = ((spec.width == 5) ? + xlat4[counter] : xlat[counter]); + int signed_weight = sign ? -weight : weight; + pout = newyout - signed_weight; + if ((pout >= 1) == taken) { + // we have found a weight that if we blow + // it away will help! + besti = i; + break; + } + } + if (besti != -1) { + int i = besti; + HistorySpec const &spec = *specs[i]; + unsigned int hashed_idx = getIndex(tid, bi, spec, i); + int counter = tables[i][hashed_idx]; + bool sign = + sign_bits[i][hashed_idx][bi.getHPC() % n_sign_bits]; + if (counter > 1) { + counter--; + tables[i][hashed_idx] = counter; + } + int weight = ((spec.width == 5) ? + xlat4[counter] : xlat[counter]); + int signed_weight = sign ? -weight : weight; + int out = pout + signed_weight; + round_counter += 1; + if ((out >= 1) != taken) { + found = true; + } + } + } while (found && round_counter < extra_rounds); + } + } +} + +void +MultiperspectivePerceptron::uncondBranch(ThreadID tid, Addr pc, + void * &bp_history) +{ + MPPBranchInfo *bi = new MPPBranchInfo(pc, pcshift, false); + std::vector &ghist_words = threadData[tid]->ghist_words; + + bp_history = (void *)bi; + unsigned short int pc2 = pc >> 2; + bool ab = !(pc & (1<> (blockSize - 1)) & 1; + ghist_words[i] <<= 1; + ghist_words[i] |= ab; + ghist_words[i] &= (1 << blockSize) - 1; + ab = ab_new; + } + memmove(&threadData[tid]->path_history[1], + &threadData[tid]->path_history[0], + sizeof(unsigned short int) * (path_length - 1)); + threadData[tid]->path_history[0] = pc2; +} + +bool +MultiperspectivePerceptron::lookup(ThreadID tid, Addr instPC, + void * &bp_history) +{ + MPPBranchInfo *bi = new MPPBranchInfo(instPC, pcshift, true); + bp_history = (void *)bi; + + bool use_static = false; + + if (!threadData[tid]->filterTable.empty()) { + unsigned int findex = + bi->getHashFilter(threadData[tid]->last_ghist_bit) % + threadData[tid]->filterTable.size(); + FilterEntry &f = threadData[tid]->filterTable[findex]; + if (f.alwaysNotTakenSoFar()) { + bi->filtered = true; + bi->prediction = false; + return false; + } else if (f.alwaysTakenSoFar()) { + bi->filtered = true; + bi->prediction = true; + return true; + } + if (f.neverSeen()) { + use_static = true; + } + } + + int bestval = computeOutput(tid, *bi); + if (use_static) { + bi->prediction = false; + } else { + if (abs(bi->yout) <= threshold) { + bi->prediction = (bestval >= 1); + } else { + bi->prediction = (bi->yout >= 1); + } + } + + return bi->prediction; +} + +void +MultiperspectivePerceptron::update(ThreadID tid, Addr instPC, bool taken, + void *bp_history, bool squashed, + const StaticInstPtr & inst, + Addr corrTarget) +{ + assert(bp_history); + MPPBranchInfo *bi = static_cast(bp_history); + assert(corrTarget != MaxAddr); + if (squashed) { + //delete bi; + return; + } + + if (bi->isUnconditional()) { + delete bi; + return; + } + + bool do_train = true; + + if (!threadData[tid]->filterTable.empty()) { + int findex = bi->getHashFilter(threadData[tid]->last_ghist_bit) % + threadData[tid]->filterTable.size(); + FilterEntry &f = threadData[tid]->filterTable[findex]; + + // compute this first, so we don't not train on the + // first time a branch is seen. + bool transition = false; + if (f.alwaysNotTakenSoFar() || f.alwaysTakenSoFar()) { + do_train = false; + } + if (taken) { + if (f.alwaysNotTakenSoFar()) { + transition = true; + } + f.seenTaken = true; + } else { + if (f.alwaysTakenSoFar()) { + transition = true; + } + f.seenUntaken = true; + } + // is this the first time time the branch has gone both ways? + if (transition) { + threadData[tid]->occupancy += 1; + } + // for every new dynamic branch, when there ar + // more than 'decay' number of branches in the + // filter, blow a random filter entry away + if (decay && transition && + ((threadData[tid]->occupancy > decay) || (decay == 1))) { + int rnd = rand_r(&randSeed) % threadData[tid]->filterTable.size(); + FilterEntry &frand = threadData[tid]->filterTable[rnd]; + if (frand.seenTaken && frand.seenUntaken) { + threadData[tid]->occupancy -= 1; + } + frand.seenTaken = false; + frand.seenUntaken = false; + } + } + + if (do_train) { + train(tid, *bi, taken); + } + +#define RECORD_FILTERED_IMLI 1 +#define RECORD_FILTERED_GHIST 2 +#define RECORD_FILTERED_PATH 4 +#define RECORD_FILTERED_ACYCLIC 8 +#define RECORD_FILTERED_MOD 16 +#define RECORD_FILTERED_BLURRY 32 +// should never record a filtered local branch - duh! +#define RECORD_FILTERED_LOCAL 64 +#define RECORD_FILTERED_RECENCY 128 + + // four different styles of IMLI + if (!bi->filtered || (record_mask & RECORD_FILTERED_IMLI)) { + unsigned int target = corrTarget; + if (target < bi->getPC()) { + if (taken) { + threadData[tid]->imli_counter[0] += 1; + } else { + threadData[tid]->imli_counter[0] = 0; + } + if (!taken) { + threadData[tid]->imli_counter[1] += 1; + } else { + threadData[tid]->imli_counter[1] = 0; + } + } else { + if (taken) { + threadData[tid]->imli_counter[2] += 1; + } else { + threadData[tid]->imli_counter[2] = 0; + } + if (!taken) { + threadData[tid]->imli_counter[3] += 1; + } else { + threadData[tid]->imli_counter[3] = 0; + } + } + } + + bool hashed_taken = hash_taken ? (taken ^ !!(bi->getPC() & (1<filtered || (record_mask & RECORD_FILTERED_GHIST)) { + bool ab = hashed_taken; + assert(threadData[tid]->ghist_words.size() > 0); + for (int i = 0; i < ghist_length / blockSize + 1; i += 1) { + unsigned int a = threadData[tid]->ghist_words[i]; + bool ab_new = (a >> (blockSize - 1)) & 1; + a <<= 1; + a |= ab; + ab = ab_new; + a &= (1 << blockSize) - 1; + threadData[tid]->ghist_words[i] = a; + } + } + + // record into path history + if (!bi->filtered || (record_mask & RECORD_FILTERED_PATH)) { + assert(threadData[tid]->path_history.size() > 0); + memmove(&threadData[tid]->path_history[1], + &threadData[tid]->path_history[0], + sizeof(unsigned short int) * (path_length - 1)); + threadData[tid]->path_history[0] = bi->getPC2(); + } + + // record into acyclic history + if (!bi->filtered || (record_mask & RECORD_FILTERED_ACYCLIC)) { + threadData[tid]->updateAcyclic(hashed_taken, bi->getHPC()); + } + + // record into modulo path history + if (!bi->filtered || (record_mask & RECORD_FILTERED_MOD)) { + for (int ii = 0; ii < modpath_indices.size(); ii += 1) { + int i = modpath_indices[ii]; + if (bi->getHPC() % (i + 2) == 0) { + memmove(&threadData[tid]->modpath_histories[i][1], + &threadData[tid]->modpath_histories[i][0], + sizeof(unsigned short int) * (modpath_lengths[ii]-1)); + threadData[tid]->modpath_histories[i][0] = bi->getPC2(); + } + } + } + + // update blurry history + if (!bi->filtered || (record_mask & RECORD_FILTERED_BLURRY)) { + std::vector> &blurrypath_histories = + threadData[tid]->blurrypath_histories; + + for (int i = 0; i < blurrypath_histories.size(); i += 1) + { + if (blurrypath_histories[i].size() > 0) { + unsigned int z = bi->getPC() >> i; + if (blurrypath_histories[i][0] != z) { + memmove(&blurrypath_histories[i][1], + &blurrypath_histories[i][0], + sizeof(unsigned int) * + (blurrypath_histories[i].size() - 1)); + blurrypath_histories[i][0] = z; + } + } + } + } + + // record into modulo pattern history + if (!bi->filtered || (record_mask & RECORD_FILTERED_MOD)) { + for (int ii = 0; ii < modhist_indices.size(); ii += 1) { + int i = modhist_indices[ii]; + if (bi->getHPC() % (i + 2) == 0) { + for (int j = modhist_lengths[ii] - 1; j > 0; j -= 1) { + threadData[tid]->mod_histories[i][j] = + threadData[tid]->mod_histories[i][j-1]; + } + threadData[tid]->mod_histories[i][0] = hashed_taken; + } + } + } + + // insert this PC into the recency stack + if (doing_recency) { + if (!bi->filtered || (record_mask & RECORD_FILTERED_RECENCY)) { + threadData[tid]->insertRecency(bi->getPC2(), assoc); + } + } + + // record into a local history + if (!bi->filtered || (record_mask & RECORD_FILTERED_LOCAL)) { + threadData[tid]->localHistories.update(bi->getPC(), hashed_taken); + } + + // update last ghist bit, used to index filter + threadData[tid]->last_ghist_bit = taken; + + delete bi; +} + +void +MultiperspectivePerceptron::btbUpdate(ThreadID tid, Addr branch_pc, + void* &bp_history) +{ +} + +void +MultiperspectivePerceptron::squash(ThreadID tid, void *bp_history) +{ + assert(bp_history); + MPPBranchInfo *bi = static_cast(bp_history); + delete bi; +} diff --git a/src/cpu/pred/multiperspective_perceptron.hh b/src/cpu/pred/multiperspective_perceptron.hh new file mode 100644 index 000000000..e81bd8eec --- /dev/null +++ b/src/cpu/pred/multiperspective_perceptron.hh @@ -0,0 +1,1027 @@ +/* + * 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; + unsigned int randSeed; + int thresholdCounter; + int theta; + 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 + */ + void computeBits(int num_filter_entries, int nlocal_histories, + int local_history_length); + + /** + * 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); + + 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__ diff --git a/src/cpu/pred/multiperspective_perceptron_64KB.cc b/src/cpu/pred/multiperspective_perceptron_64KB.cc new file mode 100644 index 000000000..a5d724183 --- /dev/null +++ b/src/cpu/pred/multiperspective_perceptron_64KB.cc @@ -0,0 +1,93 @@ +/* + * 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) + * - 64KB version + */ + +#include "cpu/pred/multiperspective_perceptron_64KB.hh" + +MultiperspectivePerceptron64KB::MultiperspectivePerceptron64KB( + const MultiperspectivePerceptron64KBParams *p) + : MultiperspectivePerceptron(p) +{ +} + +void +MultiperspectivePerceptron64KB::createSpecs() { + addSpec(new ACYCLIC(10, -1, -1, 1.0, 0, 6, *this)); + addSpec(new BLURRYPATH(10, 7, -1, 1.0, 0, 6, *this)); + addSpec(new GHIST(0, 19, 1.3125, 0, 6, *this)); + addSpec(new GHIST(0, 65, 0.85, 0, 6, *this)); + addSpec(new GHIST(111, 222, 1.0, 0, 6, *this)); + addSpec(new GHIST(115, 206, 1.0, 0, 6, *this)); + addSpec(new GHIST(190, 336, 1.0, 0, 6, *this)); + addSpec(new GHIST(21, 64, 1.0, 0, 6, *this)); + addSpec(new GHIST(48, 119, 1.0, 0, 6, *this)); + addSpec(new GHIST(67, 203, 0.75, 0, 6, *this)); + addSpec(new GHIST(75, 150, 1.0, 0, 6, *this)); + addSpec(new GHISTMODPATH(1, 5, 4, 1.45, 1913, 6, *this)); + addSpec(new GHISTMODPATH(3, 13, 1, 1.0, 0, 6, *this)); + addSpec(new GHISTPATH(105, 4, 0, 1.0, 0, 6, *this)); + addSpec(new GHISTPATH(11, 2, -1, 1.25, 0, 6, *this)); + addSpec(new GHISTPATH(15, 4, -1, 1.125, 0, 6, *this)); + addSpec(new GHISTPATH(23, 4, -1, 1.0, 0, 6, *this)); + addSpec(new GHISTPATH(31, 1, -1, 1.0, 0, 6, *this)); + addSpec(new GHISTPATH(32, 2, -1, 1.0, 0, 6, *this)); + addSpec(new GHISTPATH(36, 4, -1, 1.0, 0, 6, *this)); + addSpec(new GHISTPATH(51, 1, 0, 1.0, 0, 6, *this)); + addSpec(new GHISTPATH(7, 1, -1, 1.5, 0, 6, *this)); + addSpec(new GHISTPATH(72, 1, -1, 1.0, 0, 6, *this)); + addSpec(new GHISTPATH(86, 4, 0, 1.0, 0, 6, *this)); + addSpec(new IMLI(1, 1.8125, 0, 6, *this)); + addSpec(new IMLI(4, 1.78125, 1536, 6, *this)); + addSpec(new LOCAL(-1, 1.0, 1024, 6, *this)); + addSpec(new LOCAL(-1, 2.0, 3467, 6, *this)); + addSpec(new MODHIST(1, 16, 0.9375, 0, 6, *this)); + addSpec(new MODPATH(3, 20, 1, 1.0, 0, 6, *this)); + addSpec(new PATH(13, 2, -1, 1.4375, 0, 6, *this)); + addSpec(new PATH(27, 5, -1, 1.0, 0, 6, *this)); + addSpec(new RECENCY(10, 3, -1, 0.55, 1024, 6, *this)); + addSpec(new RECENCY(14, 4, -1, 1.3125, 0, 6, *this)); + addSpec(new RECENCYPOS(31, 1.5, 0, 6, *this)); + addSpec(new SGHISTPATH(1, 2, 5, 1.25, 768, 6, *this)); + addSpec(new SGHISTPATH(1, 5, 2, 1.3125, 972, 6, *this)); +} + + MultiperspectivePerceptron64KB* +MultiperspectivePerceptron64KBParams::create() +{ + return new MultiperspectivePerceptron64KB(this); +} diff --git a/src/cpu/pred/multiperspective_perceptron_64KB.hh b/src/cpu/pred/multiperspective_perceptron_64KB.hh new file mode 100644 index 000000000..a87020b6d --- /dev/null +++ b/src/cpu/pred/multiperspective_perceptron_64KB.hh @@ -0,0 +1,53 @@ +/* + * 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) + * - 64KB version + */ + +#ifndef __CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_64KB_HH__ +#define __CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_64KB_HH__ + +#include "cpu/pred/multiperspective_perceptron.hh" +#include "params/MultiperspectivePerceptron64KB.hh" + +class MultiperspectivePerceptron64KB : public MultiperspectivePerceptron { + void createSpecs() override; + public: + MultiperspectivePerceptron64KB( + const MultiperspectivePerceptron64KBParams *p); +}; + +#endif // __CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_64KB_HH__ diff --git a/src/cpu/pred/multiperspective_perceptron_8KB.cc b/src/cpu/pred/multiperspective_perceptron_8KB.cc new file mode 100644 index 000000000..832e17237 --- /dev/null +++ b/src/cpu/pred/multiperspective_perceptron_8KB.cc @@ -0,0 +1,72 @@ +/* + * 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) + * - 8KB version + */ + +#include "cpu/pred/multiperspective_perceptron_8KB.hh" + +MultiperspectivePerceptron8KB::MultiperspectivePerceptron8KB( + const MultiperspectivePerceptron8KBParams *p) + : MultiperspectivePerceptron(p) +{ +} + +void +MultiperspectivePerceptron8KB::createSpecs() { + addSpec(new BIAS(2.40625, 0, 6, *this)); + addSpec(new GHIST(0, 19, 1.4375, 0, 6, *this)); + addSpec(new GHIST(0, 65, 1.0, 0, 6, *this)); + addSpec(new GHIST(21, 64, 1.0, 0, 6, *this)); + addSpec(new GHIST(75, 150, 1.0625, 0, 6, *this)); + addSpec(new GHISTMODPATH(0, 7, 3, 1.625, 0, 6, *this)); + addSpec(new GHISTPATH(11, 2, -1, 1.25, 0, 6, *this)); + addSpec(new GHISTPATH(15, 4, -1, 1.125, 0, 6, *this)); + addSpec(new GHISTPATH(31, 1, -1, 1.40625, 0, 6, *this)); + addSpec(new GHISTPATH(7, 1, -1, 1.5, 600, 6, *this)); + addSpec(new IMLI(4, 1.28125, 375, 6, *this)); + addSpec(new LOCAL(-1, 1.5625, 512, 6, *this)); + addSpec(new RECENCY(14, 4, -1, 1.25, 0, 6, *this)); + addSpec(new RECENCYPOS(31, 1.875, 0, 6, *this)); + addSpec(new SGHISTPATH(0, 4, 3, 1.65625, 0, 6, *this)); + addSpec(new SGHISTPATH(1, 2, 5, 2.53125, 0, 5, *this)); +} + + MultiperspectivePerceptron8KB* +MultiperspectivePerceptron8KBParams::create() +{ + return new MultiperspectivePerceptron8KB(this); +} diff --git a/src/cpu/pred/multiperspective_perceptron_8KB.hh b/src/cpu/pred/multiperspective_perceptron_8KB.hh new file mode 100644 index 000000000..032ecdf03 --- /dev/null +++ b/src/cpu/pred/multiperspective_perceptron_8KB.hh @@ -0,0 +1,53 @@ +/* + * 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) + * - 8KB version + */ + +#ifndef __CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_8KB_HH__ +#define __CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_8KB_HH__ + +#include "cpu/pred/multiperspective_perceptron.hh" +#include "params/MultiperspectivePerceptron8KB.hh" + +class MultiperspectivePerceptron8KB : public MultiperspectivePerceptron { + void createSpecs() override; + public: + MultiperspectivePerceptron8KB( + const MultiperspectivePerceptron8KBParams *p); +}; + +#endif // __CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_8KB_HH__ -- 2.30.2