<commit_before>// Memory.cpp (Oclgrind)
// Copyright (c) 2013-2015, <NAME> and <NAME>,
// University of Bristol. All rights reserved.
//
// This program is provided under a three-clause BSD license. For full
// license terms please see the LICENSE file distributed with this
// source code.

#include "common.h"
#include <cassert>
#include <cmath>
#include <cstring>
#include <mutex>

#include "Context.h"
#include "Memory.h"
#include "WorkGroup.h"
#include "WorkItem.h"

using namespace oclgrind;
using namespace std;

mutex atomicMutex;

Memory::Memory(unsigned int addrSpace, const Context *context)
{
  m_context = context;
  m_addressSpace = addrSpace;

  clear();
}

Memory::~Memory()
{
  clear();
}

size_t Memory::allocateBuffer(size_t size, cl_mem_flags flags)
{
  // Check requested size doesn't exceed maximum
  if (size > MAX_BUFFER_SIZE)
  {
    return 0;
  }

  // Find first unallocated buffer slot
  unsigned b = getNextBuffer();
  if (b >= MAX_NUM_BUFFERS)
  {
    return 0;
  }

  // Create buffer
  Buffer *buffer = new Buffer;
  buffer->size   = size;
  buffer->flags  = flags;
  buffer->data   = new unsigned char[size];

  // Initialize contents to 0
  memset(buffer->data, 0, size);

  if (b >= m_memory.size())
  {
    m_memory.push_back(buffer);
  }
  else
  {
    m_memory[b] = buffer;
  }

  m_totalAllocated += size;

  size_t address = ((size_t)b) << NUM_ADDRESS_BITS;

  m_context->notifyMemoryAllocated(this, address, size, flags);

  return address;
}

uint32_t Memory::atomic(AtomicOp op, size_t address, uint32_t value)
{
  m_context->notifyMemoryAtomicLoad(this, op, address, 4);
  m_context->notifyMemoryAtomicStore(this, op, address, 4);

  // Bounds check
  if (!isAddressValid(address, 4))
  {
    return 0;
  }

  // Get buffer
  size_t offset = EXTRACT_OFFSET(address);
  Buffer *buffer = m_memory[EXTRACT_BUFFER(address)];
  uint32_t *ptr = (uint32_t*)(buffer->data + offset);

  if (m_addressSpace == AddrSpaceGlobal)
    atomicMutex.lock();

  uint32_t old = *ptr;
  switch(op)
  {
  case AtomicAdd:
    *ptr = old + value;
    break;
  case AtomicAnd:
    *ptr = old & value;
    break;
  case AtomicCmpXchg:
    FATAL_ERROR("AtomicCmpXchg in generic atomic handler");
    break;
  case AtomicDec:
    *ptr = old - 1;
    break;
  case AtomicInc:
    *ptr = old + 1;
    break;
  case AtomicMax:
    *ptr = old > value ? old : value;
    break;
  case AtomicMin:
    *ptr = old < value ? old : value;
    break;
  case AtomicOr:
    *ptr = old | value;
    break;
  case AtomicSub:
    *ptr = old - value;
    break;
  case AtomicXchg:
    *ptr = value;
    break;
  case AtomicXor:
    *ptr = old ^ value;
    break;
  }

  if (m_addressSpace == AddrSpaceGlobal)
    atomicMutex.unlock();

  return old;
}

uint32_t Memory::atomicCmpxchg(size_t address, uint32_t cmp, uint32_t value)
{
  m_context->notifyMemoryAtomicLoad(this, AtomicCmpXchg, address, 4);

  // Bounds check
  if (!isAddressValid(address, 4))
  {
    return 0;
  }

  // Get buffer
  size_t offset = EXTRACT_OFFSET(address);
  Buffer *buffer = m_memory[EXTRACT_BUFFER(address)];
  uint32_t *ptr = (uint32_t*)(buffer->data + offset);

  if (m_addressSpace == AddrSpaceGlobal)
    atomicMutex.lock();

  // Perform cmpxchg
  uint32_t old = *ptr;
  if (old == cmp)
  {
    *ptr = value;

    m_context->notifyMemoryAtomicStore(this, AtomicCmpXchg, address, 4);
  }

  if (m_addressSpace == AddrSpaceGlobal)
    atomicMutex.unlock();

  return old;
}

