blob: 10ce070419c05b8d64ccadd0dabe59cdc18c7864 [file] [log] [blame]
/*
* 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/episode.hh"
#include <fstream>
#include <unordered_set>
#include "cpu/testers/gpu_ruby_test/gpu_thread.hh"
#include "cpu/testers/gpu_ruby_test/protocol_tester.hh"
Episode::Episode(ProtocolTester* _tester, GpuThread* _thread, int num_loads,
int num_stores)
: tester(_tester),
thread(_thread),
numLoads(num_loads),
numStores(num_stores),
nextActionIdx(0)
{
assert(tester && thread);
episodeId = tester->getNextEpisodeID();
numLanes = thread->getNumLanes();
assert(numLanes > 0);
addrManager = tester->getAddressManager();
assert(addrManager);
atomicLocs.resize(numLanes, AddressManager::INVALID_LOCATION);
// generate a sequence of actions
initActions();
isActive = true;
DPRINTFN("Episode %d\n", episodeId);
}
Episode::~Episode()
{
for (Episode::Action* action : actions) {
assert(action);
delete action;
}
}
const Episode::Action*
Episode::peekCurAction() const
{
if (nextActionIdx < actions.size())
return actions[nextActionIdx];
else
return nullptr;
}
void
Episode::popAction()
{
assert(nextActionIdx < actions.size());
nextActionIdx++;
}
void
Episode::initActions()
{
// first, push Atomic & then Acquire action
actions.push_back(new Action(Action::Type::ATOMIC, numLanes));
actions.push_back(new Action(Action::Type::ACQUIRE, numLanes));
// second, push a number of LD/ST actions
int num_loads = numLoads;
int num_stores = numStores;
while ((num_loads + num_stores) > 0) {
switch (random() % 2) {
case 0: // Load
if (num_loads > 0) {
actions.push_back(new Action(Action::Type::LOAD,
numLanes));
num_loads--;
}
break;
case 1: // Store
if (num_stores > 0) {
actions.push_back(new Action(Action::Type::STORE,
numLanes));
num_stores--;
}
break;
default:
assert(false);
}
}
// last, push an Release & then Atomic action
actions.push_back(new Action(Action::Type::RELEASE, numLanes));
actions.push_back(new Action(Action::Type::ATOMIC, numLanes));
// for each lane, pick a list of locations
Location normal_loc;
for (int lane = 0; lane < numLanes; ++lane) {
normal_loc = AddressManager::INVALID_LOCATION;
// first, we select atomic loc for this lane
// atomic loc for this lane should not have been picked yet
assert(atomicLocs[lane] == AddressManager::INVALID_LOCATION);
// pick randomly an atomic location
atomicLocs[lane] = addrManager->getAtomicLoc();
assert(atomicLocs[lane] >= 0);
// go through each action in this lane and set its location
for (Action* action : actions) {
assert(action);
switch (action->getType()) {
case Action::Type::ATOMIC:
action->setLocation(lane, atomicLocs[lane]);
break;
case Action::Type::LOAD:
// pick randomly a normal location
normal_loc = addrManager->
getLoadLoc(atomicLocs[lane]);
assert(normal_loc >= AddressManager::INVALID_LOCATION);
if (normal_loc != AddressManager::INVALID_LOCATION) {
// check DRF
if (!tester->checkDRF(atomicLocs[lane],
normal_loc, false) ||
!this->checkDRF(atomicLocs[lane], normal_loc,
false, lane)) {
panic("GpuTh %d - Data race detected. STOPPED!\n",
thread->getGpuThreadId());
}
}
action->setLocation(lane, normal_loc);
break;
case Action::Type::STORE:
// pick randomly a normal location
normal_loc = addrManager->
getStoreLoc(atomicLocs[lane]);
assert(normal_loc >= AddressManager::INVALID_LOCATION);
if (normal_loc != AddressManager::INVALID_LOCATION) {
// check DRF
if (!tester->checkDRF(atomicLocs[lane],
normal_loc, true) ||
!this->checkDRF(atomicLocs[lane], normal_loc,
true, lane)) {
panic("GpuTh %d - Data race detected. STOPPED!\n",
thread->getGpuThreadId());
}
}
action->setLocation(lane, normal_loc);
break;
case Action::Type::ACQUIRE:
case Action::Type::RELEASE:
// no op
break;
default:
panic("Invalid action type\n");
}
}
addrManager->finishLocSelection(atomicLocs[lane]);
}
}
void
Episode::completeEpisode()
{
// release all locations this episode has picked and used
Location atomic_loc, normal_loc;
for (int lane = 0; lane < numLanes; ++lane) {
atomic_loc = AddressManager::INVALID_LOCATION;
normal_loc = AddressManager::INVALID_LOCATION;
std::unordered_set<Location> unique_loc_set;
for (Action* action : actions) {
assert(action);
if (action->isAtomicAction()) {
if (atomic_loc == AddressManager::INVALID_LOCATION) {
atomic_loc = action->getLocation(lane);
} else {
// both atomic ops in the same lane must be
// at the same location
assert(atomic_loc == action->getLocation(lane));
}
} else if (!action->isMemFenceAction()) {
assert(atomic_loc >= 0);
normal_loc = action->getLocation(lane);
if (normal_loc >= 0)
unique_loc_set.insert(normal_loc);
}
}
// each unique loc can be released only once
for (Location loc : unique_loc_set)
addrManager->releaseLocation(atomic_loc, loc);
}
// this episode is no longer active
isActive = false;
}
bool
Episode::checkDRF(Location atomic_loc, Location loc, bool isStore,
int max_lane) const
{
assert(atomic_loc != AddressManager::INVALID_LOCATION);
assert(loc != AddressManager::INVALID_LOCATION);
assert(max_lane <= numLanes);
for (int lane = 0; lane < max_lane; ++lane) {
if (atomic_loc == atomicLocs[lane]) {
for (const Action* action : actions) {
if (!action->isAtomicAction() &&
!action->isMemFenceAction()) {
if (isStore && loc == action->getLocation(lane)) {
warn("ST at location %d races against thread %d\n",
loc, thread->getGpuThreadId());
return false;
} else if (!isStore &&
action->getType() == Action::Type::STORE &&
loc == action->getLocation(lane)) {
warn("LD at location %d races against thread %d\n",
loc, thread->getGpuThreadId());
return false;
}
}
}
}
}
return true;
}
// -------------------- Action class ----------------------------
Episode::Action::Action(Type t, int num_lanes)
: type(t),
numLanes(num_lanes)
{
assert(numLanes > 0);
locations.resize(numLanes);
for (Location &loc : locations) loc = AddressManager::INVALID_LOCATION;
}
void
Episode::Action::setLocation(int lane, Location loc)
{
assert(lane >= 0 && lane < numLanes);
locations[lane] = loc;
}
AddressManager::Location
Episode::Action::getLocation(int lane) const
{
assert(lane >= 0 && lane < numLanes);
return locations[lane];
}
bool
Episode::Action::isAtomicAction() const
{
return (type == Type::ATOMIC);
}
bool
Episode::Action::isMemFenceAction() const
{
return (type == Type::ACQUIRE || type == Type::RELEASE);
}
const std::string
Episode::Action::printType() const
{
if (type == Type::ACQUIRE)
return "ACQUIRE";
else if (type == Type::RELEASE)
return "RELEASE";
else if (type == Type::ATOMIC)
return "ATOMIC";
else if (type == Type::LOAD)
return "LOAD";
else if (type == Type::STORE)
return "STORE";
else
panic("Invalid action type\n");
}