2020-12-12 04:30:20 +11:00
|
|
|
// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
|
|
|
|
|
|
|
|
layout(set = 0, binding = 0) buffer Memory {
|
|
|
|
// offset into memory of the next allocation, initialized by the user.
|
|
|
|
uint mem_offset;
|
2020-12-24 22:00:53 +11:00
|
|
|
// mem_error tracks the status of memory accesses, initialized to NO_ERROR
|
|
|
|
// by the user. ERR_MALLOC_FAILED is reported for insufficient memory.
|
|
|
|
// If MEM_DEBUG is defined the following errors are reported:
|
|
|
|
// - ERR_OUT_OF_BOUNDS is reported for out of bounds writes.
|
|
|
|
// - ERR_UNALIGNED_ACCESS for memory access not aligned to 32-bit words.
|
|
|
|
uint mem_error;
|
2020-12-12 04:30:20 +11:00
|
|
|
uint[] memory;
|
|
|
|
};
|
|
|
|
|
2020-12-24 22:00:53 +11:00
|
|
|
// Uncomment this line to add the size field to Alloc and enable memory checks.
|
|
|
|
// Note that the Config struct in setup.h grows size fields as well.
|
|
|
|
//#define MEM_DEBUG
|
|
|
|
|
|
|
|
#define NO_ERROR 0
|
|
|
|
#define ERR_MALLOC_FAILED 1
|
|
|
|
#define ERR_OUT_OF_BOUNDS 2
|
|
|
|
#define ERR_UNALIGNED_ACCESS 3
|
|
|
|
|
|
|
|
#define Alloc_size 8
|
|
|
|
|
2020-12-12 04:30:20 +11:00
|
|
|
// Alloc represents a memory allocation.
|
|
|
|
struct Alloc {
|
|
|
|
// offset in bytes into memory.
|
|
|
|
uint offset;
|
2020-12-24 22:00:53 +11:00
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
// size in bytes of the allocation.
|
|
|
|
uint size;
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
struct MallocResult {
|
|
|
|
Alloc alloc;
|
2020-12-12 04:30:20 +11:00
|
|
|
// failed is true if the allocation overflowed memory.
|
|
|
|
bool failed;
|
|
|
|
};
|
|
|
|
|
2020-12-24 22:00:53 +11:00
|
|
|
// new_alloc synthesizes an Alloc when its offset and size are derived.
|
|
|
|
Alloc new_alloc(uint offset, uint size) {
|
2020-12-12 04:30:20 +11:00
|
|
|
Alloc a;
|
2020-12-24 22:00:53 +11:00
|
|
|
a.offset = offset;
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
a.size = size;
|
|
|
|
#endif
|
2020-12-12 04:30:20 +11:00
|
|
|
return a;
|
|
|
|
}
|
2020-12-24 22:00:53 +11:00
|
|
|
|
|
|
|
// malloc allocates size bytes of memory.
|
|
|
|
MallocResult malloc(uint size) {
|
|
|
|
MallocResult r;
|
|
|
|
r.failed = false;
|
|
|
|
uint offset = atomicAdd(mem_offset, size);
|
|
|
|
r.alloc = new_alloc(offset, size);
|
|
|
|
if (offset + size > memory.length() * 4) {
|
|
|
|
r.failed = true;
|
|
|
|
atomicMax(mem_error, ERR_MALLOC_FAILED);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
if ((size & 3) != 0) {
|
|
|
|
r.failed = true;
|
|
|
|
atomicMax(mem_error, ERR_UNALIGNED_ACCESS);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
// touch_mem checks whether access to the memory word at offset is valid.
|
|
|
|
// If MEM_DEBUG is defined, touch_mem returns false if offset is out of bounds.
|
|
|
|
// Offset is in words.
|
|
|
|
bool touch_mem(Alloc alloc, uint offset) {
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
if (offset < alloc.offset/4 || offset >= (alloc.offset + alloc.size)/4) {
|
|
|
|
atomicMax(mem_error, ERR_OUT_OF_BOUNDS);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// write_mem writes val to memory at offset.
|
|
|
|
// Offset is in words.
|
|
|
|
void write_mem(Alloc alloc, uint offset, uint val) {
|
|
|
|
if (!touch_mem(alloc, offset)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
memory[offset] = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
// read_mem reads the value from memory at offset.
|
|
|
|
// Offset is in words.
|
|
|
|
uint read_mem(Alloc alloc, uint offset) {
|
|
|
|
if (!touch_mem(alloc, offset)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
uint v = memory[offset];
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
// slice_mem returns a sub-allocation inside another. Offset and size are in
|
|
|
|
// bytes, relative to a.offset.
|
|
|
|
Alloc slice_mem(Alloc a, uint offset, uint size) {
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
if ((offset & 3) != 0 || (size & 3) != 0) {
|
|
|
|
atomicMax(mem_error, ERR_UNALIGNED_ACCESS);
|
|
|
|
return Alloc(0, 0);
|
|
|
|
}
|
|
|
|
if (offset + size > a.size) {
|
|
|
|
// slice_mem is sometimes used for slices outside bounds,
|
|
|
|
// but never written.
|
|
|
|
return Alloc(0, 0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return new_alloc(a.offset + offset, size);
|
|
|
|
}
|