This patch adds the GPU protocol tester that uses data-race-free operation to discover bugs in GPU protocols including GPU_VIPER. For more information please see the following paper and the README: T. Ta, X. Zhang, A. Gutierrez and B. M. Beckmann, "Autonomous Data-Race-Free GPU Testing," 2019 IEEE International Symposium on Workload Characterization (IISWC), Orlando, FL, USA, 2019, pp. 81-92, doi: 10.1109/IISWC47752.2019.9042019. Change-Id: Ic9939d131a930d1e7014ed0290601140bdd1499f Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/32855 Reviewed-by: Matt Sinclair <mattdsinclair@gmail.com> Reviewed-by: Jason Lowe-Power <power.jg@gmail.com> Maintainer: Matt Sinclair <mattdsinclair@gmail.com> Tested-by: kokoro <noreply+kokoro@google.com>
432 lines
14 KiB
C++
432 lines
14 KiB
C++
/*
|
|
* Copyright (c) 2017-2020 Advanced Micro Devices, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* For use for simulation and test purposes only
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include "cpu/testers/gpu_ruby_test/address_manager.hh"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "base/intmath.hh"
|
|
#include "base/logging.hh"
|
|
#include "base/random.hh"
|
|
#include "base/trace.hh"
|
|
|
|
const int AddressManager::INVALID_VALUE = -1;
|
|
const int AddressManager::INVALID_LOCATION = -1;
|
|
|
|
AddressManager::AddressManager(int n_atomic_locs, int n_normal_locs_per_atomic)
|
|
: numAtomicLocs(n_atomic_locs),
|
|
numLocsPerAtomic(n_normal_locs_per_atomic)
|
|
{
|
|
assert(numAtomicLocs > 0 && numLocsPerAtomic > 0);
|
|
numNormalLocs = numAtomicLocs * numLocsPerAtomic;
|
|
|
|
// generate random address map
|
|
randAddressMap.resize(numAtomicLocs + numNormalLocs);
|
|
for (Location i = 0; i < numAtomicLocs + numNormalLocs; ++i) {
|
|
// all addresses are sizeof(Value) (i.e., 4-byte) aligned
|
|
randAddressMap[i] = (Addr)((i + 128) << floorLog2(sizeof(Value)));
|
|
}
|
|
|
|
// randomly shuffle randAddressMap
|
|
std::random_shuffle(randAddressMap.begin(), randAddressMap.end());
|
|
|
|
// initialize atomic locations
|
|
// first and last normal location per atomic location
|
|
Location first, last;
|
|
for (Location atomic_loc = 0; atomic_loc < numAtomicLocs; ++atomic_loc) {
|
|
first = numAtomicLocs + numLocsPerAtomic * atomic_loc;
|
|
last = first + numLocsPerAtomic - 1;
|
|
atomicStructs.push_back(new AtomicStruct(atomic_loc, first, last));
|
|
}
|
|
|
|
// initialize log table
|
|
for (Location loc = 0; loc < numAtomicLocs + numNormalLocs; ++loc) {
|
|
logTable.push_back(new LastWriter());
|
|
}
|
|
}
|
|
|
|
AddressManager::~AddressManager()
|
|
{
|
|
for (AtomicStruct* atomic_struct : atomicStructs)
|
|
delete atomic_struct;
|
|
for (LastWriter* lw : logTable)
|
|
delete lw;
|
|
}
|
|
|
|
Addr
|
|
AddressManager::getAddress(Location loc)
|
|
{
|
|
assert(loc < numAtomicLocs + numNormalLocs && loc >= 0);
|
|
return randAddressMap[loc];
|
|
}
|
|
|
|
AddressManager::Location
|
|
AddressManager::getAtomicLoc()
|
|
{
|
|
Location ret_atomic_loc = random() % numAtomicLocs;
|
|
atomicStructs[ret_atomic_loc]->startLocSelection();
|
|
return ret_atomic_loc;
|
|
}
|
|
|
|
AddressManager::Location
|
|
AddressManager::getLoadLoc(Location atomic_loc)
|
|
{
|
|
assert(atomic_loc >= 0 && atomic_loc < numAtomicLocs);
|
|
return atomicStructs[atomic_loc]->getLoadLoc();
|
|
}
|
|
|
|
AddressManager::Location
|
|
AddressManager::getStoreLoc(Location atomic_loc)
|
|
{
|
|
assert(atomic_loc >= 0 && atomic_loc < numAtomicLocs);
|
|
return atomicStructs[atomic_loc]->getStoreLoc();
|
|
}
|
|
|
|
void
|
|
AddressManager::finishLocSelection(Location atomic_loc)
|
|
{
|
|
assert(atomic_loc >= 0 && atomic_loc < numAtomicLocs);
|
|
atomicStructs[atomic_loc]->endLocSelection();
|
|
}
|
|
|
|
void
|
|
AddressManager::releaseLocation(Location atomic_loc, Location loc)
|
|
{
|
|
assert(atomic_loc >= 0 && atomic_loc < numAtomicLocs);
|
|
atomicStructs[atomic_loc]->releaseLoc(loc);
|
|
}
|
|
|
|
std::string
|
|
AddressManager::printLastWriter(Location loc) const
|
|
{
|
|
return logTable[loc]->print();
|
|
}
|
|
|
|
// ------------------- AtomicStruct --------------------------
|
|
AddressManager::AtomicStruct::AtomicStruct(Location atomic_loc,
|
|
Location loc_begin,
|
|
Location loc_end)
|
|
{
|
|
// the location range must have at least 1 location
|
|
assert(loc_begin <= loc_end);
|
|
|
|
atomicLoc = atomic_loc;
|
|
arraySize = loc_end - loc_begin + 1;
|
|
locationBase = loc_begin;
|
|
|
|
// allocate an array of arrray_size
|
|
locArray = new Location[arraySize];
|
|
|
|
// initialize locArray & locProps
|
|
Location loc;
|
|
for (int offset = 0; offset < arraySize; ++offset) {
|
|
loc = locationBase + offset;
|
|
locArray[offset] = loc;
|
|
locProps.push_back(LocProperty(offset, 0));
|
|
}
|
|
|
|
// region (1) and (3) are initially empty
|
|
firstMark = 0;
|
|
secondMark = arraySize;
|
|
// no request made at this location so far
|
|
requestCount = 0;
|
|
}
|
|
|
|
AddressManager::AtomicStruct::~AtomicStruct()
|
|
{
|
|
delete[] locArray;
|
|
}
|
|
|
|
void
|
|
AddressManager::AtomicStruct::startLocSelection()
|
|
{
|
|
assert(firstMark >= 0);
|
|
assert(firstMark <= secondMark);
|
|
assert(secondMark <= arraySize);
|
|
// make sure loadStoreMap has been cleared
|
|
assert(loadStoreMap.empty());
|
|
|
|
// this atomic location is picked for Atomic_ACQ
|
|
// and Atomic_REL in an episode
|
|
requestCount += 2;
|
|
// add two expected values in expectedValues set
|
|
expectedValues.insert(requestCount - 1);
|
|
expectedValues.insert(requestCount - 2);
|
|
}
|
|
|
|
AddressManager::Location
|
|
AddressManager::AtomicStruct::getLoadLoc()
|
|
{
|
|
assert(firstMark >= 0);
|
|
assert(firstMark <= secondMark);
|
|
assert(secondMark <= arraySize);
|
|
|
|
if (firstMark == arraySize) {
|
|
// no location can be picked for a LD now, so return an empty location
|
|
return INVALID_LOCATION;
|
|
} else {
|
|
// we can pick any location btw
|
|
// locArray [firstMark : arraySize-1]
|
|
int range_size = arraySize - firstMark;
|
|
Location ret_loc = locArray[firstMark + random() % range_size];
|
|
|
|
// update loadStoreMap
|
|
LdStMap::iterator it = loadStoreMap.find(ret_loc);
|
|
|
|
if (it == loadStoreMap.end()) {
|
|
// insert a new entry to the map b/c the entry is not there yet
|
|
// to mark this location has been picked for a LD
|
|
loadStoreMap.insert(std::pair<Location, LdStBits>
|
|
(ret_loc, LdStBits(true,false)));
|
|
} else {
|
|
// otherwise, just update the LD bit
|
|
(it->second).first = true;
|
|
}
|
|
|
|
return ret_loc;
|
|
}
|
|
}
|
|
|
|
AddressManager::Location
|
|
AddressManager::AtomicStruct::getStoreLoc()
|
|
{
|
|
assert(firstMark >= 0);
|
|
assert(firstMark <= secondMark);
|
|
assert(secondMark <= arraySize);
|
|
|
|
if (firstMark == secondMark) {
|
|
// no location can be picked for a ST now, return an invalid location
|
|
return INVALID_LOCATION;
|
|
} else {
|
|
// we can pick any location btw [firstMark : secondMark-1]
|
|
int range_size = secondMark - firstMark;
|
|
Location ret_loc = locArray[firstMark + random() % range_size];
|
|
|
|
// update loadStoreMap
|
|
LdStMap::iterator it = loadStoreMap.find(ret_loc);
|
|
|
|
if (it == loadStoreMap.end()) {
|
|
// insert a new entry to the map b/c the entry is not there yet
|
|
// to mark this location has been picked for a ST
|
|
loadStoreMap.insert(std::pair<Location, LdStBits>
|
|
(ret_loc, LdStBits(false,true)));
|
|
} else {
|
|
// otherwise, just update the ST bit
|
|
(it->second).second = true;
|
|
}
|
|
|
|
return ret_loc;
|
|
}
|
|
}
|
|
|
|
// for each entry in loadStoreMap,
|
|
// if <LD_bit, ST_bit> == <1,0>
|
|
// - if the location is in (2), then move it to (3)
|
|
// - if the location is in (3), no move
|
|
// - otherwise, throw an error
|
|
// if <LD_bit, ST_bit> == <0,1> or <1,1>
|
|
// - move it from (2) to (1)
|
|
void
|
|
AddressManager::AtomicStruct::endLocSelection()
|
|
{
|
|
assert(firstMark >= 0);
|
|
assert(firstMark <= secondMark);
|
|
assert(secondMark <= arraySize);
|
|
|
|
for (auto& it : loadStoreMap) {
|
|
Location loc = it.first;
|
|
LdStBits p = it.second;
|
|
|
|
assert(loc >= locationBase && loc < locationBase + arraySize);
|
|
LocProperty& loc_prop = locProps[loc - locationBase];
|
|
|
|
if (p.first && !p.second) {
|
|
// this location has been picked for LD(s) but not ST
|
|
// it must be in either region (2) or (3)
|
|
assert(inSecondRegion(loc_prop.first) ||
|
|
inThirdRegion(loc_prop.first));
|
|
|
|
if (inSecondRegion(loc_prop.first)) {
|
|
// there is no owner of this location yet
|
|
assert(loc_prop.second == 0);
|
|
|
|
// pick the last location in (2) to swap
|
|
Location swapped_loc = locArray[secondMark - 1];
|
|
LocProperty& swapped_loc_prop =
|
|
locProps[swapped_loc - locationBase];
|
|
|
|
// swap loc and swapped_loc
|
|
swap(loc_prop, swapped_loc_prop);
|
|
|
|
// then, expand (3)
|
|
secondMark--;
|
|
}
|
|
|
|
// increment the location's number of owners
|
|
loc_prop.second++;
|
|
} else if (p.second) {
|
|
// this location has been picked for ST(s) and/or LD(s)
|
|
// it must be in region (2)
|
|
assert(inSecondRegion(loc_prop.first) && loc_prop.second == 0);
|
|
|
|
// pick the first location in (2) to swap
|
|
Location swapped_loc = locArray[firstMark];
|
|
LocProperty& swapped_loc_prop =
|
|
locProps[swapped_loc - locationBase];
|
|
|
|
// swap loc and swapped_loc
|
|
swap(loc_prop, swapped_loc_prop);
|
|
|
|
// then, expand (1)
|
|
firstMark++;
|
|
|
|
// increment the location's number of owners
|
|
loc_prop.second++;
|
|
} else {
|
|
panic("Location in loadStoreMap but wasn't picked in any"
|
|
" action\n");
|
|
}
|
|
}
|
|
|
|
// clear the ld_st_map
|
|
loadStoreMap.clear();
|
|
}
|
|
|
|
void
|
|
AddressManager::AtomicStruct::releaseLoc(Location loc)
|
|
{
|
|
assert(loc >= locationBase && loc < locationBase + arraySize);
|
|
|
|
LocProperty& loc_prop = locProps[loc - locationBase];
|
|
|
|
if (inFirstRegion(loc_prop.first)) {
|
|
// this location must have exactly 1 owner
|
|
assert(loc_prop.second == 1);
|
|
|
|
// pick the last location in region 1 to swap
|
|
Location swapped_loc = locArray[firstMark - 1];
|
|
LocProperty& swapped_loc_prop = locProps[swapped_loc - locationBase];
|
|
|
|
// swap loc and swapped_loc
|
|
swap(loc_prop, swapped_loc_prop);
|
|
|
|
// then shrink (1)
|
|
firstMark--;
|
|
|
|
// reset the location's number of owners
|
|
loc_prop.second = 0;
|
|
} else if (inThirdRegion(loc_prop.first)) {
|
|
// this location must have at least 1 owner
|
|
assert(loc_prop.second >= 1);
|
|
|
|
if (loc_prop.second == 1) {
|
|
// pick the first location in region 3 to swap
|
|
Location swapped_loc = locArray[secondMark];
|
|
LocProperty& swapped_loc_prop =
|
|
locProps[swapped_loc - locationBase];
|
|
|
|
// swap loc and swapped_loc
|
|
swap(loc_prop, swapped_loc_prop);
|
|
|
|
// then shrink (3)
|
|
secondMark++;
|
|
}
|
|
// decrement the loc's number of owners
|
|
loc_prop.second--;
|
|
} else {
|
|
// some one else must already reset this counter
|
|
assert(inSecondRegion(loc_prop.first) && loc_prop.second == 0);
|
|
}
|
|
}
|
|
|
|
bool
|
|
AddressManager::AtomicStruct::isExpectedValue(Value val)
|
|
{
|
|
ExpectedValueSet::iterator it = expectedValues.find(val);
|
|
|
|
if (it == expectedValues.end()) {
|
|
std::stringstream exp_val_ss;
|
|
for (auto& val : expectedValues) {
|
|
exp_val_ss << " " << val;
|
|
}
|
|
|
|
warn("Expected return values are:\n\t%s\n", exp_val_ss.str());
|
|
|
|
return false;
|
|
}
|
|
|
|
// erase this value b/c it's done
|
|
expectedValues.erase(it);
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
AddressManager::AtomicStruct::swap(LocProperty& prop_1, LocProperty& prop_2)
|
|
{
|
|
int new_idx_1 = prop_2.first;
|
|
int new_idx_2 = prop_1.first;
|
|
|
|
// swap the two locations in locArray
|
|
Location tmp = locArray[prop_1.first];
|
|
locArray[prop_1.first] = locArray[prop_2.first];
|
|
locArray[prop_2.first] = tmp;
|
|
|
|
// update their new indices
|
|
prop_1.first = new_idx_1;
|
|
prop_2.first = new_idx_2;
|
|
}
|
|
|
|
// ------------------ log table ---------------------
|
|
void
|
|
AddressManager::updateLogTable(Location loc, int thread_id, int episode_id,
|
|
Value new_value, Tick cur_tick, int cu_id)
|
|
{
|
|
assert(loc >= 0 && loc < numAtomicLocs + numNormalLocs);
|
|
logTable[loc]->update(thread_id, cu_id, episode_id, new_value, cur_tick);
|
|
}
|
|
|
|
AddressManager::Value
|
|
AddressManager::getLoggedValue(Location loc) const
|
|
{
|
|
assert(loc >= 0 && loc < numAtomicLocs + numNormalLocs);
|
|
return logTable[loc]->getLastStoredValue();
|
|
}
|
|
|
|
bool
|
|
AddressManager::validateAtomicResp(Location loc, Value ret_val)
|
|
{
|
|
assert(loc >= 0 && loc < numAtomicLocs);
|
|
return atomicStructs[loc]->isExpectedValue(ret_val);
|
|
}
|