void Memory::clear()
{
  vector<Buffer*>::iterator itr;
  for (itr = m_memory.begin(); itr != m_memory.end(); itr++)
  {
    if (*itr)
    {
      if (!((*itr)->flags & CL_MEM_USE_HOST_PTR))
      {
        delete[] (*itr)->data;
      }
      delete *itr;

      size_t address = (itr-m_memory.begin())<<NUM_ADDRESS_BITS;
      m_context->notifyMemoryDeallocated(this, address);
    }
  }
  m_memory.resize(1);
  m_memory[0] = NULL;
  m_freeBuffers = queue<unsigned>();<commit_msg>Use multiple atomic mutexes to avoid unnecessary synchronisation.<commit_after>// Memory.cpp (Oclgrind)
// Copyright (c) 2013-2015, <NAME> and <NAME>,
// University of Bristol. All rights reserved.
//
// This program is provided under a three-clause BSD license. For full
// license terms please see the LICENSE file distributed with this
// source code.

#include "common.h"
#include <cassert>
#include <cmath>
#include <cstring>
#include <mutex>

#include "Context.h"
#include "Memory.h"
#include "WorkGroup.h"
#include "WorkItem.h"

using namespace oclgrind;
using namespace std;

// Multiple mutexes to mitigate risk of unnecessary synchronisation in atomics
#define NUM_ATOMIC_MUTEXES 64 // Must be power of two
mutex atomicMutex[NUM_ATOMIC_MUTEXES];
#define ATOMIC_MUTEX(offset) \
  atomicMutex[(((offset)>>2) & (NUM_ATOMIC_MUTEXES-1))]

Memory::Memory(unsigned int addrSpace, const Context *context)
{
  m_context = context;
  m_addressSpace = addrSpace;

  clear();
}

Memory::~Memory()
{
  clear();
}

size_t Memory::allocateBuffer(size_t size, cl_mem_flags flags)
{
  // Check requested size doesn't exceed maximum
  if (size > MAX_BUFFER_SIZE)
  {
    return 0;
  }

  // Find first unallocated buffer slot
  unsigned b = getNextBuffer();
  if (b >= MAX_NUM_BUFFERS)
  {
    return 0;
  }

  // Create buffer
  Buffer *buffer = new Buffer;
  buffer->size   = size;
  buffer->flags  = flags;
  buffer->data   = new unsigned char[size];

  // Initialize contents to 0
  memset(buffer->data, 0, size);

  if (b >= m_memory.size())
  {
    m_memory.push_back(buffer);
  }
  else
  {
    m_memory[b] = buffer;
  }

  m_totalAllocated += size;

  size_t address = ((size_t)b) << NUM_ADDRESS_BITS;

  m_context->notifyMemoryAllocated(this, address, size, flags);

  return address;
}

uint32_t Memory::atomic(AtomicOp op, size_t address, uint32_t value)
{
  m_context->notifyMemoryAtomicLoad(this, op, address, 4);
  m_context->notifyMemoryAtomicStore(this, op, address, 4);

  // Bounds check
  if (!isAddressValid(address, 4))
  {
    return 0;
  }

  // Get buffer
  size_t offset = EXTRACT_OFFSET(address);
  Buffer *buffer = m_memory[EXTRACT_BUFFER(address)];
  uint32_t *ptr = (uint32_t*)(buffer->data + offset);

  if (m_addressSpace == AddrSpaceGlobal)
    ATOMIC_MUTEX(offset).lock();

  uint32_t old = *ptr;
  switch(op)
  {
  case AtomicAdd:
    *ptr = old + value;
    break;
  case AtomicAnd:
    *ptr = old & value;
    break;
  case AtomicCmpXchg:
    FATAL_ERROR("AtomicCmpXchg in generic atomic handler");
    break;
  case AtomicDec:
    *ptr = old - 1;
    break;
  case AtomicInc:
    *ptr = old + 1;
    break;
  case AtomicMax:
    *ptr = old > value ? old : value;
    break;
  case AtomicMin:
    *ptr = old < value ? old : value;
    break;
  case AtomicOr:
    *ptr = old | value;
    break;
  case AtomicSub:
    *ptr = old - value;
    break;
  case AtomicXchg:
    *ptr = value;
    break;
  case AtomicXor:
    *ptr = old ^ value;
    break;
  }

  if (m_addressSpace == AddrSpaceGlobal)
    ATOMIC_MUTEX(offset).unlock();

  return old;
}

uint32_t Memory::atomicCmpxchg(size_t address, uint32_t cmp, uint32_t value)
{
  m_context->notifyMemoryAtomicLoad(this, AtomicCmpXchg, address, 4);

  // Bounds check
  if (!isAddressValid(address, 4))
  {
    return 0;
  }

  // Get buffer
  size_t offset = EXTRACT_OFFSET(address);
  Buffer *buffer = m_memory[EXTRACT_BUFFER(address)];
  uint32_t *ptr = (uint32_t*)(buffer->data + offset);

  if (m_addressSpace == AddrSpaceGlobal)
    ATOMIC_MUTEX(offset).lock();

  // Perform cmpxchg
  uint32_t old = *ptr;
  if (old == cmp)
  {
    *ptr = value;

    m_context->notifyMemoryAtomicStore(this, AtomicCmpXchg, address, 4);
  }

  if (m_addressSpace == AddrSpaceGlobal)
    ATOMIC_MUTEX(offset).unlock();

  return old;
}

void Memory::clear()
{
  vector<Buffer*>::iterator itr;
  for (itr = m_memory.begin(); itr != m_memory.end(); itr++)
  {
    if (*itr)
    {
      if (!((*itr)->flags & CL_MEM_USE_HOST_PTR))
      {
        delete[] (*itr)->data;
      }
      delete *itr;

      size_t address = (itr-m_memory.begin())<<NUM_ADDRESS_BITS;
      m_context->notifyMemoryDeallocated(this, address);
    }
  }
  m_memory.resize(1);
  m_memory[0] = NULL;
  m_freeBuffers = queue<unsigned>();
{'fraction_non_alphanumeric': 0.13050888726564402, 'fraction_numerical': 0.01168736303871439, 'mean_word_length': 3.3673577884104198, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '766794', 'n_tokens_mistral': 3076, 'n_tokens_neox': 2955, 'n_words': 724}