mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
0.5.5 features (#1151)
0.5.5 Disallow multiple `_` in a row in digits, e.g. `1__000`. #1138. Fixed toposort example. Struct/union members now correctly rejects members without storage size #1147. `math::pow` will now correctly promote integer arguments. `math::pow` will now correctly promote integer arguments. Added `new_aligned` and `alloc_aligned` functions to prevent accidental under-alignment when allocating simd. Pointer difference would fail where alignment != size (structs etc) #1150. Add test that overalignment actually works for lists. Fixed array calculation for npot2 vectors. Use native aligned alloc on Windows and POSIX. Deprecates "offset". Simplification of the Allocator interface.
This commit is contained in:
committed by
GitHub
parent
b7f4fd9074
commit
7ea3d230bb
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@@ -265,7 +265,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Compile and run some examples
|
- name: Compile and run some examples
|
||||||
run: |
|
run: |
|
||||||
cd resources
|
cd resources
|
||||||
../build/c3c compile examples/base64.c3
|
../build/c3c compile examples/base64.c3
|
||||||
../build/c3c compile examples/binarydigits.c3
|
../build/c3c compile examples/binarydigits.c3
|
||||||
../build/c3c compile examples/brainfk.c3
|
../build/c3c compile examples/brainfk.c3
|
||||||
@@ -285,7 +285,7 @@ jobs:
|
|||||||
../build/c3c compile examples/contextfree/dynscope.c3
|
../build/c3c compile examples/contextfree/dynscope.c3
|
||||||
../build/c3c compile examples/contextfree/guess_number.c3
|
../build/c3c compile examples/contextfree/guess_number.c3
|
||||||
../build/c3c compile examples/contextfree/multi.c3
|
../build/c3c compile examples/contextfree/multi.c3
|
||||||
../build/c3c compile examples/contextfree/cleanup.c3
|
../build/c3c compile examples/contextfree/cleanup.c3
|
||||||
../build/c3c compile-run examples/hello_world_many.c3
|
../build/c3c compile-run examples/hello_world_many.c3
|
||||||
../build/c3c compile-run examples/time.c3
|
../build/c3c compile-run examples/time.c3
|
||||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||||
|
|||||||
@@ -30,9 +30,11 @@ struct ArenaAllocatorHeader @local
|
|||||||
char[*] data;
|
char[*] data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @require ptr != null
|
||||||
|
**/
|
||||||
fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
||||||
{
|
{
|
||||||
if (!ptr) return;
|
|
||||||
assert((uptr)ptr >= (uptr)self.data.ptr, "Pointer originates from a different allocator.");
|
assert((uptr)ptr >= (uptr)self.data.ptr, "Pointer originates from a different allocator.");
|
||||||
ArenaAllocatorHeader* header = ptr - ArenaAllocatorHeader.sizeof;
|
ArenaAllocatorHeader* header = ptr - ArenaAllocatorHeader.sizeof;
|
||||||
// Reclaim memory if it's the last element.
|
// Reclaim memory if it's the last element.
|
||||||
@@ -41,29 +43,26 @@ fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
|||||||
self.used -= header.size + ArenaAllocatorHeader.sizeof;
|
self.used -= header.size + ArenaAllocatorHeader.sizeof;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz ArenaAllocator.mark(&self) @dynamic => self.used;
|
fn usz ArenaAllocator.mark(&self) @dynamic => self.used;
|
||||||
fn void ArenaAllocator.reset(&self, usz mark) @dynamic => self.used = mark;
|
fn void ArenaAllocator.reset(&self, usz mark) @dynamic => self.used = mark;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require !alignment || math::is_power_of_2(alignment)
|
* @require !alignment || math::is_power_of_2(alignment)
|
||||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||||
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
|
* @require size > 0
|
||||||
* @require offset <= size && offset >= 0
|
|
||||||
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
|
|
||||||
**/
|
**/
|
||||||
fn void*! ArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
|
fn void*! ArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
|
||||||
{
|
{
|
||||||
if (!size) return null;
|
|
||||||
alignment = alignment_for_allocation(alignment);
|
alignment = alignment_for_allocation(alignment);
|
||||||
usz total_len = self.data.len;
|
usz total_len = self.data.len;
|
||||||
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
|
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
|
||||||
void* start_mem = self.data.ptr;
|
void* start_mem = self.data.ptr;
|
||||||
void* unaligned_pointer_to_offset = start_mem + self.used + ArenaAllocatorHeader.sizeof + offset;
|
void* unaligned_pointer_to_offset = start_mem + self.used + ArenaAllocatorHeader.sizeof;
|
||||||
void* aligned_pointer_to_offset = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
|
void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
|
||||||
usz end = (usz)(aligned_pointer_to_offset - self.data.ptr) + size - offset;
|
usz end = (usz)(mem - self.data.ptr) + size;
|
||||||
if (end > total_len) return AllocationFailure.OUT_OF_MEMORY?;
|
if (end > total_len) return AllocationFailure.OUT_OF_MEMORY?;
|
||||||
self.used = end;
|
self.used = end;
|
||||||
void* mem = aligned_pointer_to_offset - offset;
|
|
||||||
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
|
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
|
||||||
header.size = size;
|
header.size = size;
|
||||||
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
|
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
@@ -73,21 +72,11 @@ fn void*! ArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
|
|||||||
/**
|
/**
|
||||||
* @require !alignment || math::is_power_of_2(alignment)
|
* @require !alignment || math::is_power_of_2(alignment)
|
||||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||||
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
|
* @require old_pointer != null
|
||||||
* @require offset <= size && offset >= 0
|
* @require size > 0
|
||||||
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
|
|
||||||
**/
|
**/
|
||||||
fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignment, usz offset) @dynamic
|
fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignment, usz offset) @dynamic
|
||||||
{
|
{
|
||||||
if (!size)
|
|
||||||
{
|
|
||||||
self.release(old_pointer, alignment > 0);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (!old_pointer)
|
|
||||||
{
|
|
||||||
return self.acquire(size, true, alignment, offset);
|
|
||||||
}
|
|
||||||
alignment = alignment_for_allocation(alignment);
|
alignment = alignment_for_allocation(alignment);
|
||||||
assert(old_pointer >= self.data.ptr, "Pointer originates from a different allocator.");
|
assert(old_pointer >= self.data.ptr, "Pointer originates from a different allocator.");
|
||||||
usz total_len = self.data.len;
|
usz total_len = self.data.len;
|
||||||
@@ -95,7 +84,7 @@ fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
|
|||||||
ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof;
|
ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof;
|
||||||
usz old_size = header.size;
|
usz old_size = header.size;
|
||||||
// Do last allocation and alignment match?
|
// Do last allocation and alignment match?
|
||||||
if (&self.data[self.used] == old_pointer + old_size && mem::ptr_is_aligned(old_pointer + offset, alignment))
|
if (&self.data[self.used] == old_pointer + old_size && mem::ptr_is_aligned(old_pointer, alignment))
|
||||||
{
|
{
|
||||||
if (old_size >= size)
|
if (old_size >= size)
|
||||||
{
|
{
|
||||||
@@ -111,7 +100,7 @@ fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
|
|||||||
return old_pointer;
|
return old_pointer;
|
||||||
}
|
}
|
||||||
// Otherwise just allocate new memory.
|
// Otherwise just allocate new memory.
|
||||||
void* mem = self.acquire(size, false, alignment, offset)!;
|
void* mem = self.acquire(size, false, alignment, 0)!;
|
||||||
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
@@ -59,11 +59,11 @@ struct DynamicArenaChunk @local
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @require ptr
|
||||||
* @require self.page `tried to free pointer on invalid allocator`
|
* @require self.page `tried to free pointer on invalid allocator`
|
||||||
*/
|
*/
|
||||||
fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
||||||
{
|
{
|
||||||
if (!ptr) return;
|
|
||||||
DynamicArenaPage* current_page = self.page;
|
DynamicArenaPage* current_page = self.page;
|
||||||
if (ptr == current_page.current_stack_ptr)
|
if (ptr == current_page.current_stack_ptr)
|
||||||
{
|
{
|
||||||
@@ -73,19 +73,12 @@ fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @require size > 0 `Resize doesn't support zeroing`
|
||||||
|
* @require old_pointer != null `Resize doesn't handle null pointers`
|
||||||
* @require self.page `tried to realloc pointer on invalid allocator`
|
* @require self.page `tried to realloc pointer on invalid allocator`
|
||||||
*/
|
*/
|
||||||
fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
|
fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
|
||||||
{
|
{
|
||||||
if (!size)
|
|
||||||
{
|
|
||||||
self.release(old_pointer, alignment > 0);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (!old_pointer)
|
|
||||||
{
|
|
||||||
return self.acquire(size, true, alignment, offset);
|
|
||||||
}
|
|
||||||
DynamicArenaPage* current_page = self.page;
|
DynamicArenaPage* current_page = self.page;
|
||||||
alignment = alignment_for_allocation(alignment);
|
alignment = alignment_for_allocation(alignment);
|
||||||
usz* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX;
|
usz* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX;
|
||||||
@@ -109,7 +102,7 @@ fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz a
|
|||||||
current_page.used += add_size;
|
current_page.used += add_size;
|
||||||
return old_pointer;
|
return old_pointer;
|
||||||
}
|
}
|
||||||
void* new_mem = self.acquire(size, false, alignment, offset)!;
|
void* new_mem = self.acquire(size, false, alignment, 0)!;
|
||||||
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
|
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
return new_mem;
|
return new_mem;
|
||||||
}
|
}
|
||||||
@@ -135,10 +128,10 @@ fn void DynamicArenaAllocator.reset(&self, usz mark = 0) @dynamic
|
|||||||
* @require math::is_power_of_2(alignment)
|
* @require math::is_power_of_2(alignment)
|
||||||
* @require size > 0
|
* @require size > 0
|
||||||
*/
|
*/
|
||||||
fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment, usz offset) @local
|
fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @local
|
||||||
{
|
{
|
||||||
// First, make sure that we can align it, extending the page size if needed.
|
// First, make sure that we can align it, extending the page size if needed.
|
||||||
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + offset, alignment) - offset);
|
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof, alignment));
|
||||||
|
|
||||||
// Grab the page without alignment (we do it ourselves)
|
// Grab the page without alignment (we do it ourselves)
|
||||||
void* mem = allocator::malloc_try(self.backing_allocator, page_size)!;
|
void* mem = allocator::malloc_try(self.backing_allocator, page_size)!;
|
||||||
@@ -149,7 +142,7 @@ fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment, usz o
|
|||||||
return err?;
|
return err?;
|
||||||
}
|
}
|
||||||
page.memory = mem;
|
page.memory = mem;
|
||||||
void* mem_start = mem::aligned_pointer(mem + offset + DynamicArenaChunk.sizeof, alignment) - offset;
|
void* mem_start = mem::aligned_pointer(mem + DynamicArenaChunk.sizeof, alignment);
|
||||||
assert(mem_start + size < mem + page_size);
|
assert(mem_start + size < mem + page_size);
|
||||||
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem_start - 1;
|
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem_start - 1;
|
||||||
chunk.size = size;
|
chunk.size = size;
|
||||||
@@ -162,11 +155,11 @@ fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment, usz o
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @require size > 0 `acquire expects size > 0`
|
||||||
* @require !alignment || math::is_power_of_2(alignment)
|
* @require !alignment || math::is_power_of_2(alignment)
|
||||||
*/
|
*/
|
||||||
fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
|
fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
|
||||||
{
|
{
|
||||||
if (!size) return null;
|
|
||||||
alignment = alignment_for_allocation(alignment);
|
alignment = alignment_for_allocation(alignment);
|
||||||
DynamicArenaPage* page = self.page;
|
DynamicArenaPage* page = self.page;
|
||||||
void* ptr = {|
|
void* ptr = {|
|
||||||
@@ -176,14 +169,14 @@ fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignme
|
|||||||
self.unused_page = page.prev_arena;
|
self.unused_page = page.prev_arena;
|
||||||
page.prev_arena = null;
|
page.prev_arena = null;
|
||||||
}
|
}
|
||||||
if (!page) return self._alloc_new(size, alignment, offset);
|
if (!page) return self._alloc_new(size, alignment);
|
||||||
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
|
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
|
||||||
usz new_used = start - page.memory + size;
|
usz new_used = start - page.memory + size;
|
||||||
if ALLOCATE_NEW: (new_used > page.total)
|
if ALLOCATE_NEW: (new_used > page.total)
|
||||||
{
|
{
|
||||||
if ((page = self.unused_page))
|
if ((page = self.unused_page))
|
||||||
{
|
{
|
||||||
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
|
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
|
||||||
new_used = start + size - page.memory;
|
new_used = start + size - page.memory;
|
||||||
if (page.total >= new_used)
|
if (page.total >= new_used)
|
||||||
{
|
{
|
||||||
@@ -193,7 +186,7 @@ fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignme
|
|||||||
break ALLOCATE_NEW;
|
break ALLOCATE_NEW;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self._alloc_new(size, alignment, offset);
|
return self._alloc_new(size, alignment);
|
||||||
}
|
}
|
||||||
page.used = new_used;
|
page.used = new_used;
|
||||||
assert(start + size == page.memory + page.used);
|
assert(start + size == page.memory + page.used);
|
||||||
|
|||||||
@@ -23,27 +23,17 @@ fn void SimpleHeapAllocator.init(&self, MemoryAllocFn allocator)
|
|||||||
|
|
||||||
fn void*! SimpleHeapAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
|
fn void*! SimpleHeapAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
|
||||||
{
|
{
|
||||||
if (!size) return null;
|
|
||||||
if (clear)
|
if (clear)
|
||||||
{
|
{
|
||||||
return alignment > 0 ? @aligned_calloc(self._calloc, size, alignment, offset) : self._calloc(size);
|
return alignment > 0 ? @aligned_alloc(self._calloc, size, alignment) : self._calloc(size);
|
||||||
}
|
}
|
||||||
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment, offset) : self._alloc(size);
|
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment) : self._alloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void*! SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
|
fn void*! SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
|
||||||
{
|
{
|
||||||
if (!size)
|
|
||||||
{
|
|
||||||
self.release(old_pointer, alignment > 0);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (!old_pointer)
|
|
||||||
{
|
|
||||||
return self.acquire(size, true, alignment, offset);
|
|
||||||
}
|
|
||||||
return alignment > 0
|
return alignment > 0
|
||||||
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment, offset)
|
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment)
|
||||||
: self._realloc(old_pointer, size);
|
: self._realloc(old_pointer, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,22 +6,36 @@ module std::core::mem::allocator;
|
|||||||
import libc;
|
import libc;
|
||||||
|
|
||||||
const LibcAllocator LIBC_ALLOCATOR = {};
|
const LibcAllocator LIBC_ALLOCATOR = {};
|
||||||
|
|
||||||
|
|
||||||
distinct LibcAllocator (Allocator) = uptr;
|
distinct LibcAllocator (Allocator) = uptr;
|
||||||
|
|
||||||
|
module std::core::mem::allocator @if(env::POSIX);
|
||||||
|
import std::os;
|
||||||
|
import libc;
|
||||||
|
|
||||||
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
|
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
|
||||||
{
|
{
|
||||||
assert(alignment != 0 || offset == 0);
|
|
||||||
if (clear)
|
if (clear)
|
||||||
{
|
{
|
||||||
void* data = alignment ? @aligned_calloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment, offset)!! : libc::calloc(bytes, 1);
|
void* data @noinit;
|
||||||
return data ?: AllocationFailure.OUT_OF_MEMORY?;
|
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
|
||||||
|
{
|
||||||
|
if (posix::posix_memalign(&data, alignment, bytes)) return AllocationFailure.OUT_OF_MEMORY?;
|
||||||
|
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return libc::calloc(1, bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
void* data = alignment ? @aligned_alloc(libc::malloc, bytes, alignment, offset)!! : libc::malloc(bytes);
|
void* data @noinit;
|
||||||
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
|
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
|
||||||
|
{
|
||||||
|
if (posix::posix_memalign(&data, alignment, bytes)) return AllocationFailure.OUT_OF_MEMORY?;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!(data = libc::malloc(bytes))) return AllocationFailure.OUT_OF_MEMORY?;
|
||||||
|
}
|
||||||
$if env::TESTING:
|
$if env::TESTING:
|
||||||
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
|
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
|
||||||
$endif
|
$endif
|
||||||
@@ -31,7 +45,6 @@ fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz
|
|||||||
|
|
||||||
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
|
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
|
||||||
{
|
{
|
||||||
assert(alignment != 0 || offset == 0);
|
|
||||||
if (!new_bytes)
|
if (!new_bytes)
|
||||||
{
|
{
|
||||||
self.release(old_ptr, alignment > 0);
|
self.release(old_ptr, alignment > 0);
|
||||||
@@ -39,11 +52,101 @@ fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignmen
|
|||||||
}
|
}
|
||||||
if (!old_ptr)
|
if (!old_ptr)
|
||||||
{
|
{
|
||||||
return self.acquire(new_bytes, true, alignment, offset);
|
return self.acquire(new_bytes, false, alignment, 0);
|
||||||
}
|
}
|
||||||
|
if (alignment <= mem::DEFAULT_MEM_ALIGNMENT) return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
|
||||||
|
|
||||||
|
void* new_ptr;
|
||||||
|
if (posix::posix_memalign(&new_ptr, alignment, new_bytes)) return AllocationFailure.OUT_OF_MEMORY?;
|
||||||
|
|
||||||
|
$switch
|
||||||
|
$case env::DARWIN:
|
||||||
|
usz old_usable_size = darwin::malloc_size(old_ptr);
|
||||||
|
$case env::LINUX:
|
||||||
|
usz old_usable_size = linux::malloc_usable_size(old_ptr);
|
||||||
|
$default:
|
||||||
|
usz old_usable_size = new_bytes;
|
||||||
|
$endswitch
|
||||||
|
|
||||||
|
usz copy_size = new_bytes < old_usable_size ? new_bytes : old_usable_size;
|
||||||
|
mem::copy(new_ptr, old_ptr, copy_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
|
libc::free(old_ptr);
|
||||||
|
return new_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
|
||||||
|
{
|
||||||
|
libc::free(old_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
module std::core::mem::allocator @if(env::WIN32);
|
||||||
|
import std::os::win32;
|
||||||
|
import libc;
|
||||||
|
|
||||||
|
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
|
||||||
|
{
|
||||||
|
if (clear)
|
||||||
|
{
|
||||||
|
if (alignment > 0)
|
||||||
|
{
|
||||||
|
return win32::_aligned_recalloc(null, bytes, alignment) ?: AllocationFailure.OUT_OF_MEMORY?;
|
||||||
|
}
|
||||||
|
return libc::calloc(1, bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
|
||||||
|
}
|
||||||
|
void* data = alignment > 0 ? win32::_aligned_malloc(bytes, alignment) : libc::malloc(bytes);
|
||||||
|
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
|
||||||
|
$if env::TESTING:
|
||||||
|
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
|
||||||
|
$endif
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
|
||||||
|
{
|
||||||
if (alignment)
|
if (alignment)
|
||||||
{
|
{
|
||||||
void* data = @aligned_realloc(fn void*(usz bytes) => libc::calloc(bytes, 1), libc::free, old_ptr, new_bytes, alignment, offset)!!;
|
return win32::_aligned_realloc(old_ptr, new_bytes, alignment) ?: AllocationFailure.OUT_OF_MEMORY?;
|
||||||
|
}
|
||||||
|
return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
|
||||||
|
{
|
||||||
|
if (aligned)
|
||||||
|
{
|
||||||
|
win32::_aligned_free(old_ptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
libc::free(old_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
module std::core::mem::allocator @if(!env::WIN32 && !env::POSIX);
|
||||||
|
import libc;
|
||||||
|
|
||||||
|
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
|
||||||
|
{
|
||||||
|
if (clear)
|
||||||
|
{
|
||||||
|
void* data = alignment ? @aligned_alloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment)!! : libc::calloc(bytes, 1);
|
||||||
|
return data ?: AllocationFailure.OUT_OF_MEMORY?;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
void* data = alignment ? @aligned_alloc(libc::malloc, bytes, alignment)!! : libc::malloc(bytes);
|
||||||
|
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
|
||||||
|
$if env::TESTING:
|
||||||
|
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
|
||||||
|
$endif
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
|
||||||
|
{
|
||||||
|
if (alignment)
|
||||||
|
{
|
||||||
|
void* data = @aligned_realloc(fn void*(usz bytes) => libc::malloc(bytes), libc::free, old_ptr, new_bytes, alignment)!!;
|
||||||
return data ?: AllocationFailure.OUT_OF_MEMORY?;
|
return data ?: AllocationFailure.OUT_OF_MEMORY?;
|
||||||
}
|
}
|
||||||
return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
|
return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
|
||||||
|
|||||||
@@ -54,9 +54,11 @@ struct OnStackAllocatorHeader
|
|||||||
char[*] data;
|
char[*] data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require old_pointer
|
||||||
|
**/
|
||||||
fn void OnStackAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
|
fn void OnStackAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
|
||||||
{
|
{
|
||||||
if (!old_pointer) return;
|
|
||||||
if (allocation_in_stack_mem(self, old_pointer)) return;
|
if (allocation_in_stack_mem(self, old_pointer)) return;
|
||||||
on_stack_allocator_remove_chunk(self, old_pointer);
|
on_stack_allocator_remove_chunk(self, old_pointer);
|
||||||
self.release(old_pointer, aligned);
|
self.release(old_pointer, aligned);
|
||||||
@@ -98,44 +100,38 @@ fn OnStackAllocatorExtraChunk* on_stack_allocator_find_chunk(OnStackAllocator* a
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @require size > 0
|
* @require size > 0
|
||||||
|
* @require old_pointer != null
|
||||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||||
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
|
|
||||||
* @require offset <= size && offset >= 0
|
|
||||||
* @require mem::aligned_offset(offset, OnStackAllocatorExtraChunk.alignof) == offset
|
|
||||||
**/
|
**/
|
||||||
fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
|
fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz deprecated) @dynamic
|
||||||
{
|
{
|
||||||
if (!allocation_in_stack_mem(self, old_pointer))
|
if (!allocation_in_stack_mem(self, old_pointer))
|
||||||
{
|
{
|
||||||
OnStackAllocatorExtraChunk* chunk = on_stack_allocator_find_chunk(self, old_pointer);
|
OnStackAllocatorExtraChunk* chunk = on_stack_allocator_find_chunk(self, old_pointer);
|
||||||
assert(chunk, "Tried to realloc pointer not belonging to the allocator");
|
assert(chunk, "Tried to realloc pointer not belonging to the allocator");
|
||||||
return chunk.data = self.backing_allocator.resize(old_pointer, size, alignment, offset)!;
|
return chunk.data = self.backing_allocator.resize(old_pointer, size, alignment, 0)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
|
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
|
||||||
usz old_size = header.size;
|
usz old_size = header.size;
|
||||||
void* mem = self.acquire(size, true, alignment, offset)!;
|
void* mem = self.acquire(size, false, alignment, 0)!;
|
||||||
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||||
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
|
* @require size > 0
|
||||||
* @require offset <= size && offset >= 0
|
|
||||||
* @require offset == 0 || alignment > 0
|
|
||||||
* @require mem::aligned_offset(offset, OnStackAllocatorHeader.alignof) == offset
|
|
||||||
**/
|
**/
|
||||||
fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
|
fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @dynamic
|
||||||
{
|
{
|
||||||
if (size == 0) return null;
|
|
||||||
bool aligned = alignment > 0;
|
bool aligned = alignment > 0;
|
||||||
alignment = alignment_for_allocation(alignment);
|
alignment = alignment_for_allocation(alignment);
|
||||||
usz total_len = self.data.len;
|
usz total_len = self.data.len;
|
||||||
void* start_mem = self.data.ptr;
|
void* start_mem = self.data.ptr;
|
||||||
void* unaligned_pointer_to_offset = start_mem + self.used + OnStackAllocatorHeader.sizeof + offset;
|
void* unaligned_pointer_to_offset = start_mem + self.used + OnStackAllocatorHeader.sizeof ;
|
||||||
void* aligned_pointer_to_offset = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
|
void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
|
||||||
usz end = (usz)(aligned_pointer_to_offset - self.data.ptr) + size - offset;
|
usz end = (usz)(mem - self.data.ptr) + size;
|
||||||
Allocator* backing_allocator = self.backing_allocator;
|
Allocator* backing_allocator = self.backing_allocator;
|
||||||
|
|
||||||
if (end > total_len)
|
if (end > total_len)
|
||||||
@@ -144,10 +140,9 @@ fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, u
|
|||||||
defer catch allocator::free(backing_allocator, chunk);
|
defer catch allocator::free(backing_allocator, chunk);
|
||||||
defer try self.chunk = chunk;
|
defer try self.chunk = chunk;
|
||||||
*chunk = { .prev = self.chunk, .is_aligned = aligned };
|
*chunk = { .prev = self.chunk, .is_aligned = aligned };
|
||||||
return chunk.data = backing_allocator.acquire(size, clear, aligned ? alignment : 0, offset)!;
|
return chunk.data = backing_allocator.acquire(size, clear, aligned ? alignment : 0, 0)!;
|
||||||
}
|
}
|
||||||
self.used = end;
|
self.used = end;
|
||||||
void *mem = aligned_pointer_to_offset - offset;
|
|
||||||
OnStackAllocatorHeader* header = mem - OnStackAllocatorHeader.sizeof;
|
OnStackAllocatorHeader* header = mem - OnStackAllocatorHeader.sizeof;
|
||||||
header.size = size;
|
header.size = size;
|
||||||
return mem;
|
return mem;
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ fn void! TempAllocator._free_page(&self, TempAllocatorPage* page) @inline @local
|
|||||||
return self.backing_allocator.release(mem, page.is_aligned());
|
return self.backing_allocator.release(mem, page.is_aligned());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment, usz offset) @inline @local
|
fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment) @inline @local
|
||||||
{
|
{
|
||||||
// Then the actual start pointer:
|
// Then the actual start pointer:
|
||||||
void* real_pointer = page.start;
|
void* real_pointer = page.start;
|
||||||
@@ -94,46 +94,37 @@ fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size,
|
|||||||
*pointer_to_prev = page.prev_page;
|
*pointer_to_prev = page.prev_page;
|
||||||
usz page_size = page.pagesize();
|
usz page_size = page.pagesize();
|
||||||
// Clear on size > original size.
|
// Clear on size > original size.
|
||||||
void* data = self.acquire(size, size > page_size, alignment, offset)!;
|
void* data = self.acquire(size, size > page_size, alignment, 0)!;
|
||||||
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
self.backing_allocator.release(real_pointer, page.is_aligned());
|
self.backing_allocator.release(real_pointer, page.is_aligned());
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, usz offset) @dynamic
|
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, usz deprecated) @dynamic
|
||||||
{
|
{
|
||||||
if (!size)
|
|
||||||
{
|
|
||||||
self.release(pointer, alignment > 0);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (!pointer)
|
|
||||||
{
|
|
||||||
return self.acquire(size, true, alignment, offset);
|
|
||||||
}
|
|
||||||
TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
|
TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
|
||||||
if (chunk.size == (usz)-1)
|
if (chunk.size == (usz)-1)
|
||||||
{
|
{
|
||||||
assert(self.last_page, "Realloc of non temp pointer");
|
assert(self.last_page, "Realloc of non temp pointer");
|
||||||
// First grab the page
|
// First grab the page
|
||||||
TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof;
|
TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof;
|
||||||
return self._realloc_page(page, size, alignment, offset);
|
return self._realloc_page(page, size, alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO optimize last allocation
|
// TODO optimize last allocation
|
||||||
TempAllocatorChunk* data = self.acquire(size, size > chunk.size, alignment, offset)!;
|
TempAllocatorChunk* data = self.acquire(size, size > chunk.size, alignment, 0)!;
|
||||||
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @require size > 0
|
||||||
* @require !alignment || math::is_power_of_2(alignment)
|
* @require !alignment || math::is_power_of_2(alignment)
|
||||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||||
**/
|
**/
|
||||||
fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
|
fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @dynamic
|
||||||
{
|
{
|
||||||
if (!size) return null;
|
|
||||||
alignment = alignment_for_allocation(alignment);
|
alignment = alignment_for_allocation(alignment);
|
||||||
void* start_mem = &self.data;
|
void* start_mem = &self.data;
|
||||||
void* starting_ptr = start_mem + self.used;
|
void* starting_ptr = start_mem + self.used;
|
||||||
@@ -141,7 +132,7 @@ fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
|
|||||||
void* mem = aligned_header_start + TempAllocatorChunk.sizeof;
|
void* mem = aligned_header_start + TempAllocatorChunk.sizeof;
|
||||||
if (alignment > TempAllocatorChunk.alignof)
|
if (alignment > TempAllocatorChunk.alignof)
|
||||||
{
|
{
|
||||||
mem = mem::aligned_pointer(mem + offset, alignment) - offset;
|
mem = mem::aligned_pointer(mem, alignment);
|
||||||
}
|
}
|
||||||
usz new_usage = (usz)(mem - start_mem) + size;
|
usz new_usage = (usz)(mem - start_mem) + size;
|
||||||
|
|
||||||
@@ -159,19 +150,20 @@ fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
|
|||||||
TempAllocatorPage* page;
|
TempAllocatorPage* page;
|
||||||
|
|
||||||
// We have something we need to align.
|
// We have something we need to align.
|
||||||
if (alignment > mem::DEFAULT_MEM_ALIGNMENT || offset)
|
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
|
||||||
{
|
{
|
||||||
// This is actually simpler, since it will create the offset for us.
|
// This is actually simpler, since it will create the offset for us.
|
||||||
usz total_alloc_size = TempAllocatorPage.sizeof + size;
|
usz total_alloc_size = mem::aligned_offset(TempAllocatorPage.sizeof + size, alignment);
|
||||||
if (clear)
|
if (clear)
|
||||||
{
|
{
|
||||||
page = allocator::calloc_aligned(self.backing_allocator, total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
|
mem = allocator::calloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
page = allocator::malloc_aligned(self.backing_allocator, total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
|
mem = allocator::malloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
|
||||||
}
|
}
|
||||||
page.start = page;
|
page = (TempAllocatorPage*)mem - 1;
|
||||||
|
page.start = mem;
|
||||||
page.size = size | PAGE_IS_ALIGNED;
|
page.size = size | PAGE_IS_ALIGNED;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -79,48 +79,36 @@ fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator* allocator)
|
|||||||
**/
|
**/
|
||||||
fn usz TrackingAllocator.allocation_count(&self) => self.map.count;
|
fn usz TrackingAllocator.allocation_count(&self) => self.map.count;
|
||||||
|
|
||||||
fn void*! TrackingAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
|
fn void*! TrackingAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @dynamic
|
||||||
{
|
{
|
||||||
void* data = self.inner_allocator.acquire(size, clear, alignment, offset)!;
|
void* data = self.inner_allocator.acquire(size, clear, alignment, 0)!;
|
||||||
|
self.allocs_total++;
|
||||||
|
void*[MAX_BACKTRACE] bt;
|
||||||
|
backtrace::capture_current(&bt);
|
||||||
|
self.map.set((uptr)data, { data, size, bt });
|
||||||
|
self.mem_total += size;
|
||||||
self.allocs_total++;
|
self.allocs_total++;
|
||||||
if (data)
|
|
||||||
{
|
|
||||||
void*[MAX_BACKTRACE] bt;
|
|
||||||
backtrace::capture_current(&bt);
|
|
||||||
self.map.set((uptr)data, { data, size, bt });
|
|
||||||
self.mem_total += size;
|
|
||||||
self.allocs_total++;
|
|
||||||
}
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
|
fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz deprecated) @dynamic
|
||||||
{
|
{
|
||||||
void* data = self.inner_allocator.resize(old_pointer, size, alignment, offset)!;
|
void* data = self.inner_allocator.resize(old_pointer, size, alignment, 0)!;
|
||||||
if (old_pointer)
|
self.map.remove((uptr)old_pointer);
|
||||||
{
|
void*[MAX_BACKTRACE] bt;
|
||||||
self.map.remove((uptr)old_pointer);
|
backtrace::capture_current(&bt);
|
||||||
}
|
self.map.set((uptr)data, { data, size, bt });
|
||||||
if (data)
|
self.mem_total += size;
|
||||||
{
|
self.allocs_total++;
|
||||||
void*[MAX_BACKTRACE] bt;
|
|
||||||
backtrace::capture_current(&bt);
|
|
||||||
self.map.set((uptr)data, { data, size, bt });
|
|
||||||
self.mem_total += size;
|
|
||||||
self.allocs_total++;
|
|
||||||
}
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void TrackingAllocator.release(&self, void* old_pointer, bool is_aligned) @dynamic
|
fn void TrackingAllocator.release(&self, void* old_pointer, bool is_aligned) @dynamic
|
||||||
{
|
{
|
||||||
if (old_pointer)
|
if (catch self.map.remove((uptr)old_pointer))
|
||||||
{
|
{
|
||||||
if (catch self.map.remove((uptr)old_pointer))
|
assert(false, "Attempt to release untracked pointer %p, this is likely a bug.", old_pointer);
|
||||||
{
|
}
|
||||||
assert(false, "Attempt to release untracked pointer %p, this is likely a bug.", old_pointer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.inner_allocator.release(old_pointer, is_aligned);
|
self.inner_allocator.release(old_pointer, is_aligned);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -253,11 +253,12 @@ fn bool ptr_is_aligned(void* ptr, usz alignment) @inline
|
|||||||
|
|
||||||
macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
|
macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
|
||||||
{
|
{
|
||||||
$if $inlined:
|
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
|
||||||
$$memset_inline(dst, (char)0, len, $is_volatile, $dst_align);
|
}
|
||||||
$else
|
|
||||||
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
|
macro void clear_inline(void* dst, usz $len, usz $dst_align = 0, bool $is_volatile = false)
|
||||||
$endif
|
{
|
||||||
|
$$memset_inline(dst, (char)0, $len, $is_volatile, $dst_align);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -269,17 +270,30 @@ macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = fal
|
|||||||
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
|
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||||
* @param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
|
* @param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||||
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
|
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
|
||||||
* @param $inlined "True if this copy should never call the OS memcpy."
|
|
||||||
*
|
*
|
||||||
* @require len == 0 || dst + len <= src || src + len <= dst : "Ranges may not overlap"
|
* @require len == 0 || dst + len <= src || src + len <= dst : "Ranges may not overlap"
|
||||||
**/
|
**/
|
||||||
macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false, bool $inlined = false)
|
macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false, bool $inlined = false)
|
||||||
{
|
{
|
||||||
$if $inlined:
|
$$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align);
|
||||||
$$memcpy_inline(dst, src, len, $is_volatile, $dst_align, $src_align);
|
}
|
||||||
$else
|
|
||||||
$$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align);
|
/**
|
||||||
$endif
|
* Copy memory from src to dst efficiently, assuming the memory ranges do not overlap, it
|
||||||
|
* will always be inlined and never call memcopy
|
||||||
|
*
|
||||||
|
* @param [&out] dst "The destination to copy to"
|
||||||
|
* @param [&in] src "The source to copy from"
|
||||||
|
* @param $len "The number of bytes to copy"
|
||||||
|
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||||
|
* @param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||||
|
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
|
||||||
|
*
|
||||||
|
* @require $len == 0 || dst + $len <= src || src + $len <= dst : "Ranges may not overlap"
|
||||||
|
**/
|
||||||
|
macro void copy_inline(void* dst, void* src, usz $len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
|
||||||
|
{
|
||||||
|
$$memcpy_inline(dst, src, $len, $is_volatile, $dst_align, $src_align);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -305,19 +319,29 @@ macro void move(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_alig
|
|||||||
* @param len "The number of bytes to copy"
|
* @param len "The number of bytes to copy"
|
||||||
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
|
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||||
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
|
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
|
||||||
* @param $inlined "True if this copy should never call the OS memset."
|
|
||||||
*
|
*
|
||||||
* @ensure !len || (dst[0] == val && dst[len - 1] == val)
|
* @ensure !len || (dst[0] == val && dst[len - 1] == val)
|
||||||
**/
|
**/
|
||||||
macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
|
macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volatile = false)
|
||||||
{
|
{
|
||||||
$if $inlined:
|
$$memset(dst, val, len, $is_volatile, $dst_align);
|
||||||
$$memset_inline(dst, val, len, $is_volatile, $dst_align);
|
|
||||||
$else
|
|
||||||
$$memset(dst, val, len, $is_volatile, $dst_align);
|
|
||||||
$endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets all memory in a region to that of the provided byte. Never calls OS memset.
|
||||||
|
*
|
||||||
|
* @param [&out] dst "The destination to copy to"
|
||||||
|
* @param val "The value to copy into memory"
|
||||||
|
* @param $len "The number of bytes to copy"
|
||||||
|
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||||
|
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
|
||||||
|
*
|
||||||
|
* @ensure !$len || (dst[0] == val && dst[$len - 1] == val)
|
||||||
|
**/
|
||||||
|
macro void set_inline(void* dst, char val, usz $len, usz $dst_align = 0, bool $is_volatile = false)
|
||||||
|
{
|
||||||
|
$$memset_inline(dst, val, $len, $is_volatile, $dst_align);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @require values::@inner_kind(a) == TypeKind.SUBARRAY || values::@inner_kind(a) == TypeKind.POINTER
|
* @require values::@inner_kind(a) == TypeKind.SUBARRAY || values::@inner_kind(a) == TypeKind.POINTER
|
||||||
* @require values::@inner_kind(b) == TypeKind.SUBARRAY || values::@inner_kind(b) == TypeKind.POINTER
|
* @require values::@inner_kind(b) == TypeKind.SUBARRAY || values::@inner_kind(b) == TypeKind.POINTER
|
||||||
@@ -530,12 +554,14 @@ fn void* malloc(usz size) @builtin @inline @nodiscard
|
|||||||
|
|
||||||
fn void* tmalloc(usz size, usz alignment = 0, usz offset = 0) @builtin @inline @nodiscard
|
fn void* tmalloc(usz size, usz alignment = 0, usz offset = 0) @builtin @inline @nodiscard
|
||||||
{
|
{
|
||||||
return allocator::temp().acquire(size, false, alignment, offset)!!;
|
if (!size) return null;
|
||||||
|
return allocator::temp().acquire(size, false, alignment, 0)!!;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require $vacount < 2 : "Too many arguments."
|
* @require $vacount < 2 : "Too many arguments."
|
||||||
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
|
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
|
||||||
|
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
||||||
**/
|
**/
|
||||||
macro new($Type, ...) @nodiscard
|
macro new($Type, ...) @nodiscard
|
||||||
{
|
{
|
||||||
@@ -548,11 +574,40 @@ macro new($Type, ...) @nodiscard
|
|||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||||
|
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||||
|
* @require $vacount < 2 : "Too many arguments."
|
||||||
|
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
|
||||||
|
**/
|
||||||
|
macro new_aligned($Type, ...) @nodiscard
|
||||||
|
{
|
||||||
|
$if $vacount == 0:
|
||||||
|
return ($Type*)calloc_aligned($Type.sizeof, $Type.alignof);
|
||||||
|
$else
|
||||||
|
$Type* val = malloc_aligned($Type.sizeof, $Type.alignof);
|
||||||
|
*val = $vaexpr(0);
|
||||||
|
return val;
|
||||||
|
$endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
||||||
|
**/
|
||||||
macro alloc($Type) @nodiscard
|
macro alloc($Type) @nodiscard
|
||||||
{
|
{
|
||||||
return ($Type*)malloc($Type.sizeof);
|
return ($Type*)malloc($Type.sizeof);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||||
|
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||||
|
**/
|
||||||
|
macro alloc_aligned($Type) @nodiscard
|
||||||
|
{
|
||||||
|
return ($Type*)malloc_aligned($Type.sizeof, $Type.alignof);
|
||||||
|
}
|
||||||
|
|
||||||
macro new_clear($Type) @deprecated("Use mem::new")
|
macro new_clear($Type) @deprecated("Use mem::new")
|
||||||
{
|
{
|
||||||
return new($Type);
|
return new($Type);
|
||||||
@@ -588,16 +643,42 @@ macro new_temp_clear($Type) @deprecated("use mem::temp_new")
|
|||||||
return tcalloc($Type.sizeof);
|
return tcalloc($Type.sizeof);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead"
|
||||||
|
**/
|
||||||
macro new_array($Type, usz elements) @nodiscard
|
macro new_array($Type, usz elements) @nodiscard
|
||||||
{
|
{
|
||||||
return allocator::new_array(allocator::heap(), $Type, elements);
|
return allocator::new_array(allocator::heap(), $Type, elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||||
|
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||||
|
**/
|
||||||
|
macro new_array_aligned($Type, usz elements) @nodiscard
|
||||||
|
{
|
||||||
|
return allocator::new_array_aligned(allocator::heap(), $Type, elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead"
|
||||||
|
**/
|
||||||
macro alloc_array($Type, usz elements) @nodiscard
|
macro alloc_array($Type, usz elements) @nodiscard
|
||||||
{
|
{
|
||||||
return allocator::alloc_array(allocator::heap(), $Type, elements);
|
return allocator::alloc_array(allocator::heap(), $Type, elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||||
|
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||||
|
**/
|
||||||
|
macro alloc_array_aligned($Type, usz elements) @nodiscard
|
||||||
|
{
|
||||||
|
return allocator::alloc_array(allocator::heap(), $Type, elements);
|
||||||
|
}
|
||||||
|
|
||||||
macro talloc_array($Type, usz elements) @nodiscard @deprecated("use mem::temp_alloc_array")
|
macro talloc_array($Type, usz elements) @nodiscard @deprecated("use mem::temp_alloc_array")
|
||||||
{
|
{
|
||||||
return temp_alloc_array($Type, elements);
|
return temp_alloc_array($Type, elements);
|
||||||
@@ -633,9 +714,15 @@ fn void* calloc(usz size) @builtin @inline @nodiscard
|
|||||||
return allocator::calloc(allocator::heap(), size);
|
return allocator::calloc(allocator::heap(), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn void* calloc_aligned(usz size, usz alignment) @builtin @inline @nodiscard
|
||||||
|
{
|
||||||
|
return allocator::calloc_aligned(allocator::heap(), size, alignment)!!;
|
||||||
|
}
|
||||||
|
|
||||||
fn void* tcalloc(usz size, usz alignment = 0, usz offset = 0) @builtin @inline @nodiscard
|
fn void* tcalloc(usz size, usz alignment = 0, usz offset = 0) @builtin @inline @nodiscard
|
||||||
{
|
{
|
||||||
return allocator::temp().acquire(size, false, alignment, offset)!!;
|
if (!size) return null;
|
||||||
|
return allocator::temp().acquire(size, false, alignment, 0)!!;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void* realloc(void *ptr, usz new_size) @builtin @inline @nodiscard
|
fn void* realloc(void *ptr, usz new_size) @builtin @inline @nodiscard
|
||||||
@@ -643,13 +730,25 @@ fn void* realloc(void *ptr, usz new_size) @builtin @inline @nodiscard
|
|||||||
return allocator::realloc(allocator::heap(), ptr, new_size);
|
return allocator::realloc(allocator::heap(), ptr, new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn void* realloc_aligned(void *ptr, usz new_size, usz alignment) @builtin @inline @nodiscard
|
||||||
|
{
|
||||||
|
return allocator::realloc_aligned(allocator::heap(), ptr, new_size, alignment)!!;
|
||||||
|
}
|
||||||
|
|
||||||
fn void free(void* ptr) @builtin @inline
|
fn void free(void* ptr) @builtin @inline
|
||||||
{
|
{
|
||||||
return allocator::free(allocator::heap(), ptr);
|
return allocator::free(allocator::heap(), ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn void free_aligned(void* ptr) @builtin @inline
|
||||||
|
{
|
||||||
|
return allocator::free_aligned(allocator::heap(), ptr);
|
||||||
|
}
|
||||||
|
|
||||||
fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin @inline @nodiscard
|
fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin @inline @nodiscard
|
||||||
{
|
{
|
||||||
|
if (!size) return null;
|
||||||
|
if (!ptr) return tmalloc(size, alignment);
|
||||||
return allocator::temp().resize(ptr, size, alignment, 0)!!;
|
return allocator::temp().resize(ptr, size, alignment, 0)!!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,24 @@ interface Allocator
|
|||||||
{
|
{
|
||||||
fn void reset(usz mark) @optional;
|
fn void reset(usz mark) @optional;
|
||||||
fn usz mark() @optional;
|
fn usz mark() @optional;
|
||||||
|
/**
|
||||||
|
* @require !alignment || math::is_power_of_2(alignment)
|
||||||
|
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||||
|
* @require offset == 0 `offset no longer supported`
|
||||||
|
* @require size > 0
|
||||||
|
**/
|
||||||
fn void*! acquire(usz size, bool clear, usz alignment, usz offset);
|
fn void*! acquire(usz size, bool clear, usz alignment, usz offset);
|
||||||
|
/**
|
||||||
|
* @require !alignment || math::is_power_of_2(alignment)
|
||||||
|
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||||
|
* @require offset == 0 `offset no longer supported`
|
||||||
|
* @require ptr != null
|
||||||
|
* @require new_size > 0
|
||||||
|
**/
|
||||||
fn void*! resize(void* ptr, usz new_size, usz alignment, usz offset);
|
fn void*! resize(void* ptr, usz new_size, usz alignment, usz offset);
|
||||||
|
/**
|
||||||
|
* @require ptr != null
|
||||||
|
**/
|
||||||
fn void release(void* ptr, bool aligned);
|
fn void release(void* ptr, bool aligned);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,6 +56,7 @@ macro void* malloc(Allocator* allocator, usz size) @nodiscard
|
|||||||
|
|
||||||
macro void*! malloc_try(Allocator* allocator, usz size) @nodiscard
|
macro void*! malloc_try(Allocator* allocator, usz size) @nodiscard
|
||||||
{
|
{
|
||||||
|
if (!size) return null;
|
||||||
$if env::TESTING:
|
$if env::TESTING:
|
||||||
char* data = allocator.acquire(size, false, 0, 0)!;
|
char* data = allocator.acquire(size, false, 0, 0)!;
|
||||||
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
|
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
@@ -56,6 +73,7 @@ macro void* calloc(Allocator* allocator, usz size) @nodiscard
|
|||||||
|
|
||||||
macro void*! calloc_try(Allocator* allocator, usz size) @nodiscard
|
macro void*! calloc_try(Allocator* allocator, usz size) @nodiscard
|
||||||
{
|
{
|
||||||
|
if (!size) return null;
|
||||||
return allocator.acquire(size, true, 0, 0);
|
return allocator.acquire(size, true, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,19 +84,27 @@ macro void* realloc(Allocator* allocator, void* ptr, usz new_size) @nodiscard
|
|||||||
|
|
||||||
macro void*! realloc_try(Allocator* allocator, void* ptr, usz new_size) @nodiscard
|
macro void*! realloc_try(Allocator* allocator, void* ptr, usz new_size) @nodiscard
|
||||||
{
|
{
|
||||||
|
if (!new_size)
|
||||||
|
{
|
||||||
|
free(allocator, ptr);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!ptr) return allocator.acquire(new_size, false, 0, 0);
|
||||||
return allocator.resize(ptr, new_size, 0, 0);
|
return allocator.resize(ptr, new_size, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void free(Allocator* allocator, void* ptr)
|
macro void free(Allocator* allocator, void* ptr)
|
||||||
{
|
{
|
||||||
|
if (!ptr) return;
|
||||||
$if env::TESTING:
|
$if env::TESTING:
|
||||||
if (ptr) ((char*)ptr)[0] = 0xBA;
|
((char*)ptr)[0] = 0xBA;
|
||||||
$endif
|
$endif
|
||||||
allocator.release(ptr, false);
|
allocator.release(ptr, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void*! malloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @nodiscard
|
macro void*! malloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @nodiscard
|
||||||
{
|
{
|
||||||
|
if (!size) return null;
|
||||||
$if env::TESTING:
|
$if env::TESTING:
|
||||||
char* data = allocator.acquire(size, false, alignment, offset)!;
|
char* data = allocator.acquire(size, false, alignment, offset)!;
|
||||||
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
|
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
@@ -90,18 +116,29 @@ macro void*! malloc_aligned(Allocator* allocator, usz size, usz alignment, usz o
|
|||||||
|
|
||||||
macro void*! calloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @nodiscard
|
macro void*! calloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @nodiscard
|
||||||
{
|
{
|
||||||
|
if (!size) return null;
|
||||||
return allocator.acquire(size, true, alignment, offset);
|
return allocator.acquire(size, true, alignment, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void*! realloc_aligned(Allocator* allocator, void* ptr, usz new_size, usz alignment, usz offset = 0) @nodiscard
|
macro void*! realloc_aligned(Allocator* allocator, void* ptr, usz new_size, usz alignment, usz offset = 0) @nodiscard
|
||||||
{
|
{
|
||||||
|
if (!new_size)
|
||||||
|
{
|
||||||
|
free_aligned(allocator, ptr);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!ptr)
|
||||||
|
{
|
||||||
|
return malloc_aligned(allocator, new_size, alignment);
|
||||||
|
}
|
||||||
return allocator.resize(ptr, new_size, alignment, offset);
|
return allocator.resize(ptr, new_size, alignment, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void free_aligned(Allocator* allocator, void* ptr)
|
macro void free_aligned(Allocator* allocator, void* ptr)
|
||||||
{
|
{
|
||||||
|
if (!ptr) return;
|
||||||
$if env::TESTING:
|
$if env::TESTING:
|
||||||
if (ptr) ((char*)ptr)[0] = 0xBA;
|
((char*)ptr)[0] = 0xBA;
|
||||||
$endif
|
$endif
|
||||||
allocator.release(ptr, true);
|
allocator.release(ptr, true);
|
||||||
}
|
}
|
||||||
@@ -166,11 +203,21 @@ macro new_array_try(Allocator* allocator, $Type, usz elements) @nodiscard
|
|||||||
return (($Type*)calloc_try(allocator, $Type.sizeof * elements))[:elements];
|
return (($Type*)calloc_try(allocator, $Type.sizeof * elements))[:elements];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro new_array_aligned(Allocator* allocator, $Type, usz elements) @nodiscard
|
||||||
|
{
|
||||||
|
return ((Type*)calloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!;
|
||||||
|
}
|
||||||
|
|
||||||
macro alloc_array(Allocator* allocator, $Type, usz elements) @nodiscard
|
macro alloc_array(Allocator* allocator, $Type, usz elements) @nodiscard
|
||||||
{
|
{
|
||||||
return alloc_array_try(allocator, $Type, elements)!!;
|
return alloc_array_try(allocator, $Type, elements)!!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro alloc_array_aligned(Allocator* allocator, $Type, usz elements) @nodiscard
|
||||||
|
{
|
||||||
|
return ((Type*)malloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!;
|
||||||
|
}
|
||||||
|
|
||||||
macro alloc_array_try(Allocator* allocator, $Type, usz elements) @nodiscard
|
macro alloc_array_try(Allocator* allocator, $Type, usz elements) @nodiscard
|
||||||
{
|
{
|
||||||
return (($Type*)malloc_try(allocator, $Type.sizeof * elements))[:elements];
|
return (($Type*)malloc_try(allocator, $Type.sizeof * elements))[:elements];
|
||||||
@@ -193,23 +240,17 @@ fn any* clone_any(Allocator* allocator, any* value) @nodiscard
|
|||||||
|
|
||||||
macro void*! Allocator.alloc_checked(&self, usz size) @deprecated("Use allocator::malloc_try")
|
macro void*! Allocator.alloc_checked(&self, usz size) @deprecated("Use allocator::malloc_try")
|
||||||
{
|
{
|
||||||
$if env::TESTING:
|
return malloc_try(self, size);
|
||||||
char* data = self.acquire(size, false, 0, 0)!;
|
|
||||||
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
|
|
||||||
return data;
|
|
||||||
$else
|
|
||||||
return self.acquire(size, false, 0, 0);
|
|
||||||
$endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void*! Allocator.calloc_checked(&self, usz size) @deprecated("Use allocator::calloc_try")
|
macro void*! Allocator.calloc_checked(&self, usz size) @deprecated("Use allocator::calloc_try")
|
||||||
{
|
{
|
||||||
return self.acquire(size, true, 0, 0);
|
return calloc_try(self, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void*! Allocator.realloc_checked(&self, void* ptr, usz new_size) @deprecated("Use allocator::realloc_try")
|
macro void*! Allocator.realloc_checked(&self, void* ptr, usz new_size) @deprecated("Use allocator::realloc_try")
|
||||||
{
|
{
|
||||||
return self.resize(ptr, new_size, 0, 0);
|
return realloc_try(ptr, new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro Allocator.new_array(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::alloc_array")
|
macro Allocator.new_array(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::alloc_array")
|
||||||
@@ -258,86 +299,60 @@ macro Allocator.clone(&self, value) @deprecated("Use allocator::clone")
|
|||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void* Allocator.alloc(&self, usz size) @nodiscard @deprecated("Use allocator::malloc")
|
fn void* Allocator.alloc(&self, usz size) @nodiscard @deprecated("Use allocator::malloc")
|
||||||
{
|
{
|
||||||
return self.alloc_checked(size)!!;
|
return malloc(self, size);
|
||||||
}
|
}
|
||||||
macro void* Allocator.calloc(&self, usz size) @nodiscard @deprecated("Use allocator::calloc")
|
fn void* Allocator.calloc(&self, usz size) @nodiscard @deprecated("Use allocator::calloc")
|
||||||
{
|
{
|
||||||
return self.acquire(size, true, 0, 0)!!;
|
return calloc(self, size);
|
||||||
}
|
}
|
||||||
macro void* Allocator.realloc(&self, void* ptr, usz new_size) @nodiscard @deprecated("Use allocator::realloc")
|
fn void* Allocator.realloc(&self, void* ptr, usz new_size) @nodiscard @deprecated("Use allocator::realloc")
|
||||||
{
|
{
|
||||||
return self.resize(ptr, new_size, 0, 0)!!;
|
return realloc(self, ptr, new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void*! Allocator.alloc_aligned(&self, usz size, usz alignment, usz offset = 0) @deprecated("Use allocator::alloc_aligned")
|
fn void*! Allocator.alloc_aligned(&self, usz size, usz alignment, usz offset = 0) @deprecated("Use allocator::malloc_aligned")
|
||||||
{
|
{
|
||||||
$if env::TESTING:
|
return malloc_aligned(self, size, alignment, 0);
|
||||||
char* data = self.acquire(size, false, alignment, offset)!;
|
|
||||||
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
|
|
||||||
return data;
|
|
||||||
$else
|
|
||||||
return self.acquire(size, false, alignment, offset);
|
|
||||||
$endif
|
|
||||||
}
|
|
||||||
macro void*! Allocator.calloc_aligned(&self, usz size, usz alignment, usz offset = 0) @deprecated("Use allocator::calloc_aligned")
|
|
||||||
{
|
|
||||||
return self.acquire(size, true, alignment, offset);
|
|
||||||
}
|
|
||||||
macro void*! Allocator.realloc_aligned(&self, void* ptr, usz new_size, usz alignment = 0, usz offset = 0) @deprecated("Use allocator::realloc_aligned")
|
|
||||||
{
|
|
||||||
return self.resize(ptr, new_size, alignment, offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void Allocator.free(&self, void* ptr) @deprecated("Use allocator::free")
|
fn void*! Allocator.calloc_aligned(&self, usz size, usz alignment, usz offset = 0) @deprecated("Use allocator::calloc_aligned")
|
||||||
{
|
{
|
||||||
$if env::TESTING:
|
return calloc_aligned(self, size, alignment, 0);
|
||||||
if (ptr) ((char*)ptr)[0] = 0xBA;
|
|
||||||
$endif
|
|
||||||
self.release(ptr, false);
|
|
||||||
}
|
}
|
||||||
macro void Allocator.free_aligned(&self, void* ptr) @deprecated("Use allocator::free_aligned")
|
|
||||||
|
fn void*! Allocator.realloc_aligned(&self, void* ptr, usz new_size, usz alignment = 0, usz offset = 0) @deprecated("Use allocator::realloc_aligned")
|
||||||
{
|
{
|
||||||
$if env::TESTING:
|
return realloc_aligned(self, ptr, new_size, alignment, 0);
|
||||||
if (ptr) ((char*)ptr)[0] = 0xBA;
|
}
|
||||||
$endif
|
|
||||||
self.release(ptr, true);
|
fn void Allocator.free(&self, void* ptr) @deprecated("Use allocator::free")
|
||||||
|
{
|
||||||
|
free(self, ptr);
|
||||||
|
}
|
||||||
|
fn void Allocator.free_aligned(&self, void* ptr) @deprecated("Use allocator::free_aligned")
|
||||||
|
{
|
||||||
|
free_aligned(self, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require bytes > 0
|
* @require bytes > 0
|
||||||
* @require alignment > 0
|
* @require alignment > 0
|
||||||
|
* @require bytes <= isz.max
|
||||||
**/
|
**/
|
||||||
macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment, usz offset)
|
macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment)
|
||||||
{
|
{
|
||||||
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
|
if (alignment < void*.alignof) alignment = void*.alignof;
|
||||||
|
usz header = AlignedBlock.sizeof + alignment;
|
||||||
|
usz alignsize = bytes + header;
|
||||||
$if @typekind(#alloc_fn(bytes)) == OPTIONAL:
|
$if @typekind(#alloc_fn(bytes)) == OPTIONAL:
|
||||||
void* data = #alloc_fn(header + bytes)!;
|
void* data = #alloc_fn(alignsize)!;
|
||||||
$else
|
$else
|
||||||
void* data = #alloc_fn(header + bytes);
|
void* data = #alloc_fn(alignsize);
|
||||||
$endif
|
$endif
|
||||||
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
|
void* mem = mem::aligned_pointer(data + AlignedBlock.sizeof, alignment);
|
||||||
assert(mem > data);
|
|
||||||
AlignedBlock* desc = (AlignedBlock*)mem - 1;
|
|
||||||
*desc = { bytes, data };
|
|
||||||
return mem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @require bytes > 0
|
|
||||||
* @require alignment > 0
|
|
||||||
**/
|
|
||||||
macro void*! @aligned_calloc(#calloc_fn, usz bytes, usz alignment, usz offset)
|
|
||||||
{
|
|
||||||
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
|
|
||||||
$if @typekind(#calloc_fn(bytes)) == OPTIONAL:
|
|
||||||
void* data = #calloc_fn(header + bytes)!;
|
|
||||||
$else
|
|
||||||
void* data = #calloc_fn(header + bytes);
|
|
||||||
$endif
|
|
||||||
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
|
|
||||||
AlignedBlock* desc = (AlignedBlock*)mem - 1;
|
AlignedBlock* desc = (AlignedBlock*)mem - 1;
|
||||||
assert(mem > data);
|
assert(mem > data);
|
||||||
*desc = { bytes, data };
|
*desc = { bytes, data };
|
||||||
@@ -364,12 +379,12 @@ macro void! @aligned_free(#free_fn, void* old_pointer)
|
|||||||
* @require bytes > 0
|
* @require bytes > 0
|
||||||
* @require alignment > 0
|
* @require alignment > 0
|
||||||
**/
|
**/
|
||||||
macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment, usz offset)
|
macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment)
|
||||||
{
|
{
|
||||||
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
|
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
|
||||||
void* data_start = desc.start;
|
void* data_start = desc.start;
|
||||||
void* new_data = @aligned_calloc(#calloc_fn, bytes, alignment, offset)!;
|
void* new_data = @aligned_alloc(#calloc_fn, bytes, alignment)!;
|
||||||
mem::copy(new_data, old_pointer, desc.len < bytes ? desc.len : bytes, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
mem::copy(new_data, old_pointer, desc.len < bytes ? desc.len : bytes, 1, 1);
|
||||||
$if @typekind(#free_fn(data_start)) == OPTIONAL:
|
$if @typekind(#free_fn(data_start)) == OPTIONAL:
|
||||||
#free_fn(data_start)!;
|
#free_fn(data_start)!;
|
||||||
$else
|
$else
|
||||||
|
|||||||
@@ -97,10 +97,10 @@ def complex_identity = complex::identity(<double>);
|
|||||||
|
|
||||||
def Quaternionf = Quaternion(<float>);
|
def Quaternionf = Quaternion(<float>);
|
||||||
def Quaternion = Quaternion(<double>);
|
def Quaternion = Quaternion(<double>);
|
||||||
def quaternionf_identity = quaternion::identity(<float>);
|
|
||||||
def quaternion_identity = quaternion::identity(<double>);
|
|
||||||
def QUATERNION_IDENTITY = quaternion::IDENTITY(<double>);
|
def QUATERNION_IDENTITY = quaternion::IDENTITY(<double>);
|
||||||
def QUATERNIONF_IDENTITY = quaternion::IDENTITY(<float>);
|
def QUATERNIONF_IDENTITY = quaternion::IDENTITY(<float>);
|
||||||
|
def quaternion_identity = quaternion::identity(<double>);
|
||||||
|
def quaternionf_identity = quaternion::identity(<float>);
|
||||||
|
|
||||||
def Matrix2f = Matrix2x2(<float>);
|
def Matrix2f = Matrix2x2(<float>);
|
||||||
def Matrix2 = Matrix2x2(<double>);
|
def Matrix2 = Matrix2x2(<double>);
|
||||||
@@ -120,6 +120,14 @@ def MATRIX3F_IDENTITY = matrix::IDENTITY3(<float>);
|
|||||||
def MATRIX4_IDENTITY = matrix::IDENTITY4(<double>);
|
def MATRIX4_IDENTITY = matrix::IDENTITY4(<double>);
|
||||||
def MATRIX4F_IDENTITY = matrix::IDENTITY4(<float>);
|
def MATRIX4F_IDENTITY = matrix::IDENTITY4(<float>);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
|
||||||
|
**/
|
||||||
|
macro deg_to_rad(x) {
|
||||||
|
return x * PI / 180;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
|
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
|
||||||
**/
|
**/
|
||||||
@@ -392,14 +400,14 @@ macro nearbyint(x) => $$nearbyint(x);
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
||||||
* @require $assignable(exp, $typeof(x)) || values::@is_int(exp) `The input must be an integer, castable to the type of x`
|
* @require $assignable(exp, $typeof(values::promote_int(x))) || values::@is_int(exp) `The input must be an integer, castable to the type of x`
|
||||||
**/
|
**/
|
||||||
macro pow(x, exp)
|
macro pow(x, exp)
|
||||||
{
|
{
|
||||||
$if types::is_floatlike($typeof(exp)):
|
$if types::is_floatlike($typeof(exp)):
|
||||||
return $$pow(x, ($typeof(x))exp);
|
return $$pow(values::promote_int(x), ($typeof(values::promote_int(x)))exp);
|
||||||
$else
|
$else
|
||||||
return $$pow_int(x, exp);
|
return $$pow_int(values::promote_int(x), exp);
|
||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,6 +449,16 @@ macro rint(x) => $$rint(x);
|
|||||||
**/
|
**/
|
||||||
macro round(x) => $$round(x);
|
macro round(x) => $$round(x);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||||
|
**/
|
||||||
|
macro round_to_decimals(x, int decimal_places)
|
||||||
|
{
|
||||||
|
var div = $$pow_int(($typeof(x))10, decimal_places);
|
||||||
|
return round(div * x) / div;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||||
**/
|
**/
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ union Quaternion
|
|||||||
Real[<4>] v;
|
Real[<4>] v;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro Quaternion identity() => { 0, 0, 0, 1 };
|
|
||||||
const Quaternion IDENTITY = { 0, 0, 0, 1 };
|
const Quaternion IDENTITY = { 0, 0, 0, 1 };
|
||||||
|
|
||||||
|
macro Quaternion identity() @deprecated("Replaced with QUATERNION_IDENTITY constant") => { 0, 0, 0, 1 };
|
||||||
macro Quaternion Quaternion.add(Quaternion a, Quaternion b) => Quaternion { .v = a.v + b.v };
|
macro Quaternion Quaternion.add(Quaternion a, Quaternion b) => Quaternion { .v = a.v + b.v };
|
||||||
macro Quaternion Quaternion.add_each(Quaternion a, Real b) => Quaternion { .v = a.v + b };
|
macro Quaternion Quaternion.add_each(Quaternion a, Real b) => Quaternion { .v = a.v + b };
|
||||||
macro Quaternion Quaternion.sub(Quaternion a, Quaternion b) => Quaternion { .v = a.v - b.v };
|
macro Quaternion Quaternion.sub(Quaternion a, Quaternion b) => Quaternion { .v = a.v - b.v };
|
||||||
@@ -70,33 +71,16 @@ fn Quaternion Quaternion.mul(a, Quaternion b)
|
|||||||
|
|
||||||
macro into_matrix(Quaternion* q, $Type) @private
|
macro into_matrix(Quaternion* q, $Type) @private
|
||||||
{
|
{
|
||||||
Quaternion q_norm = q.normalize();
|
Quaternion rotation = q.normalize();
|
||||||
|
var x = rotation.i;
|
||||||
|
var y = rotation.j;
|
||||||
|
var z = rotation.k;
|
||||||
|
var w = rotation.l;
|
||||||
|
|
||||||
var x = q_norm.i;
|
return $Type {
|
||||||
var y = q_norm.j;
|
1 - 2*y*y - 2*z*z, 2*x*y - 2*z*w, 2*x*z + 2*y*w, 0,
|
||||||
var z = q_norm.k;
|
2*x*y + 2*z*w, 1 - 2*x*x - 2*z*z, 2*y*z - 2*x*w, 0,
|
||||||
var w = q_norm.l;
|
2*x*z - 2*y*w, 2*y*z + 2*x*w , 1 - 2*x*x - 2*y*y, 0,
|
||||||
|
0.0, 0.0, 0.0, 1.0,
|
||||||
var xx = x + x;
|
};
|
||||||
var yy = y + y;
|
|
||||||
var zz = z + z;
|
|
||||||
|
|
||||||
var xxx = xx * x;
|
|
||||||
var xxy = xx * y;
|
|
||||||
var xxz = xx * z;
|
|
||||||
|
|
||||||
var yyy = yy * y;
|
|
||||||
var yyz = yy * z;
|
|
||||||
var zzz = zz * z;
|
|
||||||
|
|
||||||
var yyw = yy * w;
|
|
||||||
var zzw = zz * w;
|
|
||||||
var xxw = xx * w;
|
|
||||||
|
|
||||||
return $Type {
|
|
||||||
1 - yyy - zzz, xxy - zzw, xxz + yyw, 0,
|
|
||||||
xxy + zzw, 1 - xxx - zzz, yyz - xxw, 0,
|
|
||||||
xxz - yyw, yyz + zzw, 1.0 - xxx - yyy, 0,
|
|
||||||
0, 0, 0, 1,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
@@ -206,7 +206,7 @@ macro matrix_look_at($Type, eye, target, up) @private
|
|||||||
vx[0], vx[1], vx[2], - vx.dot(eye),
|
vx[0], vx[1], vx[2], - vx.dot(eye),
|
||||||
vy[0], vy[1], vy[2], - vy.dot(eye),
|
vy[0], vy[1], vy[2], - vy.dot(eye),
|
||||||
vz[0], vz[1], vz[2], - vz.dot(eye),
|
vz[0], vz[1], vz[2], - vz.dot(eye),
|
||||||
0, 0, 0, 1
|
0.0, 0.0, 0.0, 1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
lib/std/os/linux/heap.c3
Normal file
2
lib/std/os/linux/heap.c3
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
module std::os::linux @if(env::LINUX);
|
||||||
|
extern fn usz malloc_usable_size(void* ptr);
|
||||||
3
lib/std/os/macos/heap.c3
Normal file
3
lib/std/os/macos/heap.c3
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module std::os::darwin @if(env::DARWIN);
|
||||||
|
|
||||||
|
extern fn usz malloc_size(void* ptr);
|
||||||
3
lib/std/os/posix/heap.c3
Normal file
3
lib/std/os/posix/heap.c3
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module std::os::posix @if(env::POSIX);
|
||||||
|
|
||||||
|
extern fn CInt posix_memalign(void **memptr, usz alignment, usz size);
|
||||||
11
lib/std/os/win32/heap.c3
Normal file
11
lib/std/os/win32/heap.c3
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
module std::os::win32 @if(env::WIN32);
|
||||||
|
|
||||||
|
extern fn void* _aligned_malloc(usz size, usz alignment);
|
||||||
|
extern fn void* _aligned_realloc(void* memblock, usz size, usz alignment);
|
||||||
|
extern fn void* _aligned_recalloc(void* memblock, usz size, usz alignment);
|
||||||
|
extern fn void _aligned_free(void* memblock);
|
||||||
|
extern fn void _aligned_msize(void* memblock, usz alignment, usz offset);
|
||||||
|
extern fn void* _aligned_offset_malloc(usz size, usz alignment, usz offset);
|
||||||
|
extern fn void* _aligned_offset_realloc(void* memblock, usz size, usz alignment, usz offset);
|
||||||
|
extern fn void* _aligned_offset_recalloc(void* memblock, usz size, usz alignment, usz offset);
|
||||||
|
extern fn usz _msize(void* memblock);
|
||||||
@@ -1,5 +1,26 @@
|
|||||||
# C3C Release Notes
|
# C3C Release Notes
|
||||||
|
|
||||||
|
## 0.5.5 Change list
|
||||||
|
|
||||||
|
### Changes / improvements
|
||||||
|
- Disallow multiple `_` in a row in digits, e.g. `1__000`.
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Struct/union members now correctly rejects members without storage size #1147.
|
||||||
|
- `math::pow` will now correctly promote integer arguments.
|
||||||
|
- Pointer difference would fail where alignment != size (structs etc) #1150
|
||||||
|
- Fixed array calculation for npot2 vectors.
|
||||||
|
- $$memcpy_inline and $$memset_inline fixed.
|
||||||
|
|
||||||
|
### Stdlib changes
|
||||||
|
- Added `new_aligned` and `alloc_aligned` functions to prevent accidental under-alignment when allocating simd.
|
||||||
|
- Fixes to realloc of aligned allocations
|
||||||
|
- Use native Windows calls on aligned allocations on Windows.
|
||||||
|
- mem::copy_inline, mem::clear_inline and mem::set_inline added.
|
||||||
|
- mem::copy / clear / set no longer has an `$inline` attribute.
|
||||||
|
- Native aligned libc malloc on Windows & POSIX.
|
||||||
|
- Simplification of the allocator interface.
|
||||||
|
|
||||||
## 0.5.4 Change list
|
## 0.5.4 Change list
|
||||||
|
|
||||||
### Changes / improvements
|
### Changes / improvements
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ B64 [ \t\v\n\f]?[A-Za-z0-9+/][ \t\v\n\fA-Za-z0-9+/=]+
|
|||||||
HEX [ \t\v\n\f]?[A-Fa-f0-9][ \t\v\n\fA-Fa-f0-9]+
|
HEX [ \t\v\n\f]?[A-Fa-f0-9][ \t\v\n\fA-Fa-f0-9]+
|
||||||
INTTYPE ([ui](8|16|32|64|128)|[Uu][Ll]?|[Ll])
|
INTTYPE ([ui](8|16|32|64|128)|[Uu][Ll]?|[Ll])
|
||||||
REALTYPE ([f](8|16|32|64|128)?)
|
REALTYPE ([f](8|16|32|64|128)?)
|
||||||
INT {D}(_*{D})*
|
INT {D}(_?{D})*
|
||||||
HINT {H}(_*{H})*
|
HINT {H}(_?{H})*
|
||||||
OINT {O}(_*{O})*
|
OINT {O}(_?{O})*
|
||||||
BINT {B}(_*{B})*
|
BINT {B}(_?{B})*
|
||||||
|
|
||||||
%x COMMENT RAW_STRING
|
%x COMMENT RAW_STRING
|
||||||
|
|
||||||
|
|||||||
17
resources/testfragments/diagnostic.c3
Normal file
17
resources/testfragments/diagnostic.c3
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import std;
|
||||||
|
struct Overalign
|
||||||
|
{
|
||||||
|
double[<33>] x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void main()
|
||||||
|
{
|
||||||
|
List(<Overalign>) l;
|
||||||
|
Overalign y;
|
||||||
|
for (int i = 0; i < 1000; i++)
|
||||||
|
{
|
||||||
|
io::printfn("Pushing %d", i);
|
||||||
|
l.push(y);
|
||||||
|
if (i > 3) io::printfn("Diff %d", (usz)l.get_ref(i) - (usz)l.get_ref(i - (usz)1));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
module topologicalsort;
|
module topologicalsort;
|
||||||
|
import std::io;
|
||||||
extern fn void printf(char* x, ...);
|
|
||||||
|
|
||||||
struct InputPair
|
struct InputPair
|
||||||
{
|
{
|
||||||
@@ -24,10 +23,9 @@ struct TopoList
|
|||||||
fn void sort(InputPair[] pairs, uint elements)
|
fn void sort(InputPair[] pairs, uint elements)
|
||||||
{
|
{
|
||||||
InputPair[] result = mem::alloc_array(InputPair, pairs.len);
|
InputPair[] result = mem::alloc_array(InputPair, pairs.len);
|
||||||
TopoList* top = mem::alloc_array(TopoList, elements);
|
TopoList* top = mem::new_array(TopoList, elements);
|
||||||
for (int i = 0; i < pairs.len; i++)
|
foreach (pair : pairs)
|
||||||
{
|
{
|
||||||
InputPair pair = pairs[i];
|
|
||||||
assert(pair.value >= 0 && pair.value < elements);
|
assert(pair.value >= 0 && pair.value < elements);
|
||||||
assert(pair.successor >= 0 && pair.successor < elements);
|
assert(pair.successor >= 0 && pair.successor < elements);
|
||||||
top[pair.successor].count++;
|
top[pair.successor].count++;
|
||||||
@@ -60,10 +58,10 @@ fn void sort(InputPair[] pairs, uint elements)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
printf("Got %d elements.\n", count);
|
io::printfn("Got %d elements.", count);
|
||||||
for (int i = 0; i < count; i++)
|
foreach (val : intout[:count])
|
||||||
{
|
{
|
||||||
printf("%d\n", intout[i]);
|
io::printn(val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2837,6 +2837,7 @@ INLINE bool type_is_invalid_storage_type(Type *type)
|
|||||||
if (type == type_wildcard_optional) return true;
|
if (type == type_wildcard_optional) return true;
|
||||||
switch (type->type_kind)
|
switch (type->type_kind)
|
||||||
{
|
{
|
||||||
|
case TYPE_VOID:
|
||||||
case TYPE_MEMBER:
|
case TYPE_MEMBER:
|
||||||
case TYPE_UNTYPED_LIST:
|
case TYPE_UNTYPED_LIST:
|
||||||
case TYPE_TYPEINFO:
|
case TYPE_TYPEINFO:
|
||||||
|
|||||||
@@ -417,6 +417,11 @@ static bool scan_number_suffix(Lexer *lexer, bool *is_float)
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define NEXT_AND_CHECK_NO_MULTIPLE_(lexer__) \
|
||||||
|
do { if (next(lexer__) == '_' && prev(lexer__) == '_') { \
|
||||||
|
return add_error_token_at_current(lexer__, "Multiple consecutive '_' are not allowed."); \
|
||||||
|
} } while(0);
|
||||||
/**
|
/**
|
||||||
* Parsing octals. Here we depart from the (error prone) C style octals with initial zero e.g. 0231
|
* Parsing octals. Here we depart from the (error prone) C style octals with initial zero e.g. 0231
|
||||||
* Instead we only support 0o prefix like 0o231. Note that lexing here doesn't actually parse the
|
* Instead we only support 0o prefix like 0o231. Note that lexing here doesn't actually parse the
|
||||||
@@ -429,7 +434,8 @@ static bool scan_oct(Lexer *lexer)
|
|||||||
return add_error_token_at_current(lexer, "An expression starting with '0o' should be followed by octal numbers (0-7).");
|
return add_error_token_at_current(lexer, "An expression starting with '0o' should be followed by octal numbers (0-7).");
|
||||||
}
|
}
|
||||||
next(lexer);
|
next(lexer);
|
||||||
while (char_is_oct_or_(peek(lexer))) next(lexer);
|
while (char_is_oct_or_(peek(lexer))) NEXT_AND_CHECK_NO_MULTIPLE_(lexer);
|
||||||
|
|
||||||
if (char_is_digit(peek(lexer)))
|
if (char_is_digit(peek(lexer)))
|
||||||
{
|
{
|
||||||
return add_error_token_at_current(lexer, "An expression starting with '0o' should be followed by octal numbers (0-7).");
|
return add_error_token_at_current(lexer, "An expression starting with '0o' should be followed by octal numbers (0-7).");
|
||||||
@@ -453,7 +459,7 @@ static bool scan_binary(Lexer *lexer)
|
|||||||
return add_error_token_at_current(lexer, "An expression starting with '0b' should be followed by binary digits (0-1).");
|
return add_error_token_at_current(lexer, "An expression starting with '0b' should be followed by binary digits (0-1).");
|
||||||
}
|
}
|
||||||
next(lexer);
|
next(lexer);
|
||||||
while (char_is_binary_or_(peek(lexer))) next(lexer);
|
while (char_is_binary_or_(peek(lexer))) NEXT_AND_CHECK_NO_MULTIPLE_(lexer);
|
||||||
if (char_is_digit(peek((lexer))))
|
if (char_is_digit(peek((lexer))))
|
||||||
{
|
{
|
||||||
return add_error_token_at_current(lexer, "An expression starting with '0b' should be followed by binary digits (0-1).");
|
return add_error_token_at_current(lexer, "An expression starting with '0b' should be followed by binary digits (0-1).");
|
||||||
@@ -512,7 +518,7 @@ static inline bool scan_hex(Lexer *lexer)
|
|||||||
return add_error_token_at_current(lexer, "'0x' starts a hexadecimal number, so the next character should be 0-9, a-f or A-F.");
|
return add_error_token_at_current(lexer, "'0x' starts a hexadecimal number, so the next character should be 0-9, a-f or A-F.");
|
||||||
}
|
}
|
||||||
next(lexer);
|
next(lexer);
|
||||||
while (char_is_hex_or_(peek(lexer))) next(lexer);
|
while (char_is_hex_or_(peek(lexer))) NEXT_AND_CHECK_NO_MULTIPLE_(lexer);
|
||||||
bool is_float = false;
|
bool is_float = false;
|
||||||
if (peek(lexer) == '.' && peek_next(lexer) != '.')
|
if (peek(lexer) == '.' && peek_next(lexer) != '.')
|
||||||
{
|
{
|
||||||
@@ -521,7 +527,7 @@ static inline bool scan_hex(Lexer *lexer)
|
|||||||
char c = peek(lexer);
|
char c = peek(lexer);
|
||||||
if (c == '_') return add_error_token_at_current(lexer, "'_' is not allowed directly after decimal point, try removing it.");
|
if (c == '_') return add_error_token_at_current(lexer, "'_' is not allowed directly after decimal point, try removing it.");
|
||||||
if (char_is_hex(c)) next(lexer);
|
if (char_is_hex(c)) next(lexer);
|
||||||
while (char_is_hex_or_(peek(lexer))) next(lexer);
|
while (char_is_hex_or_(peek(lexer))) NEXT_AND_CHECK_NO_MULTIPLE_(lexer);
|
||||||
}
|
}
|
||||||
char c = peek(lexer);
|
char c = peek(lexer);
|
||||||
if (c == 'p' || c == 'P')
|
if (c == 'p' || c == 'P')
|
||||||
@@ -547,7 +553,7 @@ static inline bool scan_dec(Lexer *lexer)
|
|||||||
|
|
||||||
// Walk through the digits, we don't need to worry about
|
// Walk through the digits, we don't need to worry about
|
||||||
// initial _ because we only call this if we have a digit initially.
|
// initial _ because we only call this if we have a digit initially.
|
||||||
while (char_is_digit_or_(peek(lexer))) next(lexer);
|
while (char_is_digit_or_(peek(lexer))) NEXT_AND_CHECK_NO_MULTIPLE_(lexer);
|
||||||
|
|
||||||
// Assume no float.
|
// Assume no float.
|
||||||
bool is_float = false;
|
bool is_float = false;
|
||||||
@@ -565,7 +571,7 @@ static inline bool scan_dec(Lexer *lexer)
|
|||||||
if (c == '_') return add_error_token_at_current(lexer, "'_' is not allowed directly after decimal point, try removing it.");
|
if (c == '_') return add_error_token_at_current(lexer, "'_' is not allowed directly after decimal point, try removing it.");
|
||||||
// Now walk until we see no more digits.
|
// Now walk until we see no more digits.
|
||||||
// This allows 123. as a floating point number.
|
// This allows 123. as a floating point number.
|
||||||
while (char_is_digit_or_(peek(lexer))) next(lexer);
|
while (char_is_digit_or_(peek(lexer))) NEXT_AND_CHECK_NO_MULTIPLE_(lexer);
|
||||||
}
|
}
|
||||||
char c = peek(lexer);
|
char c = peek(lexer);
|
||||||
// We might have an exponential. We allow 123e1 and 123.e1 as floating point, so
|
// We might have an exponential. We allow 123e1 and 123.e1 as floating point, so
|
||||||
|
|||||||
@@ -1693,6 +1693,6 @@ TypeSize llvm_store_size(GenContext *c, LLVMTypeRef type)
|
|||||||
|
|
||||||
TypeSize llvm_alloc_size(GenContext *c, LLVMTypeRef type)
|
TypeSize llvm_alloc_size(GenContext *c, LLVMTypeRef type)
|
||||||
{
|
{
|
||||||
return (TypeSize)aligned_offset((AlignSize)LLVMStoreSizeOfType(c->target_data, type), llvm_abi_alignment(c, type));
|
return (TypeSize)aligned_offset((AlignSize)LLVMABISizeOfType(c->target_data, type), llvm_abi_alignment(c, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4135,7 +4135,7 @@ void llvm_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValue *lhs
|
|||||||
{
|
{
|
||||||
val = LLVMBuildSub(c->builder, LLVMBuildPtrToInt(c->builder, lhs_value, int_vec_type, ""),
|
val = LLVMBuildSub(c->builder, LLVMBuildPtrToInt(c->builder, lhs_value, int_vec_type, ""),
|
||||||
LLVMBuildPtrToInt(c->builder, rhs_value, int_vec_type, ""), "");
|
LLVMBuildPtrToInt(c->builder, rhs_value, int_vec_type, ""), "");
|
||||||
LLVMValueRef divisor = llvm_emit_const_vector(llvm_const_int(c, type_isz, type_abi_alignment(element_type)), len);
|
LLVMValueRef divisor = llvm_emit_const_vector(llvm_const_int(c, type_isz, type_size(element_type)), len);
|
||||||
val = LLVMBuildExactSDiv(c->builder, val, divisor, "");
|
val = LLVMBuildExactSDiv(c->builder, val, divisor, "");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -4150,7 +4150,7 @@ void llvm_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValue *lhs
|
|||||||
LLVMTypeRef int_type = llvm_get_type(c, type_isz);
|
LLVMTypeRef int_type = llvm_get_type(c, type_isz);
|
||||||
val = LLVMBuildSub(c->builder, LLVMBuildPtrToInt(c->builder, lhs_value, int_type, ""),
|
val = LLVMBuildSub(c->builder, LLVMBuildPtrToInt(c->builder, lhs_value, int_type, ""),
|
||||||
LLVMBuildPtrToInt(c->builder, rhs_value, int_type, ""), "");
|
LLVMBuildPtrToInt(c->builder, rhs_value, int_type, ""), "");
|
||||||
val = LLVMBuildExactSDiv(c->builder, val, llvm_const_int(c, type_isz, type_abi_alignment(lhs_type->pointer)), "");
|
val = LLVMBuildExactSDiv(c->builder, val, llvm_const_int(c, type_isz, type_size(lhs_type->pointer)), "");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
rhs_value = LLVMBuildNeg(c->builder, rhs_value, "");
|
rhs_value = LLVMBuildNeg(c->builder, rhs_value, "");
|
||||||
@@ -4992,7 +4992,7 @@ LLVMValueRef llvm_emit_array_gep_raw_index(GenContext *c, LLVMValueRef ptr, LLVM
|
|||||||
{
|
{
|
||||||
index_val = llvm_zext_trunc(c, index_val, idx_type);
|
index_val = llvm_zext_trunc(c, index_val, idx_type);
|
||||||
}
|
}
|
||||||
*alignment = type_min_alignment(llvm_store_size(c, element_type), array_alignment);
|
*alignment = type_min_alignment(llvm_abi_size(c, element_type), array_alignment);
|
||||||
return llvm_emit_pointer_inbounds_gep_raw(c, element_type, ptr, index_val);
|
return llvm_emit_pointer_inbounds_gep_raw(c, element_type, ptr, index_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5041,7 +5041,7 @@ LLVMValueRef llvm_emit_const_vector(LLVMValueRef value, ArraySize len)
|
|||||||
|
|
||||||
LLVMValueRef llvm_ptr_mult(GenContext *c, LLVMValueRef offset, LLVMTypeRef pointee_type)
|
LLVMValueRef llvm_ptr_mult(GenContext *c, LLVMValueRef offset, LLVMTypeRef pointee_type)
|
||||||
{
|
{
|
||||||
ByteSize size = llvm_store_size(c, pointee_type);
|
ByteSize size = llvm_abi_size(c, pointee_type);
|
||||||
if (size == 1) return offset;
|
if (size == 1) return offset;
|
||||||
|
|
||||||
LLVMTypeRef offset_type = LLVMTypeOf(offset);
|
LLVMTypeRef offset_type = LLVMTypeOf(offset);
|
||||||
@@ -5062,7 +5062,7 @@ LLVMValueRef llvm_emit_pointer_gep_raw(GenContext *c, LLVMTypeRef pointee_type,
|
|||||||
{
|
{
|
||||||
return llvm_emit_ptradd_raw(c, ptr, llvm_ptr_mult(c, offset, pointee_type), 1);
|
return llvm_emit_ptradd_raw(c, ptr, llvm_ptr_mult(c, offset, pointee_type), 1);
|
||||||
}
|
}
|
||||||
return llvm_emit_ptradd_raw(c, ptr, offset, llvm_store_size(c, pointee_type));
|
return llvm_emit_ptradd_raw(c, ptr, offset, llvm_abi_size(c, pointee_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
LLVMValueRef llvm_emit_pointer_inbounds_gep_raw(GenContext *c, LLVMTypeRef pointee_type, LLVMValueRef ptr, LLVMValueRef offset)
|
LLVMValueRef llvm_emit_pointer_inbounds_gep_raw(GenContext *c, LLVMTypeRef pointee_type, LLVMValueRef ptr, LLVMValueRef offset)
|
||||||
@@ -5071,7 +5071,7 @@ LLVMValueRef llvm_emit_pointer_inbounds_gep_raw(GenContext *c, LLVMTypeRef point
|
|||||||
{
|
{
|
||||||
return llvm_emit_ptradd_inbounds_raw(c, ptr, llvm_ptr_mult(c, offset, pointee_type), 1);
|
return llvm_emit_ptradd_inbounds_raw(c, ptr, llvm_ptr_mult(c, offset, pointee_type), 1);
|
||||||
}
|
}
|
||||||
return llvm_emit_ptradd_inbounds_raw(c, ptr, offset, llvm_store_size(c, pointee_type));
|
return llvm_emit_ptradd_inbounds_raw(c, ptr, offset, llvm_abi_size(c, pointee_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
LLVMValueRef llvm_emit_const_ptradd_inbounds_raw(GenContext *c, LLVMValueRef ptr, ByteSize offset)
|
LLVMValueRef llvm_emit_const_ptradd_inbounds_raw(GenContext *c, LLVMValueRef ptr, ByteSize offset)
|
||||||
|
|||||||
@@ -421,8 +421,15 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr)
|
|||||||
if (!sema_check_builtin_args(args, (BuiltinArg[]) { BA_INTLIKE }, 1)) return false;
|
if (!sema_check_builtin_args(args, (BuiltinArg[]) { BA_INTLIKE }, 1)) return false;
|
||||||
rtype = args[0]->type->canonical;
|
rtype = args[0]->type->canonical;
|
||||||
break;
|
break;
|
||||||
case BUILTIN_MEMCOPY:
|
|
||||||
case BUILTIN_MEMCOPY_INLINE:
|
case BUILTIN_MEMCOPY_INLINE:
|
||||||
|
assert(arg_count == 6);
|
||||||
|
if (!sema_check_builtin_args(args,
|
||||||
|
(BuiltinArg[]) { BA_POINTER, BA_POINTER, BA_SIZE, BA_BOOL, BA_SIZE, BA_SIZE },
|
||||||
|
6)) return false;
|
||||||
|
if (!sema_check_builtin_args_const(&args[2], 4)) return false;
|
||||||
|
rtype = type_void;
|
||||||
|
break;
|
||||||
|
case BUILTIN_MEMCOPY:
|
||||||
case BUILTIN_MEMMOVE:
|
case BUILTIN_MEMMOVE:
|
||||||
assert(arg_count == 6);
|
assert(arg_count == 6);
|
||||||
if (!sema_check_builtin_args(args,
|
if (!sema_check_builtin_args(args,
|
||||||
@@ -432,11 +439,17 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr)
|
|||||||
rtype = type_void;
|
rtype = type_void;
|
||||||
break;
|
break;
|
||||||
case BUILTIN_MEMSET:
|
case BUILTIN_MEMSET:
|
||||||
|
assert(arg_count == 5);
|
||||||
|
if (!sema_check_builtin_args(args, (BuiltinArg[]) { BA_POINTER, BA_CHAR, BA_SIZE, BA_BOOL, BA_SIZE },
|
||||||
|
5)) return false;
|
||||||
|
if (!sema_check_builtin_args_const(&args[3], 2)) return false;
|
||||||
|
rtype = type_void;
|
||||||
|
break;
|
||||||
case BUILTIN_MEMSET_INLINE:
|
case BUILTIN_MEMSET_INLINE:
|
||||||
assert(arg_count == 5);
|
assert(arg_count == 5);
|
||||||
if (!sema_check_builtin_args(args, (BuiltinArg[]) { BA_POINTER, BA_CHAR, BA_SIZE, BA_BOOL, BA_SIZE },
|
if (!sema_check_builtin_args(args, (BuiltinArg[]) { BA_POINTER, BA_CHAR, BA_SIZE, BA_BOOL, BA_SIZE },
|
||||||
5)) return false;
|
5)) return false;
|
||||||
if (!sema_check_builtin_args_const(&args[3], 2)) return false;
|
if (!sema_check_builtin_args_const(&args[2], 3)) return false;
|
||||||
rtype = type_void;
|
rtype = type_void;
|
||||||
break;
|
break;
|
||||||
case BUILTIN_BITREVERSE:
|
case BUILTIN_BITREVERSE:
|
||||||
|
|||||||
@@ -225,13 +225,22 @@ static inline bool sema_analyse_struct_member(SemaContext *context, Decl *parent
|
|||||||
switch (decl->decl_kind)
|
switch (decl->decl_kind)
|
||||||
{
|
{
|
||||||
case DECL_VAR:
|
case DECL_VAR:
|
||||||
|
{
|
||||||
assert(decl->var.kind == VARDECL_MEMBER);
|
assert(decl->var.kind == VARDECL_MEMBER);
|
||||||
decl->resolve_status = RESOLVE_RUNNING;
|
decl->resolve_status = RESOLVE_RUNNING;
|
||||||
// Inferred types are not strictly allowed, but we use the int[*] for the flexible array member.
|
// Inferred types are not strictly allowed, but we use the int[*] for the flexible array member.
|
||||||
if (!sema_resolve_type_info(context, type_infoptrzero(decl->var.type_info), RESOLVE_TYPE_ALLOW_FLEXIBLE)) return decl_poison(decl);
|
assert(type_infoptrzero(decl->var.type_info));
|
||||||
decl->type = typeget(decl->var.type_info);
|
TypeInfo *type_info = type_infoptr(decl->var.type_info);
|
||||||
|
if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_ALLOW_FLEXIBLE)) return decl_poison(decl);
|
||||||
|
Type *type = type_info->type;
|
||||||
|
if (type_is_invalid_storage_type(type))
|
||||||
|
{
|
||||||
|
RETURN_SEMA_ERROR(type_info, "Members cannot be of type %s.", type_quoted_error_string(type));
|
||||||
|
}
|
||||||
|
decl->type = type;
|
||||||
decl->resolve_status = RESOLVE_DONE;
|
decl->resolve_status = RESOLVE_DONE;
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
case DECL_STRUCT:
|
case DECL_STRUCT:
|
||||||
case DECL_UNION:
|
case DECL_UNION:
|
||||||
case DECL_BITSTRUCT:
|
case DECL_BITSTRUCT:
|
||||||
|
|||||||
@@ -1276,7 +1276,7 @@ INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee,
|
|||||||
{
|
{
|
||||||
if (!sema_analyse_expr(context, arg)) return false;
|
if (!sema_analyse_expr(context, arg)) return false;
|
||||||
Type *type = arg->type;
|
Type *type = arg->type;
|
||||||
if (type_is_invalid_storage_type(type) || type == type_void)
|
if (type_is_invalid_storage_type(type))
|
||||||
{
|
{
|
||||||
RETURN_SEMA_ERROR(arg, "A value of type %s cannot be passed as a variadic argument.",
|
RETURN_SEMA_ERROR(arg, "A value of type %s cannot be passed as a variadic argument.",
|
||||||
type_quoted_error_string(type));
|
type_quoted_error_string(type));
|
||||||
@@ -1489,7 +1489,7 @@ static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!sema_analyse_expr(context, val)) return false;
|
if (!sema_analyse_expr(context, val)) return false;
|
||||||
if (type_is_invalid_storage_type(val->type) || val->type == type_void)
|
if (type_is_invalid_storage_type(val->type))
|
||||||
{
|
{
|
||||||
SEMA_ERROR(val, "A value of type %s cannot be passed as a variadic argument.",
|
SEMA_ERROR(val, "A value of type %s cannot be passed as a variadic argument.",
|
||||||
type_quoted_error_string(val->type));
|
type_quoted_error_string(val->type));
|
||||||
@@ -1561,7 +1561,7 @@ static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call
|
|||||||
if (!type_is_any_interface_ptr(arg->type)) expr_insert_addr(arg);
|
if (!type_is_any_interface_ptr(arg->type)) expr_insert_addr(arg);
|
||||||
*optional |= IS_OPTIONAL(arg);
|
*optional |= IS_OPTIONAL(arg);
|
||||||
if (!sema_call_check_contract_param_match(context, param, arg)) return false;
|
if (!sema_call_check_contract_param_match(context, param, arg)) return false;
|
||||||
if (type_is_invalid_storage_type(type) || type == type_void)
|
if (type_is_invalid_storage_type(type))
|
||||||
{
|
{
|
||||||
SEMA_ERROR(arg, "A value of type %s cannot be passed by reference.", type_quoted_error_string(type));
|
SEMA_ERROR(arg, "A value of type %s cannot be passed by reference.", type_quoted_error_string(type));
|
||||||
return false;
|
return false;
|
||||||
@@ -6391,7 +6391,7 @@ static inline bool sema_expr_analyse_taddr(SemaContext *context, Expr *expr, boo
|
|||||||
if (!sema_analyse_expr(context, inner)) return false;
|
if (!sema_analyse_expr(context, inner)) return false;
|
||||||
|
|
||||||
Type *type = inner->type;
|
Type *type = inner->type;
|
||||||
if (type_is_invalid_storage_type(type) || type == type_void)
|
if (type_is_invalid_storage_type(type))
|
||||||
{
|
{
|
||||||
if (failed_ref)
|
if (failed_ref)
|
||||||
{
|
{
|
||||||
@@ -7141,7 +7141,7 @@ static inline bool sema_expr_analyse_ct_alignof(SemaContext *context, Expr *expr
|
|||||||
Decl *decl = sema_expr_analyse_var_path(context, main_var);
|
Decl *decl = sema_expr_analyse_var_path(context, main_var);
|
||||||
if (!decl) return false;
|
if (!decl) return false;
|
||||||
Type *type = decl->type;
|
Type *type = decl->type;
|
||||||
if (type_is_invalid_storage_type(type) || type == type_void)
|
if (type_is_invalid_storage_type(type))
|
||||||
{
|
{
|
||||||
SEMA_ERROR(main_var, "Cannot use '$alignof' on type %s.", type_quoted_error_string(type));
|
SEMA_ERROR(main_var, "Cannot use '$alignof' on type %s.", type_quoted_error_string(type));
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ INLINE bool sema_resolve_evaltype(SemaContext *context, TypeInfo *type_info, Res
|
|||||||
}
|
}
|
||||||
TypeInfo *inner_type = inner->type_expr;
|
TypeInfo *inner_type = inner->type_expr;
|
||||||
if (!sema_resolve_type(context, inner_type, resolve_kind)) return false;
|
if (!sema_resolve_type(context, inner_type, resolve_kind)) return false;
|
||||||
if (type_is_invalid_storage_type(inner_type->type))
|
if (inner_type->type != type_void && type_is_invalid_storage_type(inner_type->type))
|
||||||
{
|
{
|
||||||
SEMA_ERROR(expr, "Compile-time types may not be used with $evaltype.");
|
SEMA_ERROR(expr, "Compile-time types may not be used with $evaltype.");
|
||||||
return false;
|
return false;
|
||||||
@@ -313,19 +313,20 @@ INLINE bool sema_resolve_typeof(SemaContext *context, TypeInfo *type_info)
|
|||||||
{
|
{
|
||||||
Expr *expr = type_info->unresolved_type_expr;
|
Expr *expr = type_info->unresolved_type_expr;
|
||||||
if (!sema_analyse_expr(context, expr)) return false;
|
if (!sema_analyse_expr(context, expr)) return false;
|
||||||
if (type_is_invalid_storage_type(expr->type))
|
Type *expr_type = expr->type;
|
||||||
|
if (expr_type != type_void && type_is_invalid_storage_type(expr->type))
|
||||||
{
|
{
|
||||||
if (expr->type == type_wildcard)
|
if (expr_type == type_wildcard)
|
||||||
{
|
{
|
||||||
RETURN_SEMA_ERROR(expr, "This expression has no concrete type.");
|
RETURN_SEMA_ERROR(expr, "This expression has no concrete type.");
|
||||||
}
|
}
|
||||||
if (expr->type == type_wildcard_optional)
|
if (expr_type == type_wildcard_optional)
|
||||||
{
|
{
|
||||||
RETURN_SEMA_ERROR(expr, "This optional expression is untyped.");
|
RETURN_SEMA_ERROR(expr, "This optional expression is untyped.");
|
||||||
}
|
}
|
||||||
RETURN_SEMA_ERROR(expr, "Expected a regular runtime expression here.");
|
RETURN_SEMA_ERROR(expr, "Expected a regular runtime expression here.");
|
||||||
}
|
}
|
||||||
type_info->type = expr->type;
|
type_info->type = expr_type;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "compiler_internal.h"
|
#include "compiler_internal.h"
|
||||||
|
|
||||||
extern void LLVMSetTargetMachineUseInitArray(LLVMTargetMachineRef ref, bool use_init_array);
|
extern void LLVMSetTargetMachineUseInitArray(LLVMTargetMachineRef ref, bool use_init_array);
|
||||||
|
static bool x64features_contains(X86Features *cpu_features, X86Feature feature);
|
||||||
static ObjectFormatType object_format_from_os(OsType os, ArchType arch_type);
|
static ObjectFormatType object_format_from_os(OsType os, ArchType arch_type);
|
||||||
static unsigned arch_pointer_bit_width(OsType os, ArchType arch);
|
static unsigned arch_pointer_bit_width(OsType os, ArchType arch);
|
||||||
static ArchType arch_from_llvm_string(StringSlice string);
|
static ArchType arch_from_llvm_string(StringSlice string);
|
||||||
@@ -47,7 +47,7 @@ bool arch_is_wasm(ArchType type)
|
|||||||
return type == ARCH_TYPE_WASM32 || type == ARCH_TYPE_WASM64;
|
return type == ARCH_TYPE_WASM32 || type == ARCH_TYPE_WASM64;
|
||||||
}
|
}
|
||||||
|
|
||||||
static AlignSize os_arch_max_alignment_of_vector(OsType os, ArchType arch, EnvironmentType type, ARMVariant variant)
|
static AlignSize os_arch_max_alignment_of_vector(OsType os, ArchType arch, EnvironmentType type, ARMVariant variant, X86Features* features)
|
||||||
{
|
{
|
||||||
switch (arch)
|
switch (arch)
|
||||||
{
|
{
|
||||||
@@ -71,12 +71,14 @@ static AlignSize os_arch_max_alignment_of_vector(OsType os, ArchType arch, Envir
|
|||||||
case ARCH_TYPE_X86:
|
case ARCH_TYPE_X86:
|
||||||
if (os == OS_TYPE_WIN32) /* COFF */
|
if (os == OS_TYPE_WIN32) /* COFF */
|
||||||
{
|
{
|
||||||
return 8192;
|
return 8192 / 8;
|
||||||
}
|
}
|
||||||
if (os_is_apple(os))
|
if (os_is_apple(os))
|
||||||
{
|
{
|
||||||
// With AVX512 - 512, AVX - 256 otherwise AVX - 128
|
// With AVX512 - 512, AVX - 256 otherwise AVX - 128
|
||||||
return 256;
|
if (x64features_contains(features, X86_FEAT_AVX512F)) return 512 / 8;
|
||||||
|
if (x64features_contains(features, X86_FEAT_AVX)) return 256 / 8;
|
||||||
|
return 128 / 8;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -393,6 +395,7 @@ static char *x86_feature_name[] = {
|
|||||||
[X86_FEAT_AVXVNNIINT8] = "avxvnniint8",
|
[X86_FEAT_AVXVNNIINT8] = "avxvnniint8",
|
||||||
[X86_FEAT_AVXVNNIINT16] = "avxvnniint16",
|
[X86_FEAT_AVXVNNIINT16] = "avxvnniint16",
|
||||||
};
|
};
|
||||||
|
|
||||||
static X86Feature x86feature_from_string(const char *str)
|
static X86Feature x86feature_from_string(const char *str)
|
||||||
{
|
{
|
||||||
for (int i = 0; i <= X86_FEATURE_LAST; i++)
|
for (int i = 0; i <= X86_FEATURE_LAST; i++)
|
||||||
@@ -1928,7 +1931,11 @@ void target_setup(BuildTarget *target)
|
|||||||
platform_target.abi = ABI_UNKNOWN;
|
platform_target.abi = ABI_UNKNOWN;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
platform_target.align_max_vector = os_arch_max_alignment_of_vector(platform_target.os, platform_target.arch, platform_target.environment_type, platform_target.arm.variant);
|
platform_target.align_max_vector = os_arch_max_alignment_of_vector(platform_target.os,
|
||||||
|
platform_target.arch,
|
||||||
|
platform_target.environment_type,
|
||||||
|
platform_target.arm.variant,
|
||||||
|
&platform_target.x64.features);
|
||||||
platform_target.align_max_tls = os_arch_max_alignment_of_tls(platform_target.os,
|
platform_target.align_max_tls = os_arch_max_alignment_of_tls(platform_target.os,
|
||||||
platform_target.arch,
|
platform_target.arch,
|
||||||
platform_target.environment_type);
|
platform_target.environment_type);
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define COMPILER_VERSION "0.5.4"
|
#define COMPILER_VERSION "0.5.5"
|
||||||
|
|||||||
15
test/test_suite/expressions/underscore_errors.c3
Normal file
15
test/test_suite/expressions/underscore_errors.c3
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
fn void main()
|
||||||
|
{
|
||||||
|
int a = 1___0; // #error: consecutive
|
||||||
|
int a = 1_0;
|
||||||
|
float b = 1_0.3__4; // #error: consecutive
|
||||||
|
float b = 1_0.3_4;
|
||||||
|
int a2 = 0x1___0; // #error: consecutive
|
||||||
|
int a2 = 0x1_0;
|
||||||
|
float b2 = 0x1_0.3__4; // #error: consecutive
|
||||||
|
float b2 = 0x1_0.3_4;
|
||||||
|
int a3 = 0b1___0; // #error: consecutive
|
||||||
|
int a3 = 0b1_0;
|
||||||
|
int a3 = 0o1___0; // #error: consecutive
|
||||||
|
int a3 = 0o1_0;
|
||||||
|
}
|
||||||
7
test/test_suite/struct/struct_bad_member.c3
Normal file
7
test/test_suite/struct/struct_bad_member.c3
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
struct Foo {
|
||||||
|
void bar; // #error: Members cannot be of
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void main(String[] args) {
|
||||||
|
Foo foo;
|
||||||
|
}
|
||||||
20
test/unit/regression/pointer_diff.c3
Normal file
20
test/unit/regression/pointer_diff.c3
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import std;
|
||||||
|
struct Foo
|
||||||
|
{
|
||||||
|
int a,b,c,d;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void pointer_diff() @test
|
||||||
|
{
|
||||||
|
Foo* foo;
|
||||||
|
isz offset = &foo[1] - &foo[0];
|
||||||
|
assert(offset == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void pointer_add() @test
|
||||||
|
{
|
||||||
|
Foo* foo;
|
||||||
|
Foo* bar = foo + 2;
|
||||||
|
isz offset = bar - foo;
|
||||||
|
assert(offset == 2);
|
||||||
|
}
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
module vecpointer @test;
|
module vecpointer @test;
|
||||||
|
|
||||||
|
fn void pointer_npot2_size()
|
||||||
|
{
|
||||||
|
int[<9>][3] a;
|
||||||
|
assert((usz)&a[1] - (usz)&a[0] == 64);
|
||||||
|
}
|
||||||
|
|
||||||
fn void pointer_add_sub_diff()
|
fn void pointer_add_sub_diff()
|
||||||
{
|
{
|
||||||
int[5] a;
|
int[5] a;
|
||||||
|
|||||||
@@ -4,6 +4,20 @@ import std::collections::list;
|
|||||||
def IntList = List(<int>);
|
def IntList = List(<int>);
|
||||||
def PtrList = List(<void*>);
|
def PtrList = List(<void*>);
|
||||||
|
|
||||||
|
struct Overalign
|
||||||
|
{
|
||||||
|
double[<33>] x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void overaligned_type()
|
||||||
|
{
|
||||||
|
List(<Overalign>) l;
|
||||||
|
Overalign y;
|
||||||
|
for (int i = 0; i < 1000; i++) l.push(y);
|
||||||
|
assert((usz)l.get_ref(2) - (usz)l.get_ref(1) == Overalign.sizeof);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fn void! delete_contains_index()
|
fn void! delete_contains_index()
|
||||||
{
|
{
|
||||||
IntList test;
|
IntList test;
|
||||||
|
|||||||
@@ -144,4 +144,24 @@ fn void! test_trunc() @test
|
|||||||
$assert @typeis(math::trunc(f), float);
|
$assert @typeis(math::trunc(f), float);
|
||||||
double[<5>] vec = { -123.9, 123.9, 0.9, -0.9, 0 };
|
double[<5>] vec = { -123.9, 123.9, 0.9, -0.9, 0 };
|
||||||
assert(math::trunc(vec) == double[<5>] { -123, 123, 0, 0, 0 });
|
assert(math::trunc(vec) == double[<5>] { -123, 123, 0, 0, 0 });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void! test_round_decimals() @test
|
||||||
|
{
|
||||||
|
double d = 0.532451241142;
|
||||||
|
float d_f = 0.532451241142;
|
||||||
|
assert(math::round_to_decimals(d, 2) == 0.53);
|
||||||
|
assert(math::round_to_decimals(d, 5) == 0.53245);
|
||||||
|
|
||||||
|
assert(math::round_to_decimals(d_f, 2) == 0.53f);
|
||||||
|
assert(math::round_to_decimals(d_f, 5) == 0.53245f);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void! test() @test
|
||||||
|
{
|
||||||
|
double radians = math::deg_to_rad(45);
|
||||||
|
float radians_f = (float)math::deg_to_rad(45);
|
||||||
|
|
||||||
|
assert(math::round_to_decimals(radians, 3) == 0.785);
|
||||||
|
assert(math::round_to_decimals(radians_f, 3) == 0.785f);
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
module math_matrix @test;
|
module math_matrix @test;
|
||||||
import std::math;
|
import std::math;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn void test_mat4()
|
fn void test_mat4()
|
||||||
{
|
{
|
||||||
{|
|
{|
|
||||||
@@ -22,6 +20,69 @@ fn void test_mat4()
|
|||||||
Matrix4 value = { 56, 46, 36, 26, 152, 126, 100, 74, 56, 46, 36, 26, 152, 126, 100, 74 };
|
Matrix4 value = { 56, 46, 36, 26, 152, 126, 100, 74, 56, 46, 36, 26, 152, 126, 100, 74 };
|
||||||
assert(calc.m == value.m);
|
assert(calc.m == value.m);
|
||||||
|};
|
|};
|
||||||
|
|
||||||
|
{|
|
||||||
|
Matrix4 result = {
|
||||||
|
0.988936, 0.000000, -0.148340, -0.988936,
|
||||||
|
-0.014599, 0.995146, -0.097325, -2.970838,
|
||||||
|
0.147620, 0.098414, 0.984136, -20.765262,
|
||||||
|
0.000000, 0.000000, 0.000000, 1.000000
|
||||||
|
};
|
||||||
|
|
||||||
|
Matrix4f result_f = {
|
||||||
|
0.988936, 0.000000, -0.148340, -0.988936,
|
||||||
|
-0.014599, 0.995146, -0.097325, -2.970838,
|
||||||
|
0.147620, 0.098414, 0.984136, -20.765262,
|
||||||
|
0.000000, 0.000000, 0.000000, 1.000000
|
||||||
|
};
|
||||||
|
|
||||||
|
Matrix4 result_transposed = {
|
||||||
|
0.988936, -0.014599, 0.147620, 0.000000,
|
||||||
|
0.000000, 0.995146, 0.098414, 0.000000,
|
||||||
|
-0.148340, -0.097325, 0.984136, 0.000000,
|
||||||
|
-0.988936, -2.970838, -20.765262, 1.000000
|
||||||
|
};
|
||||||
|
|
||||||
|
Matrix4f result_transposed_f = {
|
||||||
|
0.988936, -0.014599, 0.147620, 0.000000,
|
||||||
|
0.000000, 0.995146, 0.098414, 0.000000,
|
||||||
|
-0.148340, -0.097325, 0.984136, 0.000000,
|
||||||
|
-0.988936, -2.970838, -20.765262, 1.000000
|
||||||
|
};
|
||||||
|
|
||||||
|
Matrix4 look_at = vector::matrix4_look_at({4.0, 5.0, 20.0}, {1.0, 3.0, 0.0}, {0.0, 1.0, 0.0});
|
||||||
|
Matrix4f look_at_f = vector::matrix4f_look_at({4.0, 5.0, 20.0}, {1.0, 3.0, 0.0}, {0.0, 1.0, 0.0});
|
||||||
|
|
||||||
|
assert(math::round_to_decimals((double[<16>])look_at.m, 4) == math::round_to_decimals((double[<16>])result.m, 4));
|
||||||
|
assert(math::round_to_decimals((float[<16>])look_at_f.m, 4) == math::round_to_decimals((float[<16>])result_f.m, 4));
|
||||||
|
|
||||||
|
assert(math::round_to_decimals((double[<16>])result_transposed.m, 4) == math::round_to_decimals((double[<16>])look_at.transpose().m, 4));
|
||||||
|
assert(math::round_to_decimals((float[<16>])result_transposed_f.m, 4) == math::round_to_decimals((float[<16>])look_at_f.transpose().m, 4));
|
||||||
|
|};
|
||||||
|
|
||||||
|
{|
|
||||||
|
Matrix4 result = {
|
||||||
|
1.857087, 0.000000, 0.000000,
|
||||||
|
0.000000, 0.000000, 2.414214,
|
||||||
|
0.000000, 0.000000, 0.000000, 0.000000,
|
||||||
|
-1.000200, -0.200020, 0.000000, 0.000000,
|
||||||
|
-1.000000, 0.000000
|
||||||
|
};
|
||||||
|
|
||||||
|
Matrix4f result_f = {
|
||||||
|
1.857087, 0.000000, 0.000000,
|
||||||
|
0.000000, 0.000000, 2.414214,
|
||||||
|
0.000000, 0.000000, 0.000000, 0.000000,
|
||||||
|
-1.000200, -0.200020, 0.000000, 0.000000,
|
||||||
|
-1.000000, 0.000000
|
||||||
|
};
|
||||||
|
|
||||||
|
Matrix4 perspective = matrix4_perspective(math::deg_to_rad(45), 1.3, 0.1, 1000);
|
||||||
|
Matrix4f perspective_f = matrix4f_perspective((float)math::deg_to_rad(45), 1.3, 0.1, 1000);
|
||||||
|
|
||||||
|
assert(math::round_to_decimals((double[<16>])result.m, 4) == math::round_to_decimals((double[<16>])perspective.m, 4));
|
||||||
|
assert(math::round_to_decimals((float[<16>])result_f.m, 4) == math::round_to_decimals((float[<16>])perspective_f.m, 4));
|
||||||
|
|};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
38
test/unit/stdlib/math/quaternion.c3
Normal file
38
test/unit/stdlib/math/quaternion.c3
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
module math_quaternion @test;
|
||||||
|
import std::math;
|
||||||
|
|
||||||
|
fn void test()
|
||||||
|
{
|
||||||
|
{|
|
||||||
|
Quaternion rotation = QUATERNION_IDENTITY;
|
||||||
|
Quaternionf rotation_f = QUATERNIONF_IDENTITY;
|
||||||
|
assert(rotation.v == {0,0,0,1});
|
||||||
|
assert(rotation.v == {0,0,0,1});
|
||||||
|
|};
|
||||||
|
|
||||||
|
{|
|
||||||
|
Quaternion rotation = QUATERNION_IDENTITY;
|
||||||
|
Matrix4 identity_matrix = MATRIX4_IDENTITY;
|
||||||
|
|
||||||
|
Quaternionf rotation_f = QUATERNIONF_IDENTITY;
|
||||||
|
Matrix4f identity_matrix_f = MATRIX4F_IDENTITY;
|
||||||
|
|
||||||
|
assert((double[<16>])rotation.to_matrix().m == (double[<16>])identity_matrix.m);
|
||||||
|
assert((float[<16>])rotation_f.to_matrixf().m == (float[<16>])identity_matrix_f.m);
|
||||||
|
|};
|
||||||
|
|
||||||
|
{|
|
||||||
|
Matrix4 result = {
|
||||||
|
0.428571, -0.285714, 0.857143, 0.000000,
|
||||||
|
0.857143, 0.428571, -0.285714, 0.000000,
|
||||||
|
-0.285714, 0.857143, 0.428571, 0.000000,
|
||||||
|
0.000000, 0.000000, 0.000000, 1.000000
|
||||||
|
};
|
||||||
|
|
||||||
|
Matrix4 rotation = Quaternion {0.5, 0.5, 0.5, 1}.to_matrix();
|
||||||
|
Matrix4f rotation_f = Quaternionf {0.5, 0.5, 0.5, 1}.to_matrixf();
|
||||||
|
|
||||||
|
assert(math::round_to_decimals((double[<16>])result.m, 2) == math::round_to_decimals((double[<16>])rotation.m, 2));
|
||||||
|
assert(math::round_to_decimals((float[<16>])result.m, 2) == math::round_to_decimals((float[<16>])rotation_f.m, 2));
|
||||||
|
|};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user