15 #include <spirv/unified1/spirv.h>
23 #define BUFFER_BITS (16)
26 #define OFFSET_BITS (64 - BUFFER_BITS)
29 #define LOCK_ATOMIC_MUTEX(Address) \
30 if (this->Scope == MemoryScope::Device) \
31 AtomicMutexes[Address % NUM_ATOMIC_MUTEXES].lock()
32 #define UNLOCK_ATOMIC_MUTEX(Address) \
33 if (this->Scope == MemoryScope::Device) \
34 AtomicMutexes[Address % NUM_ATOMIC_MUTEXES].unlock()
48 for (
size_t Id = 1; Id <
Buffers.size(); Id++)
54 std::lock_guard<std::mutex> Lock(
Mutex);
59 B.
Data =
new uint8_t[NumBytes];
82 uint32_t Semantics, T Value)
84 assert(
sizeof(T) == 4);
90 std::stringstream Err;
91 Err <<
"Invalid atomic access of 4 bytes"
92 <<
" at address 0x" << std::hex << Address <<
" ("
101 uint64_t Offset = (Address & (((uint64_t)-1) >>
BUFFER_BITS));
102 T *Pointer = (T *)(
Buffers[Id].Data + Offset);
107 T OldValue = *Pointer;
111 *Pointer = OldValue & Value;
113 case SpvOpAtomicExchange:
116 case SpvOpAtomicIAdd:
117 *Pointer = OldValue + Value;
119 case SpvOpAtomicIDecrement:
120 *Pointer = OldValue - 1;
122 case SpvOpAtomicIIncrement:
123 *Pointer = OldValue + 1;
125 case SpvOpAtomicISub:
126 *Pointer = OldValue - Value;
128 case SpvOpAtomicLoad:
131 *Pointer = OldValue | Value;
133 case SpvOpAtomicSMax:
134 case SpvOpAtomicUMax:
135 *Pointer = std::max(OldValue, Value);
137 case SpvOpAtomicSMin:
138 case SpvOpAtomicUMin:
139 *Pointer = std::min(OldValue, Value);
141 case SpvOpAtomicStore:
145 *Pointer = OldValue ^ Value;
157 uint32_t EqualSemantics,
158 uint32_t UnequalSemantics, uint32_t Value,
167 std::stringstream Err;
168 Err <<
"Invalid atomic access of 4 bytes"
169 <<
" at address 0x" << std::hex << Address <<
" ("
178 uint64_t Offset = (Address & (((uint64_t)-1) >>
BUFFER_BITS));
179 uint32_t *Pointer = (uint32_t *)(
Buffers[Id].Data + Offset);
184 uint32_t OldValue = *Pointer;
185 if (OldValue == Comparator)
204 for (
size_t Id = 1; Id <
Buffers.size(); Id++)
217 std::cerr <<
"Memory::dump() invalid address: " << Address << std::endl;
221 for (uint64_t i = 0; i <
Buffers[Id].NumBytes; i++)
225 std::cout << std::endl
226 << std::hex << std::uppercase << std::setw(16)
227 << std::setfill(
' ') << std::right
230 std::cout <<
" " << std::hex << std::uppercase << std::setw(2)
231 << std::setfill(
'0') << (int)
Buffers[Id].Data[i];
233 std::cout << std::endl;
239 uint64_t Offset = (Address & (((uint64_t)-1) >>
BUFFER_BITS));
244 if ((Offset + NumBytes) >
Buffers[Id].NumBytes)
249 void Memory::load(uint8_t *Data, uint64_t Address, uint64_t NumBytes)
const
252 uint64_t Offset = (Address & (((uint64_t)-1) >>
BUFFER_BITS));
258 std::stringstream Err;
259 Err <<
"Invalid load of " << NumBytes <<
" bytes"
260 <<
" from address 0x" << std::hex << Address <<
" ("
265 memset(Data, 0, NumBytes);
270 memcpy(Data,
Buffers[Id].Data + Offset, NumBytes);
273 uint8_t *
Memory::map(uint64_t Base, uint64_t Offset, uint64_t NumBytes)
281 std::stringstream Err;
282 Err <<
"Invalid mapping of " << NumBytes <<
" bytes"
283 <<
" from address 0x" << std::hex << (Base + Offset) <<
" ("
289 return Buffers[Id].Data + Offset;
294 std::lock_guard<std::mutex> Lock(
Mutex);
297 assert(
Buffers[Id].Data !=
nullptr);
306 void Memory::store(uint64_t Address, uint64_t NumBytes,
const uint8_t *Data)
309 uint64_t Offset = (Address & (((uint64_t)-1) >>
BUFFER_BITS));
315 std::stringstream Err;
316 Err <<
"Invalid store of " << NumBytes <<
" bytes"
317 <<
" to address 0x" << std::hex << Address <<
" ("
323 memcpy(
Buffers[Id].Data + Offset, Data, NumBytes);
329 const Memory &SrcMem, uint64_t NumBytes)
332 uint64_t SrcOffset = (SrcAddress & (((uint64_t)-1) >>
BUFFER_BITS));
338 std::stringstream Err;
339 Err <<
"Invalid load of " << NumBytes <<
" bytes"
340 <<
" from address 0x" << std::hex << SrcAddress <<
" ("
346 DstMem.
store(DstAddress, NumBytes, SrcMem.
Buffers[SrcId].Data + SrcOffset);
350 template uint32_t
Memory::atomic(uint64_t Address, uint32_t Opcode,
351 uint32_t Scope, uint32_t Semantics,
353 template int32_t
Memory::atomic(uint64_t Address, uint32_t Opcode,
354 uint32_t Scope, uint32_t Semantics,
uint8_t * map(uint64_t Base, uint64_t Offset, uint64_t NumBytes)
Map a region of memory and return a pointer to it.
void reportError(const std::string &Error, bool Fatal=false)
Report an error that has occurred during emulation.
#define OFFSET_BITS
Number of bits used for the address offset.
This file declares the Device class.
#define UNLOCK_ATOMIC_MUTEX(Address)
void unmap(uint64_t Base)
Unmap a previously mapped region of memory.
void dump() const
Dump the entire contents of this memory to stdout.
static void copy(uint64_t DstAddress, Memory &DstMem, uint64_t SrcAddress, const Memory &SrcMem, uint64_t NumBytes)
Copy data between memory instances.
uint32_t atomicCmpXchg(uint64_t Address, uint32_t Scope, uint32_t EqualSemantics, uint32_t UnequalSemantics, uint32_t Value, uint32_t Comparator)
Perform an atomic compare-exchange operation at Address.
MemoryScope
Describes the scope of a memory instance.
void release(uint64_t Address)
Release the allocation with base address Address.
uint64_t NumBytes
The size of the allocation in bytes.
void reportMemoryMap(const Memory *Mem, uint64_t Base, uint64_t Offset, uint64_t NumBytes)
#define LOCK_ATOMIC_MUTEX(Address)
#define BUFFER_BITS
Number of bits used for the buffer ID.
T atomic(uint64_t Address, uint32_t Opcode, uint32_t Scope, uint32_t Semantics, T Value=0)
Atomically apply operation defined by Opcode to Address.
This class represents an address space in the virtual device.
std::vector< uint64_t > FreeBuffers
Base addresses available for reuse.
std::mutex Mutex
Mutex for guarding allocate/release operations.
A Device instance encapsulates properties and state for the virtual device.
This file declares the Memory class.
void reportMemoryStore(const Memory *Mem, uint64_t Address, uint64_t NumBytes, const uint8_t *Data)
void load(uint8_t *Result, uint64_t Address, uint64_t NumBytes) const
Load NumBytes of data from Address into Result.
MemoryScope Scope
The scope of this memory instance.
void reportAtomicAccess(const Memory *Mem, uint64_t Address, uint64_t NumBytes, uint32_t Opcode, uint32_t Scope, uint32_t Semantics)
uint64_t allocate(uint64_t NumBytes)
Allocate a new buffer of size NumBytes.
void store(uint64_t Address, uint64_t NumBytes, const uint8_t *Data)
Store NumBytes of data from Data to Address.
An allocation within this memory instance.
void reportMemoryLoad(const Memory *Mem, uint64_t Address, uint64_t NumBytes)
static const char * scopeToString(MemoryScope Scope)
Returns the string representation of Scope.
Memory(Device &D, MemoryScope Scope)
Create a new Memory instance.
bool isAccessValid(uint64_t Address, uint64_t NumBytes) const
Check whether an access resides in an allocated region of memory.
uint8_t * Data
The raw data backing the allocation.
std::vector< Buffer > Buffers
List of allocations.
Device & Dev
The device this memory instance is part of.
void reportMemoryUnmap(const Memory *Mem, uint64_t Base)