Allocator uses protocols. Fix bug where it was not possible to pass a ref variable as a ref variable. Correct codegen for !anyptr.

This commit is contained in:
Christoffer Lerno
2023-10-14 02:09:11 +02:00
committed by Christoffer Lerno
parent 54f32ed71b
commit 89d4c2cab7
31 changed files with 1439 additions and 1431 deletions

View File

@@ -40,7 +40,7 @@ fn void LinkedList.tinit(&self) => self.init(mem::temp()) @inline;
**/
macro void LinkedList.free_node(&self, Node* node) @private
{
self.allocator.free(node)!!;
self.allocator.free(node);
}
macro Node* LinkedList.alloc_node(&self) @private
{

View File

@@ -19,7 +19,7 @@ struct List (Printable)
}
/**
* @require using != null "A valid allocator must be provided"
* @require using "A valid allocator must be provided"
**/
fn void List.init(&self, usz initial_capacity = 16, Allocator* using = mem::heap())
{

View File

@@ -24,7 +24,7 @@ struct HashMap
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require !map.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
* @require using != null "The allocator must be non-null"
* @require (bool)using "The allocator must be non-null"
**/
fn void HashMap.init(&map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* using = mem::heap())
{
@@ -54,7 +54,7 @@ fn void HashMap.tinit(&map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load
**/
fn bool HashMap.is_initialized(&map)
{
return map.allocator != null;
return (bool)map.allocator;
}
fn void HashMap.init_from_map(&map, HashMap* other_map, Allocator* using = mem::heap())
@@ -354,7 +354,7 @@ fn void HashMap.put_for_create(&map, Key key, Value value) @private
fn void HashMap.free_internal(&map, void* ptr) @inline @private
{
map.allocator.free(ptr)!!;
map.allocator.free(ptr);
}
fn bool HashMap.remove_entry_for_key(&map, Key key) @private

View File

@@ -3,9 +3,8 @@
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem::allocator;
struct ArenaAllocator
struct ArenaAllocator (Allocator)
{
inline Allocator allocator;
char[] data;
usz used;
}
@@ -15,83 +14,46 @@ struct ArenaAllocator
**/
fn void ArenaAllocator.init(&self, char[] data)
{
self.function = &arena_allocator_function;
self.data = data;
self.used = 0;
}
fn void ArenaAllocator.reset(&self)
fn void ArenaAllocator.clear(&self)
{
self.used = 0;
}
module std::core::mem::allocator @private;
struct ArenaAllocatorHeader
struct ArenaAllocatorHeader @local
{
usz size;
char[*] data;
}
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require data `unexpectedly missing the allocator`
*/
fn void*! arena_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind)
fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
{
ArenaAllocator* arena = (ArenaAllocator*)data;
bool clear = false;
switch (kind)
if (!ptr) return;
assert((uptr)ptr >= (uptr)self.data.ptr, "Pointer originates from a different allocator.");
ArenaAllocatorHeader* header = ptr - ArenaAllocatorHeader.sizeof;
// Reclaim memory if it's the last element.
if (ptr + header.size == &self.data[self.used])
{
case CALLOC:
case ALIGNED_CALLOC:
clear = true;
nextcase;
case ALLOC:
case ALIGNED_ALLOC:
assert(!old_pointer, "Unexpected old pointer for alloc.");
if (!size) return null;
alignment = alignment_for_allocation(alignment);
void* mem = arena._alloc(size, alignment, offset)!;
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
case ALIGNED_REALLOC:
case REALLOC:
if (!size) nextcase FREE;
if (!old_pointer) nextcase ALLOC;
alignment = alignment_for_allocation(alignment);
return arena._realloc(old_pointer, size, alignment, offset)!;
case ALIGNED_FREE:
case FREE:
if (!old_pointer) return null;
assert((uptr)old_pointer >= (uptr)arena.data.ptr, "Pointer originates from a different allocator.");
ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof;
// Reclaim memory if it's the last element.
if (old_pointer + header.size == &arena.data[arena.used])
{
arena.used -= header.size + ArenaAllocatorHeader.sizeof;
}
return null;
case MARK:
return (void*)(uptr)arena.used;
case RESET:
arena.used = size;
return null;
self.used -= header.size + ArenaAllocatorHeader.sizeof;
}
}
fn usz ArenaAllocator.mark(&self) @dynamic => self.used;
fn void ArenaAllocator.reset(&self, usz mark) @dynamic => self.used = mark;
/**
* @require alignment > 0 `alignment must be non zero`
* @require math::is_power_of_2(alignment)
* @require size > 0
* @require !alignment || math::is_power_of_2(alignment)
* @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, ArenaAllocatorHeader.alignof) == offset
**/
fn void*! ArenaAllocator._alloc(&self, usz size, usz alignment, usz offset)
fn void*! ArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
if (!size) return null;
alignment = alignment_for_allocation(alignment);
usz total_len = self.data.len;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
void* start_mem = self.data.ptr;
@@ -103,20 +65,29 @@ fn void*! ArenaAllocator._alloc(&self, usz size, usz alignment, usz offset)
void* mem = aligned_pointer_to_offset - offset;
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
header.size = size;
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
/**
* @require alignment > 0 `alignment must be non zero`
* @require math::is_power_of_2(alignment)
* @require size > 0
* @require !alignment || math::is_power_of_2(alignment)
* @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, ArenaAllocatorHeader.alignof) == offset
**/
fn void*! ArenaAllocator._realloc(&self, void *old_pointer, usz size, usz alignment, usz offset)
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);
assert(old_pointer >= self.data.ptr, "Pointer originates from a different allocator.");
usz total_len = self.data.len;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
@@ -139,7 +110,7 @@ fn void*! ArenaAllocator._realloc(&self, void *old_pointer, usz size, usz alignm
return old_pointer;
}
// Otherwise just allocate new memory.
void* mem = self._alloc(size, alignment, offset)!;
void* mem = self.acquire(size, false, alignment, offset)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}

View File

@@ -3,9 +3,8 @@
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem::allocator;
struct DynamicArenaAllocator
struct DynamicArenaAllocator (Allocator)
{
inline Allocator allocator;
Allocator* backing_allocator;
DynamicArenaPage* page;
DynamicArenaPage* unused_page;
@@ -17,7 +16,6 @@ struct DynamicArenaAllocator
**/
fn void DynamicArenaAllocator.init(&self, usz page_size, Allocator* using = mem::heap())
{
self.function = &dynamic_arena_allocator_function;
self.page = null;
self.unused_page = null;
self.page_size = page_size;
@@ -30,14 +28,14 @@ fn void DynamicArenaAllocator.free(&self)
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
free(page, .using = self.backing_allocator);
self.backing_allocator.free(page);
page = next_page;
}
page = self.unused_page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
free(page, .using = self.backing_allocator);
self.backing_allocator.free(page);
page = next_page;
}
self.page = null;
@@ -59,11 +57,11 @@ struct DynamicArenaChunk @local
}
/**
* @require ptr
* @require self.page `tried to free pointer on invalid allocator`
*/
fn void DynamicArenaAllocator.free_ptr(&self, void* ptr) @local
fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
{
if (!ptr) return;
DynamicArenaPage* current_page = self.page;
if (ptr == current_page.current_stack_ptr)
{
@@ -73,11 +71,19 @@ fn void DynamicArenaAllocator.free_ptr(&self, void* ptr) @local
}
/**
* @require old_pointer && size > 0
* @require self.page `tried to realloc pointer on invalid allocator`
*/
fn void*! DynamicArenaAllocator._realloc(&self, void* old_pointer, usz size, usz alignment, usz offset) @local
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;
alignment = alignment_for_allocation(alignment);
usz* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX;
@@ -101,13 +107,14 @@ fn void*! DynamicArenaAllocator._realloc(&self, void* old_pointer, usz size, usz
current_page.used += add_size;
return old_pointer;
}
void* new_mem = self._alloc(size, alignment, offset)!;
void* new_mem = self.acquire(size, false, alignment, offset)!;
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
return new_mem;
}
fn void DynamicArenaAllocator.reset(&self) @private
fn void DynamicArenaAllocator.reset(&self, usz mark = 0) @dynamic
{
assert(mark == 0, "Unexpectedly reset dynamic arena allocator with mark %d", mark);
DynamicArenaPage* page = self.page;
DynamicArenaPage** unused_page_ptr = &self.unused_page;
while (page)
@@ -132,7 +139,7 @@ fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment, usz o
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + offset, alignment) - offset);
// Grab the page without alignment (we do it ourselves)
void* mem = self.backing_allocator.alloc(page_size)!;
void* mem = self.backing_allocator.alloc_checked(page_size)!;
DynamicArenaPage*! page = malloc(DynamicArenaPage, .using = self.backing_allocator);
if (catch err = page)
{
@@ -154,87 +161,45 @@ fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment, usz o
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require size > 0
*/
fn void*! DynamicArenaAllocator._alloc(&self, usz size, usz alignment, usz offset) @local
fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
if (!size) return null;
alignment = alignment_for_allocation(alignment);
DynamicArenaPage* page = self.page;
if (!page && self.unused_page)
{
self.page = page = self.unused_page;
self.unused_page = page.prev_arena;
page.prev_arena = null;
}
if (!page) return self._alloc_new(size, alignment, offset);
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
usz new_used = start - page.memory + size;
if ALLOCATE_NEW: (new_used > page.total)
{
if ((page = self.unused_page))
void* ptr = {|
if (!page && self.unused_page)
{
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
new_used = start + size - page.memory;
if (page.total >= new_used)
{
self.unused_page = page.prev_arena;
page.prev_arena = self.page;
self.page = page;
break ALLOCATE_NEW;
}
self.page = page = self.unused_page;
self.unused_page = page.prev_arena;
page.prev_arena = null;
}
return self._alloc_new(size, alignment, offset);
}
page.used = new_used;
assert(start + size == page.memory + page.used);
void* mem = start;
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem - 1;
chunk.size = size;
return mem;
}
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require data `unexpectedly missing the allocator`
*/
fn void*! dynamic_arena_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
DynamicArenaAllocator* allocator = (DynamicArenaAllocator*)data;
switch (kind)
{
case CALLOC:
case ALIGNED_CALLOC:
assert(!old_pointer, "Unexpected no old pointer for calloc.");
if (!size) return null;
void* mem = allocator._alloc(size, alignment, offset)!;
mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
case ALLOC:
case ALIGNED_ALLOC:
assert(!old_pointer, "Unexpected no old pointer for alloc.");
if (!size) return null;
return allocator._alloc(size, alignment, offset);
case REALLOC:
case ALIGNED_REALLOC:
if (!size)
if (!page) return self._alloc_new(size, alignment, offset);
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
usz new_used = start - page.memory + size;
if ALLOCATE_NEW: (new_used > page.total)
{
if ((page = self.unused_page))
{
if (!old_pointer) return null;
allocator.free_ptr(old_pointer);
return null;
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
new_used = start + size - page.memory;
if (page.total >= new_used)
{
self.unused_page = page.prev_arena;
page.prev_arena = self.page;
self.page = page;
break ALLOCATE_NEW;
}
}
if (!old_pointer) return allocator._alloc(size, alignment, offset);
void* mem = allocator._realloc(old_pointer, size, alignment, offset)!;
return mem;
case ALIGNED_FREE:
case FREE:
if (!old_pointer) return null;
allocator.free_ptr(old_pointer);
return null;
case MARK:
unreachable("Tried to mark a dynamic arena");
case RESET:
allocator.reset();
return null;
}
return self._alloc_new(size, alignment, offset);
}
page.used = new_used;
assert(start + size == page.memory + page.used);
void* mem = start;
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem - 1;
chunk.size = size;
return mem;
|}!;
if (clear) mem::clear(ptr, size, mem::DEFAULT_MEM_ALIGNMENT);
return ptr;
}

View File

@@ -1,13 +1,12 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021-2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem::allocator;
struct SimpleHeapAllocator
struct SimpleHeapAllocator (Allocator)
{
inline Allocator allocator;
MemoryAllocFn alloc_fn;
Header* free_list;
}
@@ -19,47 +18,44 @@ struct SimpleHeapAllocator
fn void SimpleHeapAllocator.init(&self, MemoryAllocFn allocator)
{
self.alloc_fn = allocator;
static AllocatorFunction alloc_fn = &simple_heap_allocator_function;
self.allocator = { alloc_fn };
self.free_list = null;
}
/**
* @param [&inout] this "The allocator"
* @param [inout] old_pointer "The pointer to free/realloc"
* @require !alignment || math::is_power_of_2(alignment) "Alignment must be a power of 2"
*/
fn void*! simple_heap_allocator_function(Allocator* this, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
fn void*! SimpleHeapAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
SimpleHeapAllocator* heap = (SimpleHeapAllocator*)this;
switch (kind)
if (!size) return null;
if (clear)
{
case ALIGNED_ALLOC:
return @aligned_alloc(heap._alloc, size, alignment, offset);
case ALLOC:
return heap._alloc(size);
case ALIGNED_CALLOC:
return @aligned_calloc(heap._calloc, size, alignment, offset);
case CALLOC:
return heap._calloc(size);
case ALIGNED_REALLOC:
if (!size) nextcase ALIGNED_FREE;
if (!old_pointer) nextcase ALIGNED_CALLOC;
return @aligned_realloc(heap._calloc, heap._free, old_pointer, size, alignment, offset);
case REALLOC:
if (!size) nextcase FREE;
if (!old_pointer) nextcase CALLOC;
return heap._realloc(old_pointer, size);
case RESET:
unreachable("Reset unsupported");
case ALIGNED_FREE:
@aligned_free(heap._free, old_pointer)!;
return null;
case FREE:
heap._free(old_pointer);
return null;
default:
unreachable();
return alignment > 0 ? @aligned_calloc(self._calloc, size, alignment, offset) : self._calloc(size);
}
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment, offset) : self._alloc(size);
}
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
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment, offset)
: self._realloc(old_pointer, size);
}
fn void SimpleHeapAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
{
if (aligned)
{
@aligned_free(self._free, old_pointer)!!;
}
else
{
self._free(old_pointer);
}
}
@@ -131,7 +127,7 @@ fn void*! SimpleHeapAllocator._alloc(&self, usz bytes) @local
}
}
self.add_block(aligned_bytes)!;
return self.alloc(aligned_bytes);
return self._alloc(aligned_bytes);
}
fn void! SimpleHeapAllocator.add_block(&self, usz aligned_bytes) @local

View File

@@ -5,24 +5,6 @@
module std::core::mem::allocator;
import libc;
const Allocator _NULL_ALLOCATOR @private = { &null_allocator_fn };
const Allocator _SYSTEM_ALLOCATOR @private = { &libc_allocator_fn };
fn void*! null_allocator_fn(Allocator* this, usz bytes, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
switch (kind)
{
case ALLOC:
case CALLOC:
case REALLOC:
case ALIGNED_ALLOC:
case ALIGNED_REALLOC:
case ALIGNED_CALLOC:
return AllocationFailure.OUT_OF_MEMORY?;
default:
return null;
}
}
struct AlignedBlock
{
@@ -96,47 +78,56 @@ macro void! @aligned_free(#free_fn, void* old_pointer)
$endif
}
fn void*! libc_allocator_fn(Allocator* unused, usz bytes, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @inline
{
if (!alignment) alignment = mem::DEFAULT_MEM_ALIGNMENT;
assert(math::is_power_of_2(alignment), "Alignment was not a power of 2");
distinct LibcAllocator (Allocator) = uptr;
void* data;
switch (kind)
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
{
assert(alignment != 0 || offset == 0);
if (clear)
{
case ALIGNED_ALLOC:
data = @aligned_alloc(libc::malloc, bytes, alignment, offset)!!;
$if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
$endif
case ALLOC:
data = libc::malloc(bytes);
$if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
$endif
case ALIGNED_CALLOC:
data = @aligned_calloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment, offset)!!;
case CALLOC:
data = libc::calloc(bytes, 1);
case ALIGNED_REALLOC:
if (!bytes) nextcase ALIGNED_FREE;
if (!old_pointer) nextcase ALIGNED_CALLOC;
data = @aligned_realloc(fn void*(usz bytes) => libc::calloc(bytes, 1), libc::free, old_pointer, bytes, alignment, offset)!!;
case REALLOC:
if (!bytes) nextcase FREE;
if (!old_pointer) nextcase CALLOC;
data = libc::realloc(old_pointer, bytes);
case RESET:
unreachable("Reset unsupported");
case ALIGNED_FREE:
@aligned_free(libc::free, old_pointer)!!;
return null;
case FREE:
libc::free(old_pointer);
return null;
default:
unreachable();
void* data = alignment ? @aligned_calloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment, offset)!! : libc::calloc(bytes, 1);
return data ?: AllocationFailure.OUT_OF_MEMORY?;
}
else
{
void* data = alignment ? @aligned_alloc(libc::malloc, bytes, alignment, offset)!! : 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
{
assert(alignment != 0 || offset == 0);
if (!new_bytes)
{
self.release(old_ptr, alignment > 0);
return null;
}
if (!old_ptr)
{
return self.acquire(new_bytes, true, alignment, offset);
}
if (alignment)
{
void* data = @aligned_realloc(fn void*(usz bytes) => libc::calloc(bytes, 1), libc::free, old_ptr, new_bytes, alignment, offset)!!;
return data ?: 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)
{
@aligned_free(libc::free, old_ptr)!!;
}
else
{
libc::free(old_ptr);
}
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
return data;
}

View File

@@ -1,8 +1,7 @@
module std::core::mem::allocator;
struct OnStackAllocator
struct OnStackAllocator (Allocator)
{
inline Allocator allocator;
Allocator* backing_allocator;
char[] data;
usz used;
@@ -22,7 +21,6 @@ struct OnStackAllocatorExtraChunk @local
**/
fn void OnStackAllocator.init(&self, char[] data, Allocator* using = mem::heap())
{
self.function = &on_stack_allocator_function;
self.data = data;
self.backing_allocator = using;
self.used = 0;
@@ -35,15 +33,15 @@ fn void OnStackAllocator.free(&self)
{
if (chunk.is_aligned)
{
self.backing_allocator.free_aligned(chunk.data)!!;
self.backing_allocator.free_aligned(chunk.data);
}
else
{
self.backing_allocator.free(chunk.data)!!;
self.backing_allocator.free(chunk.data);
}
void* old = chunk;
chunk = chunk.prev;
self.backing_allocator.free(old)!!;
self.backing_allocator.free(old);
}
self.chunk = null;
self.used = 0;
@@ -55,48 +53,12 @@ struct OnStackAllocatorHeader
char[*] data;
}
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require data `unexpectedly missing the allocator`
*/
fn void*! on_stack_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
fn void OnStackAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
{
OnStackAllocator* allocator = (OnStackAllocator*)data;
bool clear = false;
switch (kind)
{
case CALLOC:
case ALIGNED_CALLOC:
clear = true;
nextcase;
case ALLOC:
case ALIGNED_ALLOC:
assert(!old_pointer, "Unexpected old pointer for alloc.");
if (!size) return null;
return on_stack_allocator_alloc(allocator, size, alignment, offset, clear, kind == AllocationKind.ALIGNED_ALLOC || kind == AllocationKind.ALIGNED_CALLOC);
case ALIGNED_REALLOC:
case REALLOC:
if (!size) nextcase FREE;
if (!old_pointer) nextcase ALLOC;
return on_stack_allocator_realloc(allocator, old_pointer, size, alignment, offset, kind == AllocationKind.ALIGNED_REALLOC);
case ALIGNED_FREE:
case FREE:
if (!old_pointer) return null;
if (allocation_in_stack_mem(allocator, old_pointer)) return null;
on_stack_allocator_remove_chunk(allocator, old_pointer);
if (kind == AllocationKind.ALIGNED_FREE)
{
allocator.backing_allocator.free_aligned(old_pointer)!;
}
else
{
allocator.backing_allocator.free(old_pointer)!;
}
return null;
case MARK:
case RESET:
unreachable("Reset unsupported");
}
if (!old_pointer) return;
if (allocation_in_stack_mem(self, old_pointer)) return;
on_stack_allocator_remove_chunk(self, old_pointer);
self.release(old_pointer, aligned);
}
fn bool allocation_in_stack_mem(OnStackAllocator* a, void* ptr) @local
@@ -113,7 +75,7 @@ fn void on_stack_allocator_remove_chunk(OnStackAllocator* a, void* ptr) @local
if (chunk.data == ptr)
{
*addr = chunk.prev;
a.backing_allocator.free(chunk)!!;
a.backing_allocator.free(chunk);
return;
}
addr = &chunk.prev;
@@ -138,70 +100,52 @@ fn OnStackAllocatorExtraChunk* on_stack_allocator_find_chunk(OnStackAllocator* a
* @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, ArenaAllocatorHeader.alignof) == offset
* @require a != null
* @require mem::aligned_offset(offset, OnStackAllocatorExtraChunk.alignof) == offset
**/
fn void*! on_stack_allocator_realloc(OnStackAllocator* a, void* old_pointer, usz size, usz alignment, usz offset, bool aligned) @local @inline
fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
{
if (!allocation_in_stack_mem(a, old_pointer))
if (!allocation_in_stack_mem(self, old_pointer))
{
OnStackAllocatorExtraChunk* chunk = on_stack_allocator_find_chunk(a, old_pointer);
OnStackAllocatorExtraChunk* chunk = on_stack_allocator_find_chunk(self, old_pointer);
assert(chunk, "Tried to realloc pointer not belonging to the allocator");
if (aligned)
{
return chunk.data = a.backing_allocator.realloc_aligned(old_pointer, size, alignment, offset)!;
}
return chunk.data = a.backing_allocator.realloc(old_pointer, size)!;
return chunk.data = self.backing_allocator.resize(old_pointer, size, alignment, offset)!;
}
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
usz old_size = header.size;
void* mem = on_stack_allocator_alloc(a, size, alignment, offset, true, aligned)!;
void* mem = self.acquire(size, true, alignment, offset)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
import std::io;
/**
* @require size > 0
* @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, ArenaAllocatorHeader.alignof) == offset
* @require a != null
* @require offset == 0 || alignment > 0
* @require mem::aligned_offset(offset, OnStackAllocatorHeader.alignof) == offset
**/
fn void*! on_stack_allocator_alloc(OnStackAllocator* a, usz size, usz alignment, usz offset, bool clear, bool aligned) @local @inline
fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
if (size == 0) return null;
bool aligned = alignment > 0;
alignment = alignment_for_allocation(alignment);
usz total_len = a.data.len;
void* start_mem = a.data.ptr;
void* unaligned_pointer_to_offset = start_mem + a.used + OnStackAllocatorHeader.sizeof + offset;
usz total_len = self.data.len;
void* start_mem = self.data.ptr;
void* unaligned_pointer_to_offset = start_mem + self.used + OnStackAllocatorHeader.sizeof + offset;
void* aligned_pointer_to_offset = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(aligned_pointer_to_offset - a.data.ptr) + size - offset;
Allocator* backing_allocator = a.backing_allocator;
usz end = (usz)(aligned_pointer_to_offset - self.data.ptr) + size - offset;
Allocator* backing_allocator = self.backing_allocator;
if (end > total_len)
{
OnStackAllocatorExtraChunk* chunk = backing_allocator.alloc(OnStackAllocatorExtraChunk.sizeof)!;
defer catch backing_allocator.free(chunk)!!;
defer try a.chunk = chunk;
*chunk = { .prev = a.chunk, .is_aligned = aligned };
void* data @noinit;
switch
{
case !aligned && !clear:
data = backing_allocator.alloc(size)!;
case aligned && !clear:
data = backing_allocator.alloc_aligned(size, alignment, offset)!;
case !aligned && clear:
data = backing_allocator.calloc(size)!;
case aligned && clear:
data = backing_allocator.calloc_aligned(size, alignment, offset)!;
}
return chunk.data = data;
OnStackAllocatorExtraChunk* chunk = backing_allocator.alloc_checked(OnStackAllocatorExtraChunk.sizeof)!;
defer catch backing_allocator.free(chunk);
defer try self.chunk = chunk;
*chunk = { .prev = self.chunk, .is_aligned = aligned };
return chunk.data = backing_allocator.acquire(size, clear, aligned ? alignment : 0, offset)!;
}
a.used = end;
self.used = end;
void *mem = aligned_pointer_to_offset - offset;
OnStackAllocatorHeader* header = mem - OnStackAllocatorHeader.sizeof;
header.size = size;

View File

@@ -7,9 +7,8 @@ struct TempAllocatorChunk @local
char[*] data;
}
struct TempAllocator
struct TempAllocator (Allocator)
{
inline Allocator allocator;
Allocator* backing_allocator;
TempAllocatorPage* last_page;
usz used;
@@ -17,7 +16,6 @@ struct TempAllocator
char[*] data;
}
const usz PAGE_IS_ALIGNED @private = (usz)isz.max + 1u;
@@ -41,51 +39,15 @@ fn TempAllocator*! new_temp(usz size, Allocator* using)
{
TempAllocator* allocator = malloc_checked(TempAllocator, .using = using, .end_padding = size)!;
allocator.last_page = null;
allocator.function = &temp_allocator_function;
allocator.backing_allocator = using;
allocator.used = 0;
allocator.capacity = size;
return allocator;
}
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require data `unexpectedly missing the allocator`
*/
fn void*! temp_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
TempAllocator* arena = (TempAllocator*)data;
switch (kind)
{
case CALLOC:
case ALIGNED_CALLOC:
assert(!old_pointer, "Unexpected old pointer for alloc.");
if (!size) return null;
return arena._alloc(size, alignment_for_allocation(alignment), offset, true);
case ALLOC:
case ALIGNED_ALLOC:
assert(!old_pointer, "Unexpected old pointer for alloc.");
if (!size) return null;
return arena._alloc(size, alignment_for_allocation(alignment), offset, false);
case ALIGNED_REALLOC:
case REALLOC:
if (!size) nextcase FREE;
if (!old_pointer) nextcase ALLOC;
return arena._realloc(old_pointer, size, alignment_for_allocation(alignment), offset);
case FREE:
case ALIGNED_FREE:
if (!old_pointer) return null;
arena._free(old_pointer)!;
return null;
case MARK:
return (void*)(uptr)arena.used;
case RESET:
arena._reset(size)!;
return null;
}
}
fn usz TempAllocator.mark(&self) => self.used;
fn void! TempAllocator._free(&self, void* old_pointer) @local
fn void TempAllocator.release(&self, void* old_pointer, bool) @dynamic
{
usz old_size = *(usz*)(old_pointer - DEFAULT_SIZE_PREFIX);
if (old_pointer + old_size == &self.data[self.used])
@@ -93,14 +55,14 @@ fn void! TempAllocator._free(&self, void* old_pointer) @local
self.used -= old_size;
}
}
fn void! TempAllocator._reset(&self, usz mark) @local
fn void TempAllocator.reset(&self, usz mark) @dynamic
{
TempAllocatorPage *last_page = self.last_page;
while (last_page && last_page.mark > mark)
{
TempAllocatorPage *to_free = last_page;
last_page = last_page.prev_page;
self._free_page(to_free)!;
self._free_page(to_free)!!;
}
self.last_page = last_page;
self.used = mark;
@@ -128,21 +90,30 @@ fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size,
*pointer_to_prev = page.prev_page;
usz page_size = page.pagesize();
// Clear on size > original size.
void* data = self._alloc(size, alignment, offset, false)!;
void* data = self.acquire(size, size > page_size, alignment, offset)!;
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
if (page.is_aligned())
{
self.backing_allocator.free_aligned(real_pointer)!;
self.backing_allocator.free_aligned(real_pointer);
}
else
{
self.backing_allocator.free(real_pointer)!;
self.backing_allocator.free(real_pointer);
}
return data;
}
fn void*! TempAllocator._realloc(&self, void* pointer, usz size, usz alignment, usz offset) @inline @local
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, usz offset) @dynamic
{
if (!size)
{
self.release(pointer, alignment > 0);
return null;
}
if (!pointer)
{
return self.acquire(size, true, alignment, offset);
}
TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
if (chunk.size == (usz)-1)
{
@@ -153,19 +124,20 @@ fn void*! TempAllocator._realloc(&self, void* pointer, usz size, usz alignment,
}
// TODO optimize last allocation
TempAllocatorChunk* data = self._alloc(size, alignment, offset, size > chunk.size)!;
TempAllocatorChunk* data = self.acquire(size, size > chunk.size, alignment, offset)!;
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
/**
* @require math::is_power_of_2(alignment)
* @require size > 0
* @require !alignment || math::is_power_of_2(alignment)
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
**/
fn void*! TempAllocator._alloc(&self, usz size, usz alignment, usz offset, bool clear) @local
fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
if (!size) return null;
alignment = alignment_for_allocation(alignment);
void* start_mem = &self.data;
void* starting_ptr = start_mem + self.used;
void* aligned_header_start = mem::aligned_pointer(starting_ptr, TempAllocatorChunk.alignof);
@@ -210,7 +182,7 @@ fn void*! TempAllocator._alloc(&self, usz size, usz alignment, usz offset, bool
// Here we might need to pad
usz padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, mem::DEFAULT_MEM_ALIGNMENT);
usz total_alloc_size = padded_header_size + size;
void* alloc = (clear ? self.backing_allocator.calloc(total_alloc_size) : self.backing_allocator.alloc(total_alloc_size))!;
void* alloc = self.backing_allocator.acquire(total_alloc_size, clear)!;
// Find the page.
page = alloc + padded_header_size - TempAllocatorPage.sizeof;

View File

@@ -10,9 +10,8 @@ def PtrMap = HashMap(<uptr, usz>);
// A simple tracking allocator.
// It tracks allocations using a hash map but
// is not compatible with allocators that uses mark()
struct TrackingAllocator
struct TrackingAllocator (Allocator)
{
inline Allocator allocator;
Allocator* inner_allocator;
PtrMap map;
usz mem_total;
@@ -26,7 +25,7 @@ struct TrackingAllocator
**/
fn void TrackingAllocator.init(&self, Allocator* using)
{
*self = { .inner_allocator = using, .allocator.function = &tracking_allocator_fn };
*self = { .inner_allocator = using };
self.map.init(.using = using);
}
@@ -67,41 +66,42 @@ fn usz TrackingAllocator.total_allocation_count(&self) => self.allocs_total;
**/
fn usz TrackingAllocator.allocation_count(&self) => self.map.count;
/**
* @param [inout] data
* @require !alignment || math::is_power_of_2(alignment)
*/
fn void*! tracking_allocator_fn(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
fn void*! TrackingAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
TrackingAllocator* this = (TrackingAllocator*)data;
void* result = this.inner_allocator.function(this.inner_allocator, size, alignment, offset, old_pointer, kind)!;
switch (kind)
void* data = self.inner_allocator.acquire(size, clear, alignment, offset)!;
self.allocs_total++;
if (data)
{
case CALLOC:
case ALIGNED_CALLOC:
case ALLOC:
case ALIGNED_ALLOC:
this.map.set((uptr)result, size);
this.mem_total += size;
this.allocs_total++;
return result;
case REALLOC:
case ALIGNED_REALLOC:
this.map.remove((uptr)old_pointer);
this.map.set((uptr)result, size);
this.mem_total += size;
if (size > 0) this.allocs_total++;
return result;
case ALIGNED_FREE:
case FREE:
if (!old_pointer) return null;
this.map.remove((uptr)old_pointer);
return null;
case MARK:
// Unsupported
return null;
case RESET:
this.map.clear();
return null;
self.map.set((uptr)data, size);
self.mem_total += size;
self.allocs_total++;
}
return data;
}
fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
{
void* data = self.inner_allocator.resize(old_pointer, size, alignment, offset)!;
if (old_pointer)
{
self.map.remove((uptr)old_pointer);
}
if (data)
{
self.map.set((uptr)data, size);
self.mem_total += size;
self.allocs_total++;
}
return data;
}
fn void TrackingAllocator.release(&self, void* old_pointer, bool is_aligned) @dynamic
{
self.inner_allocator.release(old_pointer, is_aligned);
if (old_pointer) self.map.remove((uptr)old_pointer);
}
fn void TrackingAllocator.clear(&self)
{
self.map.clear();
}

View File

@@ -7,8 +7,6 @@ const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
/**
* Load a vector from memory according to a mask assuming default alignment.
*
@@ -403,12 +401,12 @@ macro malloc_checked(..., Allocator* using = mem::heap(), usz end_padding = 0) @
$assert !type_alloc_must_be_aligned($vatype(0)) : "Type must be allocated with malloc_aligned";
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)using.alloc($Type.sizeof * size + end_padding))[:size];
return (($Type*)using.alloc_checked($Type.sizeof * size + end_padding))[:size];
$else
return ($Type*)using.alloc($Type.sizeof + end_padding);
return ($Type*)using.alloc_checked($Type.sizeof + end_padding);
$endif
$else
return using.alloc($vaarg(0) + end_padding);
return using.alloc_checked($vaarg(0) + end_padding);
$endif
}
@@ -453,12 +451,12 @@ macro calloc_checked(..., Allocator* using = mem::heap(), usz end_padding = 0) @
$assert !type_alloc_must_be_aligned($vatype(0)) : "Type must be allocated with calloc_aligned";
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)using.calloc($Type.sizeof * size + end_padding))[:size];
return (($Type*)using.calloc_checked($Type.sizeof * size + end_padding))[:size];
$else
return ($Type*)using.calloc($Type.sizeof + end_padding);
return ($Type*)using.calloc_checked($Type.sizeof + end_padding);
$endif
$else
return using.calloc($vaarg(0) + end_padding);
return using.calloc_checked($vaarg(0) + end_padding);
$endif
}
@@ -485,12 +483,12 @@ macro calloc_aligned(..., usz alignment = 0, Allocator* using = mem::heap(), usz
fn void* realloc(void *ptr, usz new_size, Allocator* using = mem::heap()) @builtin @inline
{
return using.realloc(ptr, new_size)!!;
return using.realloc(ptr, new_size);
}
fn void*! realloc_checked(void *ptr, usz new_size, Allocator* using = mem::heap()) @builtin @inline
{
return using.realloc(ptr, new_size);
return using.realloc_checked(ptr, new_size);
}
/**
@@ -501,10 +499,10 @@ fn void*! realloc_aligned(void *ptr, usz new_size, usz alignment, Allocator* usi
return using.realloc_aligned(ptr, new_size, alignment);
}
macro void free(void* ptr, Allocator* using = mem::heap()) @builtin => using.free(ptr)!!;
macro void! free_checked(void* ptr, Allocator* using = mem::heap()) @builtin => using.free(ptr);
macro void free_aligned(void* ptr, Allocator* using = mem::heap()) @builtin => using.free_aligned(ptr)!!;
macro void! free_aligned_checked(void* ptr, Allocator* using = mem::heap()) @builtin => using.free_aligned(ptr);
macro void free(void* ptr, Allocator* using = mem::heap()) @builtin => using.free(ptr);
//macro void! free_checked(void* ptr, Allocator* using = mem::heap()) @builtin => using.free(ptr);
macro void free_aligned(void* ptr, Allocator* using = mem::heap()) @builtin => using.free_aligned(ptr);
//macro void! free_aligned_checked(void* ptr, Allocator* using = mem::heap()) @builtin => using.free_aligned(ptr);
/**
* Run with a specific allocator inside of the macro body.
@@ -527,12 +525,12 @@ macro tmalloc(..., usz end_padding = 0, usz alignment = DEFAULT_MEM_ALIGNMENT) @
var $Type = $vatype(0);
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)temp().alloc_aligned($Type.sizeof * size + end_padding, alignment))[:size]!!;
return (($Type*)temp().acquire($Type.sizeof * size + end_padding, false, alignment, 0))[:size]!!;
$else
return ($Type*)temp().alloc_aligned($Type.sizeof + end_padding, alignment)!!;
return ($Type*)temp().acquire($Type.sizeof + end_padding, false, alignment, 0)!!;
$endif
$else
return temp().alloc_aligned($vaarg(0) + end_padding, alignment)!!;
return temp().acquire($vaarg(0) + end_padding, false, alignment, 0)!!;
$endif
}
@@ -546,18 +544,18 @@ macro tcalloc(..., usz end_padding = 0, usz alignment = mem::DEFAULT_MEM_ALIGNME
var $Type = $vatype(0);
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)temp().calloc_aligned($Type.sizeof * size + end_padding, alignment))[:size]!!;
return (($Type*)temp().acquire($Type.sizeof * size + end_padding, true, alignment, 0))[:size]!!;
$else
return ($Type*)temp().calloc_aligned($Type.sizeof + end_padding, alignment)!!;
return ($Type*)temp().acquire($Type.sizeof + end_padding, true, alignment, 0)!!;
$endif
$else
return temp().calloc_aligned($vaarg(0) + end_padding, alignment)!!;
return temp().acquire($vaarg(0) + end_padding, true, alignment, 0)!!;
$endif
}
fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin @inline
{
return temp().realloc_aligned(ptr, size, alignment)!!;
return temp().resize(ptr, size, alignment, 0)!!;
}
macro void @stack_mem(usz $size; @body(Allocator* mem)) @builtin
@@ -587,7 +585,7 @@ macro void @pool(TempAllocator* #other_temp = null; @body) @builtin
var $has_arg = !$checks(var $x = #other_temp);
$if $has_arg:
TempAllocator* original = current;
if (current == #other_temp) current = temp_allocator_next();
if (current == (void*)#other_temp) current = temp_allocator_next();
$endif
usz mark = current.used;
defer
@@ -600,7 +598,7 @@ macro void @pool(TempAllocator* #other_temp = null; @body) @builtin
@body();
}
tlocal Allocator* thread_allocator @private = allocator::LIBC_ALLOCATOR;
tlocal Allocator* thread_allocator @private = &allocator::LIBC_ALLOCATOR;
tlocal TempAllocator* thread_temp_allocator @private = null;
tlocal TempAllocator*[2] temp_allocator_pair @private;

View File

@@ -3,55 +3,63 @@ module std::core::mem::allocator;
const DEFAULT_SIZE_PREFIX = usz.sizeof;
const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof;
const Allocator* NULL_ALLOCATOR = &_NULL_ALLOCATOR;
const Allocator* LIBC_ALLOCATOR = &_SYSTEM_ALLOCATOR;
protocol Allocator
{
fn void reset(usz mark) @optional;
fn usz mark() @optional;
fn void*! acquire(usz size, bool clear = false, usz alignment = 0, usz offset = 0);
fn void*! resize(void* ptr, usz new_size, usz alignment = 0, usz offset = 0);
fn void release(void* ptr, bool aligned = false);
}
const LibcAllocator LIBC_ALLOCATOR = {};
def AllocatorFunction = fn void*!(Allocator* allocator, usz new_size, usz alignment, usz offset, void* old_pointer, AllocationKind kind);
def MemoryAllocFn = fn char[]!(usz);
macro bool is_allocator($Type)
// Allocator "functions"
macro void*! Allocator.alloc_checked(&self, usz size)
{
return $checks(
$Type mem,
usz sz = 1,
void*! x = mem.alloc(sz),
void*! y = mem.calloc(sz),
void*! z = mem.realloc(x, sz),
(void)mem.free(x)
);
$if env::TESTING:
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 bool is_valid_aligned_allocator($Type)
macro void*! Allocator.calloc_checked(&self, usz size) => self.acquire(size, true, 0, 0);
macro void*! Allocator.realloc_checked(&self, void* ptr, usz new_size) => self.resize(ptr, new_size, 0, 0);
macro void* Allocator.alloc(&self, usz size) @nodiscard => self.alloc_checked(size)!!;
macro void* Allocator.calloc(&self, usz size) @nodiscard => self.acquire(size, true, 0, 0)!!;
macro void* Allocator.realloc(&self, void* ptr, usz new_size) @nodiscard => self.resize(ptr, new_size, 0, 0)!!;
macro void*! Allocator.alloc_aligned(&self, usz size, usz alignment, usz offset = 0)
{
return !$checks($Type.alloc_aligned) ||
$checks(
$Type mem,
usz sz = 1,
void*! x = mem.alloc_aligned(sz, sz, az),
void*! y = mem.calloc_aligned(sz, sz, sz),
void*! z = mem.realloc_aligned(x, sz, sz, sz),
(void)mem.free_aligned(x, sz, sz)
);
$if env::TESTING:
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
}
struct Allocator
macro void*! Allocator.calloc_aligned(&self, usz size, usz alignment, usz offset = 0) => self.acquire(size, true, alignment, offset);
macro void*! Allocator.realloc_aligned(&self, void* ptr, usz new_size, usz alignment = 0, usz offset = 0) => self.resize(ptr, new_size, alignment, offset);
macro void Allocator.free(&self, void* ptr)
{
AllocatorFunction function;
$if env::TESTING:
if (ptr) ((char*)ptr)[0] = 0xBA;
$endif
self.release(ptr, false);
}
enum AllocationKind
macro void Allocator.free_aligned(&self, void* ptr)
{
ALLOC,
CALLOC,
REALLOC,
FREE,
ALIGNED_ALLOC,
ALIGNED_CALLOC,
ALIGNED_REALLOC,
ALIGNED_FREE,
RESET,
MARK,
$if env::TESTING:
if (ptr) ((char*)ptr)[0] = 0xBA;
$endif
self.release(ptr, true);
}
fault AllocationFailure
@@ -60,75 +68,9 @@ fault AllocationFailure
CHUNK_TOO_LARGE,
}
macro void*! Allocator.alloc(&allocator, usz size)
{
return allocator.function(allocator, size, 0, 0, null, ALLOC);
}
/**
* @require alignment && math::is_power_of_2(alignment)
*/
macro void*! Allocator.alloc_aligned(&allocator, usz size, usz alignment, usz offset = 0)
{
return allocator.function(allocator, size, alignment, offset, null, ALIGNED_ALLOC);
}
macro void*! Allocator.realloc(&allocator, void* old_pointer, usz size)
{
return allocator.function(allocator, size, 0, 0, old_pointer, REALLOC);
}
/**
* @require alignment && math::is_power_of_2(alignment)
*/
macro void*! Allocator.realloc_aligned(&allocator, void* old_pointer, usz size, usz alignment, usz offset = 0)
{
return allocator.function(allocator, size, alignment, offset, old_pointer, ALIGNED_REALLOC);
}
macro usz Allocator.mark(&allocator)
{
return (usz)(uptr)allocator.function(allocator, 0, 0, 0, null, MARK) ?? 0;
}
macro void*! Allocator.calloc(&allocator, usz size)
{
return allocator.function(allocator, size, 0, 0, null, CALLOC);
}
/**
* @require alignment && math::is_power_of_2(alignment)
*/
macro void*! Allocator.calloc_aligned(&allocator, usz size, usz alignment, usz offset = 0)
{
return allocator.function(allocator, size, alignment, offset, null, ALIGNED_CALLOC);
}
macro void! Allocator.free(&allocator, void* old_pointer)
{
allocator.function(allocator, 0, 0, 0, old_pointer, FREE)!;
}
macro void! Allocator.free_aligned(&allocator, void* old_pointer)
{
allocator.function(allocator, 0, 0, 0, old_pointer, ALIGNED_FREE)!;
}
macro void Allocator.reset(&allocator, usz mark = 0)
{
(void)allocator.function(allocator, mark, 0, 0, null, RESET);
}
fn usz alignment_for_allocation(usz alignment) @inline @private
{
if (alignment < mem::DEFAULT_MEM_ALIGNMENT)
{
alignment = mem::DEFAULT_MEM_ALIGNMENT;
}
return alignment;
return alignment < mem::DEFAULT_MEM_ALIGNMENT ? alignment = mem::DEFAULT_MEM_ALIGNMENT : alignment;
}

View File

@@ -40,9 +40,9 @@ fn ByteBuffer*! ByteBuffer.init_with_buffer(&self, char[] buf)
return self;
}
fn void! ByteBuffer.free(&self)
fn void ByteBuffer.free(&self)
{
if (self.allocator) self.allocator.free(self.bytes)!;
if (self.allocator) self.allocator.free(self.bytes);
*self = {};
}

View File

@@ -12,7 +12,7 @@ struct ByteWriter
* @param [&inout] self
* @param [&in] using
* @require self.bytes.len == 0 "Init may not run on on already initialized data"
* @ensure using != null, self.index == 0
* @ensure (bool)using, self.index == 0
**/
fn ByteWriter* ByteWriter.init(&self, Allocator* using = mem::heap())
{

View File

@@ -13,7 +13,7 @@ struct Summary
struct StringData @private
{
Allocator allocator;
Allocator* allocator;
usz len;
usz capacity;
char[*] chars;

View File

@@ -2606,6 +2606,12 @@ static void llvm_emit_unary_expr(GenContext *c, BEValue *value, Expr *expr)
llvm_value_rvalue(c, value);
llvm_value = LLVMBuildIsNull(c->builder, value->value, "not");
break;
case TYPE_ANYPTR:
case TYPE_PROPTR:
llvm_emit_any_pointer(c, value, value);
llvm_value_rvalue(c, value);
llvm_value = LLVMBuildIsNull(c->builder, value->value, "not");
break;
default:
DEBUG_LOG("Unexpectedly tried to not %s", type_quoted_error_string(inner->type));
UNREACHABLE

View File

@@ -2401,7 +2401,7 @@ static inline bool sema_analyse_doc_header(AstId doc, Decl **params, Decl **extr
NEXT:;
Type *type = param->type;
if (type) type = type_flatten(type);
bool may_be_pointer = !type || type_is_pointer(type);
bool may_be_pointer = !type || type_is_pointer(type) || type_is_any_protocol_ptr(type);
if (directive->contract_stmt.param.by_ref)
{
if (!may_be_pointer)

View File

@@ -1377,6 +1377,14 @@ static inline bool sema_call_check_contract_param_match(SemaContext *context, De
}
return true;
}
INLINE bool sema_arg_is_pass_through_ref(Expr *expr)
{
if (expr->expr_kind != EXPR_IDENTIFIER) return false;
Decl *decl = expr->identifier_expr.decl;
if (decl->decl_kind != DECL_VAR) return false;
return decl->var.kind == VARDECL_PARAM_REF;
}
static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call, CalledDecl callee, bool *optional)
{
// 1. Check body arguments (for macro calls, or possibly broken )
@@ -1537,7 +1545,7 @@ static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call
case VARDECL_PARAM_REF:
// &foo
if (!sema_analyse_expr_lvalue(context, arg)) return false;
if (!sema_expr_check_assign(context, arg)) return false;
if (!sema_arg_is_pass_through_ref(arg) && !sema_expr_check_assign(context, arg)) return false;
if (!type_is_any_protocol_ptr(arg->type)) expr_insert_addr(arg);
*optional |= IS_OPTIONAL(arg);
if (!sema_call_check_contract_param_match(context, param, arg)) return false;

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.4.679"
#define COMPILER_VERSION "0.4.680"

View File

@@ -41,15 +41,13 @@ fn void main()
@"$ct.inherit.Test" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 8, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8
@std.core.mem.thread_allocator = external thread_local global ptr, align 8
@"$ct.anyfault" = linkonce global %.introspect { i8 6, i64 0, ptr null, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8
@.panic_msg = internal constant [37 x i8] c"Unexpected fault '%s' was unwrapped!\00", align 1
@.file = internal constant [7 x i8] c"mem.c3\00", align 1
@.func = internal constant [5 x i8] c"main\00", align 1
@"$sel.tesT" = linkonce_odr constant [5 x i8] c"tesT\00", align 1
@.panic_msg.1 = internal constant [42 x i8] c"No method 'tesT' could be found on target\00", align 1
@.file.2 = internal constant [11 x i8] c"inherit.c3\00", align 1
@std.core.builtin.panic = external global ptr, align 8
@"$ct.anyfault" = linkonce global %.introspect { i8 6, i64 0, ptr null, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8
@.panic_msg.1 = internal constant [37 x i8] c"Unexpected fault '%s' was unwrapped!\00", align 1
@"$sel.tesT" = linkonce_odr constant [5 x i8] c"tesT\00", align 1
@.panic_msg.3 = internal constant [42 x i8] c"No method 'tesT' could be found on target\00", align 1
@.file.4 = internal constant [11 x i8] c"inherit.c3\00", align 1
@"$ct.dyn.inherit.Test.tesT" = global { ptr, ptr, ptr } { ptr @inherit.Test.tesT, ptr @"$sel.tesT", ptr null }, align 8
@"$ct.dyn.inherit.Test.hello" = global { ptr, ptr, ptr } { ptr @inherit.Test.hello, ptr @"$sel.hello", ptr null }, align 8
@"$sel.hello" = linkonce_odr constant [6 x i8] c"hello\00", align 1
@@ -58,126 +56,154 @@ fn void main()
define void @inherit.main() #0 {
entry:
%z = alloca %"any*", align 8
%using = alloca ptr, align 8
%using = alloca %"any*", align 8
%error_var = alloca i64, align 8
%using1 = alloca ptr, align 8
%allocator = alloca ptr, align 8
%using1 = alloca %"any*", align 8
%self = alloca %"any*", align 8
%.inlinecache = alloca ptr, align 8
%.cachedtype = alloca ptr, align 8
%retparam = alloca ptr, align 8
%varargslots = alloca [1 x %"any*"], align 16
%indirectarg = alloca %"any*[]", align 8
%.inlinecache = alloca ptr, align 8
%.cachedtype = alloca ptr, align 8
%w = alloca %"any*", align 8
%.inlinecache2 = alloca ptr, align 8
%.cachedtype3 = alloca ptr, align 8
%w = alloca %"any*", align 8
%.inlinecache11 = alloca ptr, align 8
%.cachedtype12 = alloca ptr, align 8
store ptr null, ptr %.cachedtype12, align 8
store ptr null, ptr %.cachedtype3, align 8
store ptr null, ptr %.cachedtype, align 8
%0 = load ptr, ptr @std.core.mem.thread_allocator, align 8
store ptr %0, ptr %using, align 8
%1 = load ptr, ptr %using, align 8
store ptr %1, ptr %using1, align 8
%2 = load ptr, ptr %using1, align 8
store ptr %2, ptr %allocator, align 8
%3 = load ptr, ptr %allocator, align 8
%4 = getelementptr inbounds %Allocator, ptr %3, i32 0, i32 0
%5 = load ptr, ptr %4, align 8
%6 = load ptr, ptr %allocator, align 8
%7 = call i64 %5(ptr %retparam, ptr %6, i64 8, i64 0, i64 0, ptr null, i32 0)
%not_err = icmp eq i64 %7, 0
%8 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
br i1 %8, label %after_check, label %assign_optional
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %using, ptr align 8 @std.core.mem.thread_allocator, i32 16, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %using1, ptr align 8 %using, i32 16, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %self, ptr align 8 %using1, i32 16, i1 false)
%0 = getelementptr inbounds %"any*", ptr %self, i32 0, i32 1
%1 = load i64, ptr %0, align 8
%2 = getelementptr inbounds %"any*", ptr %self, i32 0, i32 0
%3 = inttoptr i64 %1 to ptr
%type = load ptr, ptr %.cachedtype, align 8
%4 = icmp eq ptr %3, %type
br i1 %4, label %cache_hit, label %cache_miss
assign_optional: ; preds = %entry
store i64 %7, ptr %error_var, align 8
cache_miss: ; preds = %entry
%5 = getelementptr inbounds %.introspect, ptr %3, i32 0, i32 2
%6 = load ptr, ptr %5, align 8
%7 = call ptr @.dyn_search(ptr %6, ptr @"$sel.acquire")
store ptr %7, ptr %.inlinecache, align 8
store ptr %3, ptr %.cachedtype, align 8
br label %8
cache_hit: ; preds = %entry
%cache_hit_fn = load ptr, ptr %.inlinecache, align 8
br label %8
8: ; preds = %cache_hit, %cache_miss
%fn_phi = phi ptr [ %cache_hit_fn, %cache_hit ], [ %7, %cache_miss ]
%9 = icmp eq ptr %fn_phi, null
br i1 %9, label %missing_function, label %match
missing_function: ; preds = %8
%10 = load ptr, ptr @std.core.builtin.panic, align 8
call void %10(ptr @.panic_msg, i64 44, ptr @.file, i64 16, ptr @.func, i64 4, i32 28)
unreachable
match: ; preds = %8
%11 = load ptr, ptr %2, align 8
%12 = call i64 %fn_phi(ptr %retparam, ptr %11, i64 8, i8 zeroext 0, i64 0, i64 0)
%not_err = icmp eq i64 %12, 0
%13 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
br i1 %13, label %after_check, label %assign_optional
assign_optional: ; preds = %match
store i64 %12, ptr %error_var, align 8
br label %panic_block
after_check: ; preds = %entry
%9 = load ptr, ptr %retparam, align 8
after_check: ; preds = %match
%14 = load ptr, ptr %retparam, align 8
br label %noerr_block
panic_block: ; preds = %assign_optional
%10 = insertvalue %"any*" undef, ptr %error_var, 0
%11 = insertvalue %"any*" %10, i64 ptrtoint (ptr @"$ct.anyfault" to i64), 1
%12 = getelementptr inbounds [1 x %"any*"], ptr %varargslots, i64 0, i64 0
store %"any*" %11, ptr %12, align 16
%13 = insertvalue %"any*[]" undef, ptr %varargslots, 0
%"$$temp" = insertvalue %"any*[]" %13, i64 1, 1
%15 = insertvalue %"any*" undef, ptr %error_var, 0
%16 = insertvalue %"any*" %15, i64 ptrtoint (ptr @"$ct.anyfault" to i64), 1
%17 = getelementptr inbounds [1 x %"any*"], ptr %varargslots, i64 0, i64 0
store %"any*" %16, ptr %17, align 16
%18 = insertvalue %"any*[]" undef, ptr %varargslots, 0
%"$$temp" = insertvalue %"any*[]" %18, i64 1, 1
store %"any*[]" %"$$temp", ptr %indirectarg, align 8
call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 6, ptr @.func, i64 4, i32 392, ptr byval(%"any*[]") align 8 %indirectarg)
call void @std.core.builtin.panicf(ptr @.panic_msg.1, i64 36, ptr @.file.2, i64 6, ptr @.func, i64 4, i32 390, ptr byval(%"any*[]") align 8 %indirectarg)
unreachable
noerr_block: ; preds = %after_check
%14 = insertvalue %"any*" undef, ptr %9, 0
%15 = insertvalue %"any*" %14, i64 ptrtoint (ptr @"$ct.inherit.Test" to i64), 1
store %"any*" %15, ptr %z, align 8
%16 = getelementptr inbounds %"any*", ptr %z, i32 0, i32 1
%17 = load i64, ptr %16, align 8
%18 = getelementptr inbounds %"any*", ptr %z, i32 0, i32 0
%19 = inttoptr i64 %17 to ptr
%type = load ptr, ptr %.cachedtype, align 8
%20 = icmp eq ptr %19, %type
br i1 %20, label %cache_hit, label %cache_miss
cache_miss: ; preds = %noerr_block
%21 = getelementptr inbounds %.introspect, ptr %19, i32 0, i32 2
%22 = load ptr, ptr %21, align 8
%23 = call ptr @.dyn_search(ptr %22, ptr @"$sel.tesT")
store ptr %23, ptr %.inlinecache, align 8
store ptr %19, ptr %.cachedtype, align 8
br label %24
cache_hit: ; preds = %noerr_block
%cache_hit_fn = load ptr, ptr %.inlinecache, align 8
br label %24
24: ; preds = %cache_hit, %cache_miss
%fn_phi = phi ptr [ %cache_hit_fn, %cache_hit ], [ %23, %cache_miss ]
%25 = icmp eq ptr %fn_phi, null
br i1 %25, label %missing_function, label %match
missing_function: ; preds = %24
%26 = load ptr, ptr @std.core.builtin.panic, align 8
call void %26(ptr @.panic_msg.1, i64 41, ptr @.file.2, i64 10, ptr @.func, i64 4, i32 34)
unreachable
match: ; preds = %24
%27 = load ptr, ptr %18, align 8
call void %fn_phi(ptr %27)
%28 = load %"any*", ptr %z, align 8
store %"any*" %28, ptr %w, align 8
%29 = getelementptr inbounds %"any*", ptr %w, i32 0, i32 1
%30 = load i64, ptr %29, align 8
%31 = getelementptr inbounds %"any*", ptr %w, i32 0, i32 0
%32 = inttoptr i64 %30 to ptr
%19 = insertvalue %"any*" undef, ptr %14, 0
%20 = insertvalue %"any*" %19, i64 ptrtoint (ptr @"$ct.inherit.Test" to i64), 1
store %"any*" %20, ptr %z, align 8
%21 = getelementptr inbounds %"any*", ptr %z, i32 0, i32 1
%22 = load i64, ptr %21, align 8
%23 = getelementptr inbounds %"any*", ptr %z, i32 0, i32 0
%24 = inttoptr i64 %22 to ptr
%type4 = load ptr, ptr %.cachedtype3, align 8
%33 = icmp eq ptr %32, %type4
br i1 %33, label %cache_hit6, label %cache_miss5
%25 = icmp eq ptr %24, %type4
br i1 %25, label %cache_hit6, label %cache_miss5
cache_miss5: ; preds = %match
%34 = getelementptr inbounds %.introspect, ptr %32, i32 0, i32 2
%35 = load ptr, ptr %34, align 8
%36 = call ptr @.dyn_search(ptr %35, ptr @"$sel.tesT")
store ptr %36, ptr %.inlinecache2, align 8
store ptr %32, ptr %.cachedtype3, align 8
br label %37
cache_miss5: ; preds = %noerr_block
%26 = getelementptr inbounds %.introspect, ptr %24, i32 0, i32 2
%27 = load ptr, ptr %26, align 8
%28 = call ptr @.dyn_search(ptr %27, ptr @"$sel.tesT")
store ptr %28, ptr %.inlinecache2, align 8
store ptr %24, ptr %.cachedtype3, align 8
br label %29
cache_hit6: ; preds = %match
cache_hit6: ; preds = %noerr_block
%cache_hit_fn7 = load ptr, ptr %.inlinecache2, align 8
br label %37
br label %29
37: ; preds = %cache_hit6, %cache_miss5
%fn_phi8 = phi ptr [ %cache_hit_fn7, %cache_hit6 ], [ %36, %cache_miss5 ]
%38 = icmp eq ptr %fn_phi8, null
br i1 %38, label %missing_function9, label %match10
29: ; preds = %cache_hit6, %cache_miss5
%fn_phi8 = phi ptr [ %cache_hit_fn7, %cache_hit6 ], [ %28, %cache_miss5 ]
%30 = icmp eq ptr %fn_phi8, null
br i1 %30, label %missing_function9, label %match10
missing_function9: ; preds = %37
%39 = load ptr, ptr @std.core.builtin.panic, align 8
call void %39(ptr @.panic_msg.1, i64 41, ptr @.file.2, i64 10, ptr @.func, i64 4, i32 36)
missing_function9: ; preds = %29
%31 = load ptr, ptr @std.core.builtin.panic, align 8
call void %31(ptr @.panic_msg.3, i64 41, ptr @.file.4, i64 10, ptr @.func, i64 4, i32 34)
unreachable
match10: ; preds = %37
%40 = load ptr, ptr %31, align 8
call void %fn_phi8(ptr %40)
match10: ; preds = %29
%32 = load ptr, ptr %23, align 8
call void %fn_phi8(ptr %32)
%33 = load %"any*", ptr %z, align 8
store %"any*" %33, ptr %w, align 8
%34 = getelementptr inbounds %"any*", ptr %w, i32 0, i32 1
%35 = load i64, ptr %34, align 8
%36 = getelementptr inbounds %"any*", ptr %w, i32 0, i32 0
%37 = inttoptr i64 %35 to ptr
%type13 = load ptr, ptr %.cachedtype12, align 8
%38 = icmp eq ptr %37, %type13
br i1 %38, label %cache_hit15, label %cache_miss14
cache_miss14: ; preds = %match10
%39 = getelementptr inbounds %.introspect, ptr %37, i32 0, i32 2
%40 = load ptr, ptr %39, align 8
%41 = call ptr @.dyn_search(ptr %40, ptr @"$sel.tesT")
store ptr %41, ptr %.inlinecache11, align 8
store ptr %37, ptr %.cachedtype12, align 8
br label %42
cache_hit15: ; preds = %match10
%cache_hit_fn16 = load ptr, ptr %.inlinecache11, align 8
br label %42
42: ; preds = %cache_hit15, %cache_miss14
%fn_phi17 = phi ptr [ %cache_hit_fn16, %cache_hit15 ], [ %41, %cache_miss14 ]
%43 = icmp eq ptr %fn_phi17, null
br i1 %43, label %missing_function18, label %match19
missing_function18: ; preds = %42
%44 = load ptr, ptr @std.core.builtin.panic, align 8
call void %44(ptr @.panic_msg.3, i64 41, ptr @.file.4, i64 10, ptr @.func, i64 4, i32 36)
unreachable
match19: ; preds = %42
%45 = load ptr, ptr %36, align 8
call void %fn_phi17(ptr %45)
ret void
}

View File

@@ -34,15 +34,6 @@ fn void main()
/* #expect: overlap.ll
@"$ct.overlap.Test" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 8, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8
@std.core.mem.thread_allocator = external thread_local global ptr, align 8
@"$ct.anyfault" = linkonce global %.introspect { i8 6, i64 0, ptr null, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8
@.panic_msg = internal constant [37 x i8] c"Unexpected fault '%s' was unwrapped!\00", align 1
@.file = internal constant [7 x i8] c"mem.c3\00", align 1
@.func = internal constant [5 x i8] c"main\00", align 1
@"$sel.tesT" = linkonce_odr constant [5 x i8] c"tesT\00", align 1
@.panic_msg.1 = internal constant [42 x i8] c"No method 'tesT' could be found on target\00", align 1
@.file.2 = internal constant [24 x i8] c"overlapping_function.c3\00", align 1
@std.core.builtin.panic = external global ptr, align 8
@"$ct.dyn.overlap.Test.tesT" = global { ptr, ptr, ptr } { ptr @overlap.Test.tesT, ptr @"$sel.tesT", ptr null }, align 8
@"$ct.dyn.overlap.Test.foo" = global { ptr, ptr, ptr } { ptr @overlap.Test.foo, ptr @"$sel.foo", ptr null }, align 8
@"$sel.foo" = linkonce_odr constant [4 x i8] c"foo\00", align 1
@@ -52,159 +43,160 @@ fn void main()
define void @overlap.main() #0 {
entry:
%z = alloca %"any*", align 8
%using = alloca ptr, align 8
%using = alloca %"any*", align 8
%error_var = alloca i64, align 8
%using1 = alloca ptr, align 8
%allocator = alloca ptr, align 8
%using1 = alloca %"any*", align 8
%self = alloca %"any*", align 8
%.inlinecache = alloca ptr, align 8
%.cachedtype = alloca ptr, align 8
%retparam = alloca ptr, align 8
%varargslots = alloca [1 x %"any*"], align 16
%indirectarg = alloca %"any*[]", align 8
%.inlinecache = alloca ptr, align 8
%.cachedtype = alloca ptr, align 8
%w = alloca %"any*", align 8
%.inlinecache2 = alloca ptr, align 8
%.cachedtype3 = alloca ptr, align 8
%w = alloca %"any*", align 8
%.inlinecache11 = alloca ptr, align 8
%.cachedtype12 = alloca ptr, align 8
store ptr null, ptr %.cachedtype12, align 8
store ptr null, ptr %.cachedtype3, align 8
store ptr null, ptr %.cachedtype, align 8
%0 = load ptr, ptr @std.core.mem.thread_allocator, align 8
store ptr %0, ptr %using, align 8
%1 = load ptr, ptr %using, align 8
store ptr %1, ptr %using1, align 8
%2 = load ptr, ptr %using1, align 8
store ptr %2, ptr %allocator, align 8
%3 = load ptr, ptr %allocator, align 8
%4 = getelementptr inbounds %Allocator, ptr %3, i32 0, i32 0
%5 = load ptr, ptr %4, align 8
%6 = load ptr, ptr %allocator, align 8
%7 = call i64 %5(ptr %retparam, ptr %6, i64 8, i64 0, i64 0, ptr null, i32 0)
%not_err = icmp eq i64 %7, 0
%8 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
br i1 %8, label %after_check, label %assign_optional
assign_optional: ; preds = %entry
store i64 %7, ptr %error_var, align 8
br label %panic_block
after_check: ; preds = %entry
%9 = load ptr, ptr %retparam, align 8
br label %noerr_block
panic_block: ; preds = %assign_optional
%10 = insertvalue %"any*" undef, ptr %error_var, 0
%11 = insertvalue %"any*" %10, i64 ptrtoint (ptr @"$ct.anyfault" to i64), 1
%12 = getelementptr inbounds [1 x %"any*"], ptr %varargslots, i64 0, i64 0
store %"any*" %11, ptr %12, align 16
%13 = insertvalue %"any*[]" undef, ptr %varargslots, 0
%"$$temp" = insertvalue %"any*[]" %13, i64 1, 1
store %"any*[]" %"$$temp", ptr %indirectarg, align 8
call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 6, ptr @.func, i64 4, i32 392, ptr byval(%"any*[]") align 8 %indirectarg)
unreachable
noerr_block: ; preds = %after_check
%14 = insertvalue %"any*" undef, ptr %9, 0
%15 = insertvalue %"any*" %14, i64 ptrtoint (ptr @"$ct.overlap.Test" to i64), 1
store %"any*" %15, ptr %z, align 8
%16 = getelementptr inbounds %"any*", ptr %z, i32 0, i32 1
%17 = load i64, ptr %16, align 8
%18 = getelementptr inbounds %"any*", ptr %z, i32 0, i32 0
%19 = inttoptr i64 %17 to ptr
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %using, ptr align 8 @std.core.mem.thread_allocator, i32 16, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %using1, ptr align 8 %using, i32 16, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %self, ptr align 8 %using1, i32 16, i1 false)
%0 = getelementptr inbounds %"any*", ptr %self, i32 0, i32 1
%1 = load i64, ptr %0, align 8
%2 = getelementptr inbounds %"any*", ptr %self, i32 0, i32 0
%3 = inttoptr i64 %1 to ptr
%type = load ptr, ptr %.cachedtype, align 8
%20 = icmp eq ptr %19, %type
br i1 %20, label %cache_hit, label %cache_miss
cache_miss: ; preds = %noerr_block
%21 = getelementptr inbounds %.introspect, ptr %19, i32 0, i32 2
%22 = load ptr, ptr %21, align 8
%23 = call ptr @.dyn_search(ptr %22, ptr @"$sel.tesT")
store ptr %23, ptr %.inlinecache, align 8
store ptr %19, ptr %.cachedtype, align 8
br label %24
cache_hit: ; preds = %noerr_block
%4 = icmp eq ptr %3, %type
br i1 %4, label %cache_hit, label %cache_miss
cache_miss: ; preds = %entry
%5 = getelementptr inbounds %.introspect, ptr %3, i32 0, i32 2
%6 = load ptr, ptr %5, align 8
%7 = call ptr @.dyn_search(ptr %6, ptr @"$sel.acquire")
store ptr %7, ptr %.inlinecache, align 8
store ptr %3, ptr %.cachedtype, align 8
br label %8
cache_hit: ; preds = %entry
%cache_hit_fn = load ptr, ptr %.inlinecache, align 8
br label %24
24: ; preds = %cache_hit, %cache_miss
%fn_phi = phi ptr [ %cache_hit_fn, %cache_hit ], [ %23, %cache_miss ]
%25 = icmp eq ptr %fn_phi, null
br i1 %25, label %missing_function, label %match
missing_function: ; preds = %24
%26 = load ptr, ptr @std.core.builtin.panic, align 8
call void %26(ptr @.panic_msg.1, i64 41, ptr @.file.2, i64 23, ptr @.func, i64 4, i32 28)
br label %8
8: ; preds = %cache_hit, %cache_miss
%fn_phi = phi ptr [ %cache_hit_fn, %cache_hit ], [ %7, %cache_miss ]
%9 = icmp eq ptr %fn_phi, null
br i1 %9, label %missing_function, label %match
missing_function: ; preds = %8
%10 = load ptr, ptr @std.core.builtin.panic, align 8
call void %10(ptr @.panic_msg, i64 44, ptr @.file, i64 16, ptr @.func, i64 4, i32 28)
unreachable
match: ; preds = %24
%27 = load ptr, ptr %18, align 8
call void %fn_phi(ptr %27)
%28 = load %"any*", ptr %z, align 8
store %"any*" %28, ptr %w, align 8
%29 = getelementptr inbounds %"any*", ptr %w, i32 0, i32 1
%30 = load i64, ptr %29, align 8
%31 = getelementptr inbounds %"any*", ptr %w, i32 0, i32 0
%32 = inttoptr i64 %30 to ptr
match: ; preds = %8
%11 = load ptr, ptr %2, align 8
%12 = call i64 %fn_phi(ptr %retparam, ptr %11, i64 8, i8 zeroext 0, i64 0, i64 0)
%not_err = icmp eq i64 %12, 0
%13 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
br i1 %13, label %after_check, label %assign_optional
assign_optional: ; preds = %match
store i64 %12, ptr %error_var, align 8
br label %panic_block
after_check: ; preds = %match
%14 = load ptr, ptr %retparam, align 8
br label %noerr_block
panic_block: ; preds = %assign_optional
%15 = insertvalue %"any*" undef, ptr %error_var, 0
%16 = insertvalue %"any*" %15, i64 ptrtoint (ptr @"$ct.anyfault" to i64), 1
%17 = getelementptr inbounds [1 x %"any*"], ptr %varargslots, i64 0, i64 0
store %"any*" %16, ptr %17, align 16
%18 = insertvalue %"any*[]" undef, ptr %varargslots, 0
%"$$temp" = insertvalue %"any*[]" %18, i64 1, 1
store %"any*[]" %"$$temp", ptr %indirectarg, align 8
call void @std.core.builtin.panicf(ptr @.panic_msg.1, i64 36, ptr @.file.2, i64 6, ptr @.func, i64 4, i32 390, ptr byval(%"any*[]") align 8 %indirectarg)
unreachable
noerr_block: ; preds = %after_check
%19 = insertvalue %"any*" undef, ptr %14, 0
%20 = insertvalue %"any*" %19, i64 ptrtoint (ptr @"$ct.overlap.Test" to i64), 1
store %"any*" %20, ptr %z, align 8
%21 = getelementptr inbounds %"any*", ptr %z, i32 0, i32 1
%22 = load i64, ptr %21, align 8
%23 = getelementptr inbounds %"any*", ptr %z, i32 0, i32 0
%24 = inttoptr i64 %22 to ptr
%type4 = load ptr, ptr %.cachedtype3, align 8
%33 = icmp eq ptr %32, %type4
br i1 %33, label %cache_hit6, label %cache_miss5
cache_miss5: ; preds = %match
%34 = getelementptr inbounds %.introspect, ptr %32, i32 0, i32 2
%35 = load ptr, ptr %34, align 8
%36 = call ptr @.dyn_search(ptr %35, ptr @"$sel.tesT")
store ptr %36, ptr %.inlinecache2, align 8
store ptr %32, ptr %.cachedtype3, align 8
br label %37
cache_hit6: ; preds = %match
%25 = icmp eq ptr %24, %type4
br i1 %25, label %cache_hit6, label %cache_miss5
cache_miss5: ; preds = %noerr_block
%26 = getelementptr inbounds %.introspect, ptr %24, i32 0, i32 2
%27 = load ptr, ptr %26, align 8
%28 = call ptr @.dyn_search(ptr %27, ptr @"$sel.tesT")
store ptr %28, ptr %.inlinecache2, align 8
store ptr %24, ptr %.cachedtype3, align 8
br label %29
cache_hit6: ; preds = %noerr_block
%cache_hit_fn7 = load ptr, ptr %.inlinecache2, align 8
br label %37
37: ; preds = %cache_hit6, %cache_miss5
%fn_phi8 = phi ptr [ %cache_hit_fn7, %cache_hit6 ], [ %36, %cache_miss5 ]
%38 = icmp eq ptr %fn_phi8, null
br i1 %38, label %missing_function9, label %match10
missing_function9: ; preds = %37
%39 = load ptr, ptr @std.core.builtin.panic, align 8
call void %39(ptr @.panic_msg.1, i64 41, ptr @.file.2, i64 23, ptr @.func, i64 4, i32 30)
br label %29
29: ; preds = %cache_hit6, %cache_miss5
%fn_phi8 = phi ptr [ %cache_hit_fn7, %cache_hit6 ], [ %28, %cache_miss5 ]
%30 = icmp eq ptr %fn_phi8, null
br i1 %30, label %missing_function9, label %match10
missing_function9: ; preds = %29
%31 = load ptr, ptr @std.core.builtin.panic, align 8
call void %31(ptr @.panic_msg.3, i64 41, ptr @.file.4, i64 23, ptr @.func, i64 4, i32 28)
unreachable
match10: ; preds = %37
%40 = load ptr, ptr %31, align 8
call void %fn_phi8(ptr %40)
match10: ; preds = %29
%32 = load ptr, ptr %23, align 8
call void %fn_phi8(ptr %32)
%33 = load %"any*", ptr %z, align 8
store %"any*" %33, ptr %w, align 8
%34 = getelementptr inbounds %"any*", ptr %w, i32 0, i32 1
%35 = load i64, ptr %34, align 8
%36 = getelementptr inbounds %"any*", ptr %w, i32 0, i32 0
%37 = inttoptr i64 %35 to ptr
%type13 = load ptr, ptr %.cachedtype12, align 8
%38 = icmp eq ptr %37, %type13
br i1 %38, label %cache_hit15, label %cache_miss14
cache_miss14: ; preds = %match10
%39 = getelementptr inbounds %.introspect, ptr %37, i32 0, i32 2
%40 = load ptr, ptr %39, align 8
%41 = call ptr @.dyn_search(ptr %40, ptr @"$sel.tesT")
store ptr %41, ptr %.inlinecache11, align 8
store ptr %37, ptr %.cachedtype12, align 8
br label %42
cache_hit15: ; preds = %match10
%cache_hit_fn16 = load ptr, ptr %.inlinecache11, align 8
br label %42
42: ; preds = %cache_hit15, %cache_miss14
%fn_phi17 = phi ptr [ %cache_hit_fn16, %cache_hit15 ], [ %41, %cache_miss14 ]
%43 = icmp eq ptr %fn_phi17, null
br i1 %43, label %missing_function18, label %match19
missing_function18: ; preds = %42
%44 = load ptr, ptr @std.core.builtin.panic, align 8
call void %44(ptr @.panic_msg.3, i64 41, ptr @.file.4, i64 23, ptr @.func, i64 4, i32 30)
unreachable
match19: ; preds = %42
%45 = load ptr, ptr %36, align 8
call void %fn_phi17(ptr %45)
ret void
}
define internal void @.static_initialize.0() {
entry:
br label %dtable_check
dtable_check: ; preds = %dtable_next, %entry
%dtable_ref = phi ptr [ getelementptr inbounds (%.introspect, ptr @"$ct.overlap.Test", i32 0, i32 2), %entry ], [ %next_dtable_ref, %dtable_next ]
%dtable_ptr = load ptr, ptr %dtable_ref, align 8
%0 = icmp eq ptr %dtable_ptr, null
br i1 %0, label %dtable_found, label %dtable_next
dtable_next: ; preds = %dtable_check
%next_dtable_ref = getelementptr inbounds { ptr, ptr, ptr }, ptr %dtable_ptr, i32 0, i32 2
br label %dtable_check
dtable_found: ; preds = %dtable_check
store ptr @"$ct.dyn.overlap.Test.tesT", ptr %dtable_ref, align 8
br label %dtable_check1
dtable_check1: ; preds = %dtable_next4, %dtable_found
%dtable_ref2 = phi ptr [ getelementptr inbounds (%.introspect, ptr @"$ct.overlap.Test", i32 0, i32 2), %dtable_found ], [ %next_dtable_ref5, %dtable_next4 ]
%dtable_ptr3 = load ptr, ptr %dtable_ref2, align 8
%1 = icmp eq ptr %dtable_ptr3, null
br i1 %1, label %dtable_found6, label %dtable_next4
dtable_next4: ; preds = %dtable_check1
%next_dtable_ref5 = getelementptr inbounds { ptr, ptr, ptr }, ptr %dtable_ptr3, i32 0, i32 2
br label %dtable_check1
dtable_found6: ; preds = %dtable_check1
store ptr @"$ct.dyn.overlap.Test.foo", ptr %dtable_ref2, align 8
ret void
}
}

View File

@@ -50,7 +50,6 @@ enum Foo : int(String val)
@"$ct.String" = linkonce global %.introspect { i8 18, i64 ptrtoint (ptr @"$ct.sa$char" to i64), ptr null, i64 16, i64 ptrtoint (ptr @"$ct.sa$char" to i64), i64 0, [0 x i64] zeroinitializer }, align 8
@"$ct.sa$char" = linkonce global %.introspect { i8 16, i64 0, ptr null, i64 16, i64 ptrtoint (ptr @"$ct.char" to i64), i64 0, [0 x i64] zeroinitializer }, align 8
@"$ct.char" = linkonce global %.introspect { i8 3, i64 0, ptr null, i64 1, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8
@std.core.mem.thread_allocator = external thread_local global ptr, align 8
@"$ct.anyfault" = linkonce global %.introspect { i8 6, i64 0, ptr null, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8
define void @test.main(ptr %0, i64 %1) #0 {

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
module testing;
import std::io;
macro char[] read(src, allocator, n)
macro char[] read(src, Allocator* allocator, n)
{
char* data = allocator.alloc(n)!; // #error: Rethrow is only allowed in macros
char* data = allocator.alloc_checked(n)!; // #error: Rethrow is only allowed in macros
src.read_all(data[:n])!;
}

View File

@@ -239,8 +239,9 @@ fn Type getValue(Blob blob)
%Foo2 = type { i32 }
%Bobo = type { i16, float, i16, i16, float, i16 }
%"int[]" = type { ptr, i64 }
%LinkedList = type { ptr, i64, ptr, ptr }
%List = type { i64, i64, ptr, ptr }
%LinkedList = type { %"any*", i64, ptr, ptr }
%"any*" = type { ptr, i64 }
%List = type { i64, i64, %"any*", ptr }
%Foo = type { i32, i32 }
@"$ct.test.Bobo" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 20, i64 0, i64 6, [0 x i64] zeroinitializer }, align 8
@@ -432,7 +433,7 @@ entry:
%1 = call i32 @test.test_static()
%2 = call i32 @test.test_static()
call void @hello_world.hello()
call void @llvm.memset.p0.i64(ptr align 8 %list, i8 0, i64 32, i1 false)
call void @llvm.memset.p0.i64(ptr align 8 %list, i8 0, i64 40, i1 false)
call void @"std.collections.linkedlist$int$.LinkedList.push"(ptr %list, i32 10)
call void @"std.collections.linkedlist$int$.LinkedList.push"(ptr %list, i32 15)
call void @"std.collections.linkedlist$int$.LinkedList.push"(ptr %list, i32 30)
@@ -462,7 +463,7 @@ loop.exit: ; preds = %loop.cond
%10 = call i32 (ptr, ...) @printf(ptr @.str.3, i32 3)
store i32 3, ptr %elements, align 4
%11 = call i32 (ptr, ...) @printf(ptr @.str.4)
call void @llvm.memset.p0.i64(ptr align 8 %array, i8 0, i64 32, i1 false)
call void @llvm.memset.p0.i64(ptr align 8 %array, i8 0, i64 40, i1 false)
call void @"std.collections.list$int$.List.append"(ptr %array, i32 100)
call void @"std.collections.list$int$.List.append"(ptr %array, i32 200)
call void @"std.collections.list$int$.List.append"(ptr %array, i32 400)

View File

@@ -241,8 +241,9 @@ fn Type getValue(Blob blob)
%Foo2 = type { i32 }
%Bobo = type { i16, float, i16, i16, float, i16 }
%"int[]" = type { ptr, i64 }
%LinkedList = type { ptr, i64, ptr, ptr }
%List = type { i64, i64, ptr, ptr }
%LinkedList = type { %"any*", i64, ptr, ptr }
%"any*" = type { ptr, i64 }
%List = type { i64, i64, %"any*", ptr }
%Foo = type { i32, i32 }
$"$ct.test.Bobo" = comdat any
@@ -476,7 +477,7 @@ entry:
%1 = call i32 @test.test_static()
%2 = call i32 @test.test_static()
call void @hello_world.hello()
call void @llvm.memset.p0.i64(ptr align 8 %list, i8 0, i64 32, i1 false)
call void @llvm.memset.p0.i64(ptr align 8 %list, i8 0, i64 40, i1 false)
call void @"std.collections.linkedlist$int$.LinkedList.push"(ptr %list, i32 10)
call void @"std.collections.linkedlist$int$.LinkedList.push"(ptr %list, i32 15)
call void @"std.collections.linkedlist$int$.LinkedList.push"(ptr %list, i32 30)
@@ -506,7 +507,7 @@ loop.exit: ; preds = %loop.cond
%10 = call i32 (ptr, ...) @printf(ptr @.str.3, i32 3)
store i32 3, ptr %elements, align 4
%11 = call i32 (ptr, ...) @printf(ptr @.str.4)
call void @llvm.memset.p0.i64(ptr align 8 %array, i8 0, i64 32, i1 false)
call void @llvm.memset.p0.i64(ptr align 8 %array, i8 0, i64 40, i1 false)
call void @"std.collections.list$int$.List.append"(ptr %array, i32 100)
call void @"std.collections.list$int$.List.append"(ptr %array, i32 200)
call void @"std.collections.list$int$.List.append"(ptr %array, i32 400)

View File

@@ -98,7 +98,7 @@ fn void! TextTemplate.init(&self, String template, String tag_start = "{{", Stri
fn void! TextTemplate.free(&self)
{
self.allocator.free(self.tags)!;
self.allocator.free(self.tags);
*self = {};
}
@@ -186,11 +186,12 @@ entry:
%ft = alloca %TextTemplate, align 8
%error_var = alloca i64, align 8
%indirectarg = alloca %"char[]", align 8
%error_var1 = alloca i64, align 8
%indirectarg1 = alloca %"any*", align 8
%error_var2 = alloca i64, align 8
%varargslots = alloca [1 x %"any*"], align 16
%indirectarg5 = alloca %"any*[]", align 8
%indirectarg6 = alloca %"any*[]", align 8
store %"char[]" { ptr @.str, i64 21 }, ptr %foo_tmpl, align 8
call void @llvm.memset.p0.i64(ptr align 8 %ft, i8 0, i64 64, i1 false)
call void @llvm.memset.p0.i64(ptr align 8 %ft, i8 0, i64 72, i1 false)
%0 = load ptr, ptr @std.core.mem.thread_temp_allocator, align 8
%not = icmp eq ptr %0, null
br i1 %not, label %if.then, label %if.exit
@@ -201,51 +202,54 @@ if.then: ; preds = %entry
if.exit: ; preds = %if.then, %entry
%1 = load ptr, ptr @std.core.mem.thread_temp_allocator, align 8
%2 = getelementptr inbounds %"char[]", ptr %foo_tmpl, i32 0, i32 0
%lo = load ptr, ptr %2, align 8
%3 = getelementptr inbounds %"char[]", ptr %foo_tmpl, i32 0, i32 1
%hi = load i64, ptr %3, align 8
%2 = insertvalue %"any*" undef, ptr %1, 0
%3 = insertvalue %"any*" %2, i64 ptrtoint (ptr @"$ct.std.core.mem.allocator.TempAllocator" to i64), 1
%4 = getelementptr inbounds %"char[]", ptr %foo_tmpl, i32 0, i32 0
%lo = load ptr, ptr %4, align 8
%5 = getelementptr inbounds %"char[]", ptr %foo_tmpl, i32 0, i32 1
%hi = load i64, ptr %5, align 8
store %"char[]" { ptr @.str.2, i64 2 }, ptr %indirectarg, align 8
%4 = call i64 @"abc$text_test.Foo$.TextTemplate.init"(ptr %ft, ptr %lo, i64 %hi, ptr @.str.1, i64 2, ptr byval(%"char[]") align 8 %indirectarg, ptr %1)
%not_err = icmp eq i64 %4, 0
%5 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
br i1 %5, label %after_check, label %assign_optional
store %"any*" %3, ptr %indirectarg1, align 8
%6 = call i64 @"abc$text_test.Foo$.TextTemplate.init"(ptr %ft, ptr %lo, i64 %hi, ptr @.str.1, i64 2, ptr byval(%"char[]") align 8 %indirectarg, ptr byval(%"any*") align 8 %indirectarg1)
%not_err = icmp eq i64 %6, 0
%7 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
br i1 %7, label %after_check, label %assign_optional
assign_optional: ; preds = %if.exit
store i64 %4, ptr %error_var, align 8
store i64 %6, ptr %error_var, align 8
br label %guard_block
after_check: ; preds = %if.exit
br label %noerr_block
guard_block: ; preds = %assign_optional
%6 = load i64, ptr %error_var, align 8
ret i64 %6
%8 = load i64, ptr %error_var, align 8
ret i64 %8
noerr_block: ; preds = %after_check
%7 = call i64 @"abc$text_test.Foo$.TextTemplate.free"(ptr %ft)
%not_err2 = icmp eq i64 %7, 0
%8 = call i1 @llvm.expect.i1(i1 %not_err2, i1 true)
br i1 %8, label %after_check4, label %assign_optional3
%9 = call i64 @"abc$text_test.Foo$.TextTemplate.free"(ptr %ft)
%not_err3 = icmp eq i64 %9, 0
%10 = call i1 @llvm.expect.i1(i1 %not_err3, i1 true)
br i1 %10, label %after_check5, label %assign_optional4
assign_optional3: ; preds = %noerr_block
store i64 %7, ptr %error_var1, align 8
assign_optional4: ; preds = %noerr_block
store i64 %9, ptr %error_var2, align 8
br label %panic_block
after_check4: ; preds = %noerr_block
br label %noerr_block6
after_check5: ; preds = %noerr_block
br label %noerr_block7
panic_block: ; preds = %assign_optional3
%9 = insertvalue %"any*" undef, ptr %error_var1, 0
%10 = insertvalue %"any*" %9, i64 ptrtoint (ptr @"$ct.anyfault" to i64), 1
%11 = getelementptr inbounds [1 x %"any*"], ptr %varargslots, i64 0, i64 0
store %"any*" %10, ptr %11, align 16
%12 = insertvalue %"any*[]" undef, ptr %varargslots, 0
%"$$temp" = insertvalue %"any*[]" %12, i64 1, 1
store %"any*[]" %"$$temp", ptr %indirectarg5, align 8
call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 25, ptr @.func, i64 4, i32 173, ptr byval(%"any*[]") align 8 %indirectarg5)
panic_block: ; preds = %assign_optional4
%11 = insertvalue %"any*" undef, ptr %error_var2, 0
%12 = insertvalue %"any*" %11, i64 ptrtoint (ptr @"$ct.anyfault" to i64), 1
%13 = getelementptr inbounds [1 x %"any*"], ptr %varargslots, i64 0, i64 0
store %"any*" %12, ptr %13, align 16
%14 = insertvalue %"any*[]" undef, ptr %varargslots, 0
%"$$temp" = insertvalue %"any*[]" %14, i64 1, 1
store %"any*[]" %"$$temp", ptr %indirectarg6, align 8
call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 25, ptr @.func, i64 4, i32 173, ptr byval(%"any*[]") align 8 %indirectarg6)
unreachable
noerr_block6: ; preds = %after_check4
noerr_block7: ; preds = %after_check5
ret i64 0
}

View File

@@ -17,5 +17,5 @@ TreeNode abc;
/* #expect: test.ll
%TreeNode = type { ptr, ptr, %List }
%List = type { i64, i64, ptr, ptr }
@test.abc = local_unnamed_addr global %TreeNode zeroinitializer, align 8
%List = type { i64, i64, %"any*", ptr }
@test.abc = local_unnamed_addr global %TreeNode zeroinitializer, align 8

View File

@@ -17,7 +17,7 @@ fn void main()
define void @test.main() #0 {
entry:
%map = alloca %HashMap, align 8
call void @llvm.memset.p0.i64(ptr align 8 %map, i8 0, i64 40, i1 false)
call void @llvm.memset.p0.i64(ptr align 8 %map, i8 0, i64 48, i1 false)
%0 = call i8 @"std.collections.map$sa$char$int$.HashMap.set"(ptr %map, ptr @.str, i64 5, i32 4)
%1 = call i8 @"std.collections.map$sa$char$int$.HashMap.set"(ptr %map, ptr @.str.1, i64 3, i32 5)
ret void

View File

@@ -52,32 +52,38 @@ fn void main()
/* #expect: test.ll
@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1, ptr @.static_initialize.0, ptr null }]
define { ptr, i64 } @test.Foo.to_string(ptr %0, ptr %1) #0 {
define { ptr, i64 } @test.Foo.to_string(ptr %0, i64 %1, ptr %2) #0 {
entry:
%allocator = alloca %"any*", align 8
%s = alloca ptr, align 8
%varargslots = alloca [2 x %"any*"], align 16
%retparam = alloca i64, align 8
%result = alloca %"char[]", align 8
%2 = call ptr @std.core.dstring.new_with_capacity(i64 128, ptr %1)
store ptr %2, ptr %s, align 8
%3 = getelementptr inbounds %Foo, ptr %0, i32 0, i32 0
%4 = insertvalue %"any*" undef, ptr %3, 0
%5 = insertvalue %"any*" %4, i64 ptrtoint (ptr @"$ct.int" to i64), 1
%6 = getelementptr inbounds [2 x %"any*"], ptr %varargslots, i64 0, i64 0
store %"any*" %5, ptr %6, align 16
%7 = getelementptr inbounds %Foo, ptr %0, i32 0, i32 1
%8 = insertvalue %"any*" undef, ptr %7, 0
%9 = insertvalue %"any*" %8, i64 ptrtoint (ptr @"$ct.p$void" to i64), 1
%10 = getelementptr inbounds [2 x %"any*"], ptr %varargslots, i64 0, i64 1
store %"any*" %9, ptr %10, align 16
%11 = call i64 @std.core.dstring.DString.printf(ptr %retparam, ptr %s, ptr @.str.14, i64 8, ptr %varargslots, i64 2)
%12 = load ptr, ptr %s, align 8
%13 = call { ptr, i64 } @std.core.dstring.DString.str_view(ptr %12)
store { ptr, i64 } %13, ptr %result, align 8
%14 = load { ptr, i64 }, ptr %result, align 8
ret { ptr, i64 } %14
store i64 %1, ptr %allocator, align 8
%ptroffset = getelementptr inbounds ptr, ptr %allocator, i64 1
store ptr %2, ptr %ptroffset, align 8
%3 = getelementptr inbounds { i64, ptr }, ptr %allocator, i32 0, i32 0
%lo = load i64, ptr %3, align 8
%4 = getelementptr inbounds { i64, ptr }, ptr %allocator, i32 0, i32 1
%hi = load ptr, ptr %4, align 8
%5 = call ptr @std.core.dstring.new_with_capacity(i64 128, i64 %lo, ptr %hi)
store ptr %5, ptr %s, align 8
%6 = getelementptr inbounds %Foo, ptr %0, i32 0, i32 0
%7 = insertvalue %"any*" undef, ptr %6, 0
%8 = insertvalue %"any*" %7, i64 ptrtoint (ptr @"$ct.int" to i64), 1
%9 = getelementptr inbounds [2 x %"any*"], ptr %varargslots, i64 0, i64 0
store %"any*" %8, ptr %9, align 16
%10 = getelementptr inbounds %Foo, ptr %0, i32 0, i32 1
%11 = insertvalue %"any*" undef, ptr %10, 0
%12 = insertvalue %"any*" %11, i64 ptrtoint (ptr @"$ct.p$void" to i64), 1
%13 = getelementptr inbounds [2 x %"any*"], ptr %varargslots, i64 0, i64 1
store %"any*" %12, ptr %13, align 16
%14 = call i64 @std.core.dstring.DString.printf(ptr %retparam, ptr %s, ptr @.str.14, i64 8, ptr %varargslots, i64 2)
%15 = load ptr, ptr %s, align 8
%16 = call { ptr, i64 } @std.core.dstring.DString.str_view(ptr %15)
store { ptr, i64 } %16, ptr %result, align 8
%17 = load { ptr, i64 }, ptr %result, align 8
ret { ptr, i64 } %17
}
; Function Attrs: nounwind
@@ -87,194 +93,194 @@ entry:
%varargslots = alloca [1 x %"any*"], align 16
%retparam = alloca i64, align 8
%literal = alloca %Foo, align 8
%varargslots1 = alloca [1 x %"any*"], align 16
%retparam2 = alloca i64, align 8
%literal3 = alloca %Foo, align 8
%varargslots6 = alloca [1 x %"any*"], align 16
%retparam7 = alloca i64, align 8
%varargslots3 = alloca [1 x %"any*"], align 16
%retparam4 = alloca i64, align 8
%literal5 = alloca %Foo, align 8
%varargslots8 = alloca [1 x %"any*"], align 16
%retparam9 = alloca %Foo, align 8
%retparam10 = alloca i64, align 8
%varargslots13 = alloca [1 x %"any*"], align 16
%retparam9 = alloca i64, align 8
%varargslots10 = alloca [1 x %"any*"], align 16
%retparam11 = alloca %Foo, align 8
%retparam12 = alloca i64, align 8
%varargslots15 = alloca [1 x %"any*"], align 16
%taddr = alloca i8, align 1
%retparam14 = alloca i64, align 8
%varargslots17 = alloca [1 x %"any*"], align 16
%taddr18 = alloca i8, align 1
%retparam19 = alloca i64, align 8
%literal22 = alloca %Foo, align 8
%varargslots25 = alloca [1 x %"any*"], align 16
%retparam16 = alloca i64, align 8
%varargslots19 = alloca [1 x %"any*"], align 16
%taddr20 = alloca i8, align 1
%retparam21 = alloca i64, align 8
%literal24 = alloca %Foo, align 8
%varargslots27 = alloca [1 x %"any*"], align 16
%result = alloca %"Foo[]", align 8
%retparam26 = alloca i64, align 8
%retparam30 = alloca i64, align 8
%map2 = alloca %HashMap.0, align 8
%varargslots29 = alloca [1 x %"any*"], align 16
%taddr30 = alloca i8, align 1
%retparam31 = alloca i64, align 8
%varargslots34 = alloca [1 x %"any*"], align 16
%taddr35 = alloca i8, align 1
%retparam36 = alloca i64, align 8
%varargslots39 = alloca [1 x %"any*"], align 16
%result40 = alloca %"int[]", align 8
%retparam41 = alloca i64, align 8
%varargslots44 = alloca [1 x %"any*"], align 16
%result45 = alloca %"double[]", align 8
%retparam46 = alloca i64, align 8
%varargslots35 = alloca [1 x %"any*"], align 16
%taddr36 = alloca i8, align 1
%retparam37 = alloca i64, align 8
%varargslots40 = alloca [1 x %"any*"], align 16
%taddr41 = alloca i8, align 1
%retparam42 = alloca i64, align 8
%varargslots45 = alloca [1 x %"any*"], align 16
%result48 = alloca %"int[]", align 8
%retparam49 = alloca i64, align 8
%varargslots52 = alloca [1 x %"any*"], align 16
%result55 = alloca %"double[]", align 8
%retparam56 = alloca i64, align 8
%current = alloca ptr, align 8
%mark = alloca i64, align 8
%map3 = alloca %HashMap.0, align 8
%varargslots49 = alloca [1 x %"any*"], align 16
%result50 = alloca %"int[]", align 8
%retparam51 = alloca i64, align 8
%mark54 = alloca i64, align 8
%retparam55 = alloca ptr, align 8
call void @llvm.memset.p0.i64(ptr align 8 %map, i8 0, i64 40, i1 false)
%0 = load ptr, ptr @std.core.mem.thread_allocator, align 8
call void @"std.collections.map$int$test.Foo$.HashMap.init"(ptr %map, i32 16, float 7.500000e-01, ptr %0)
%1 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2
%2 = insertvalue %"any*" undef, ptr %1, 0
%3 = insertvalue %"any*" %2, i64 ptrtoint (ptr @"$ct.uint" to i64), 1
%4 = getelementptr inbounds [1 x %"any*"], ptr %varargslots, i64 0, i64 0
store %"any*" %3, ptr %4, align 16
%5 = call i64 @std.io.printfn(ptr %retparam, ptr @.str, i64 12, ptr %varargslots, i64 1)
%varargslots61 = alloca [1 x %"any*"], align 16
%result64 = alloca %"int[]", align 8
%retparam65 = alloca i64, align 8
call void @llvm.memset.p0.i64(ptr align 8 %map, i8 0, i64 48, i1 false)
%lo = load i64, ptr @std.core.mem.thread_allocator, align 8
%hi = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8
call void @"std.collections.map$int$test.Foo$.HashMap.init"(ptr %map, i32 16, float 7.500000e-01, i64 %lo, ptr %hi)
%0 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2
%1 = insertvalue %"any*" undef, ptr %0, 0
%2 = insertvalue %"any*" %1, i64 ptrtoint (ptr @"$ct.uint" to i64), 1
%3 = getelementptr inbounds [1 x %"any*"], ptr %varargslots, i64 0, i64 0
store %"any*" %2, ptr %3, align 16
%4 = call i64 @std.io.printfn(ptr %retparam, ptr @.str, i64 12, ptr %varargslots, i64 1)
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal, ptr align 8 @.__const, i32 16, i1 false)
%6 = getelementptr inbounds { i64, ptr }, ptr %literal, i32 0, i32 0
%lo = load i64, ptr %6, align 8
%7 = getelementptr inbounds { i64, ptr }, ptr %literal, i32 0, i32 1
%hi = load ptr, ptr %7, align 8
%8 = call i8 @"std.collections.map$int$test.Foo$.HashMap.set"(ptr %map, i32 1, i64 %lo, ptr %hi)
%9 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2
%10 = insertvalue %"any*" undef, ptr %9, 0
%11 = insertvalue %"any*" %10, i64 ptrtoint (ptr @"$ct.uint" to i64), 1
%12 = getelementptr inbounds [1 x %"any*"], ptr %varargslots1, i64 0, i64 0
store %"any*" %11, ptr %12, align 16
%13 = call i64 @std.io.printfn(ptr %retparam2, ptr @.str.1, i64 12, ptr %varargslots1, i64 1)
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal3, ptr align 8 @.__const.2, i32 16, i1 false)
%14 = getelementptr inbounds { i64, ptr }, ptr %literal3, i32 0, i32 0
%lo4 = load i64, ptr %14, align 8
%15 = getelementptr inbounds { i64, ptr }, ptr %literal3, i32 0, i32 1
%hi5 = load ptr, ptr %15, align 8
%16 = call i8 @"std.collections.map$int$test.Foo$.HashMap.set"(ptr %map, i32 1, i64 %lo4, ptr %hi5)
%17 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2
%18 = insertvalue %"any*" undef, ptr %17, 0
%19 = insertvalue %"any*" %18, i64 ptrtoint (ptr @"$ct.uint" to i64), 1
%20 = getelementptr inbounds [1 x %"any*"], ptr %varargslots6, i64 0, i64 0
store %"any*" %19, ptr %20, align 16
%21 = call i64 @std.io.printfn(ptr %retparam7, ptr @.str.3, i64 12, ptr %varargslots6, i64 1)
%22 = call i64 @"std.collections.map$int$test.Foo$.HashMap.get"(ptr %retparam9, ptr %map, i32 1)
%not_err = icmp eq i64 %22, 0
%23 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
br i1 %23, label %after_check, label %after_check12
%5 = getelementptr inbounds { i64, ptr }, ptr %literal, i32 0, i32 0
%lo1 = load i64, ptr %5, align 8
%6 = getelementptr inbounds { i64, ptr }, ptr %literal, i32 0, i32 1
%hi2 = load ptr, ptr %6, align 8
%7 = call i8 @"std.collections.map$int$test.Foo$.HashMap.set"(ptr %map, i32 1, i64 %lo1, ptr %hi2)
%8 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2
%9 = insertvalue %"any*" undef, ptr %8, 0
%10 = insertvalue %"any*" %9, i64 ptrtoint (ptr @"$ct.uint" to i64), 1
%11 = getelementptr inbounds [1 x %"any*"], ptr %varargslots3, i64 0, i64 0
store %"any*" %10, ptr %11, align 16
%12 = call i64 @std.io.printfn(ptr %retparam4, ptr @.str.1, i64 12, ptr %varargslots3, i64 1)
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal5, ptr align 8 @.__const.2, i32 16, i1 false)
%13 = getelementptr inbounds { i64, ptr }, ptr %literal5, i32 0, i32 0
%lo6 = load i64, ptr %13, align 8
%14 = getelementptr inbounds { i64, ptr }, ptr %literal5, i32 0, i32 1
%hi7 = load ptr, ptr %14, align 8
%15 = call i8 @"std.collections.map$int$test.Foo$.HashMap.set"(ptr %map, i32 1, i64 %lo6, ptr %hi7)
%16 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2
%17 = insertvalue %"any*" undef, ptr %16, 0
%18 = insertvalue %"any*" %17, i64 ptrtoint (ptr @"$ct.uint" to i64), 1
%19 = getelementptr inbounds [1 x %"any*"], ptr %varargslots8, i64 0, i64 0
store %"any*" %18, ptr %19, align 16
%20 = call i64 @std.io.printfn(ptr %retparam9, ptr @.str.3, i64 12, ptr %varargslots8, i64 1)
%21 = call i64 @"std.collections.map$int$test.Foo$.HashMap.get"(ptr %retparam11, ptr %map, i32 1)
%not_err = icmp eq i64 %21, 0
%22 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
br i1 %22, label %after_check, label %after_check14
after_check: ; preds = %entry
%24 = getelementptr inbounds %Foo, ptr %retparam9, i32 0, i32 0
%25 = insertvalue %"any*" undef, ptr %24, 0
%26 = insertvalue %"any*" %25, i64 ptrtoint (ptr @"$ct.int" to i64), 1
%27 = getelementptr inbounds [1 x %"any*"], ptr %varargslots8, i64 0, i64 0
store %"any*" %26, ptr %27, align 16
%28 = call i64 @std.io.printfn(ptr %retparam10, ptr @.str.4, i64 7, ptr %varargslots8, i64 1)
%not_err11 = icmp eq i64 %28, 0
%29 = call i1 @llvm.expect.i1(i1 %not_err11, i1 true)
br i1 %29, label %after_check12, label %after_check12
%23 = getelementptr inbounds %Foo, ptr %retparam11, i32 0, i32 0
%24 = insertvalue %"any*" undef, ptr %23, 0
%25 = insertvalue %"any*" %24, i64 ptrtoint (ptr @"$ct.int" to i64), 1
%26 = getelementptr inbounds [1 x %"any*"], ptr %varargslots10, i64 0, i64 0
store %"any*" %25, ptr %26, align 16
%27 = call i64 @std.io.printfn(ptr %retparam12, ptr @.str.4, i64 7, ptr %varargslots10, i64 1)
%not_err13 = icmp eq i64 %27, 0
%28 = call i1 @llvm.expect.i1(i1 %not_err13, i1 true)
br i1 %28, label %after_check14, label %after_check14
after_check12: ; preds = %entry, %after_check, %after_check
%30 = call i8 @"std.collections.map$int$test.Foo$.HashMap.has_key"(ptr %map, i32 1)
store i8 %30, ptr %taddr, align 1
%31 = insertvalue %"any*" undef, ptr %taddr, 0
%32 = insertvalue %"any*" %31, i64 ptrtoint (ptr @"$ct.bool" to i64), 1
%33 = getelementptr inbounds [1 x %"any*"], ptr %varargslots13, i64 0, i64 0
store %"any*" %32, ptr %33, align 16
%34 = call i64 @std.io.printfn(ptr %retparam14, ptr @.str.5, i64 9, ptr %varargslots13, i64 1)
%35 = call i8 @"std.collections.map$int$test.Foo$.HashMap.has_key"(ptr %map, i32 2)
store i8 %35, ptr %taddr18, align 1
%36 = insertvalue %"any*" undef, ptr %taddr18, 0
%37 = insertvalue %"any*" %36, i64 ptrtoint (ptr @"$ct.bool" to i64), 1
%38 = getelementptr inbounds [1 x %"any*"], ptr %varargslots17, i64 0, i64 0
store %"any*" %37, ptr %38, align 16
%39 = call i64 @std.io.printfn(ptr %retparam19, ptr @.str.6, i64 9, ptr %varargslots17, i64 1)
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal22, ptr align 8 @.__const.7, i32 16, i1 false)
%40 = getelementptr inbounds { i64, ptr }, ptr %literal22, i32 0, i32 0
%lo23 = load i64, ptr %40, align 8
%41 = getelementptr inbounds { i64, ptr }, ptr %literal22, i32 0, i32 1
%hi24 = load ptr, ptr %41, align 8
%42 = call i8 @"std.collections.map$int$test.Foo$.HashMap.set"(ptr %map, i32 7, i64 %lo23, ptr %hi24)
%43 = load ptr, ptr @std.core.mem.thread_allocator, align 8
%44 = call { ptr, i64 } @"std.collections.map$int$test.Foo$.HashMap.value_list"(ptr %map, ptr %43)
store { ptr, i64 } %44, ptr %result, align 8
%45 = insertvalue %"any*" undef, ptr %result, 0
%46 = insertvalue %"any*" %45, i64 ptrtoint (ptr @"$ct.sa$test.Foo" to i64), 1
%47 = getelementptr inbounds [1 x %"any*"], ptr %varargslots25, i64 0, i64 0
store %"any*" %46, ptr %47, align 16
%48 = call i64 @std.io.printfn(ptr %retparam26, ptr @.str.8, i64 10, ptr %varargslots25, i64 1)
call void @llvm.memset.p0.i64(ptr align 8 %map2, i8 0, i64 40, i1 false)
%49 = load ptr, ptr @std.core.mem.thread_allocator, align 8
call void @"std.collections.map$int$double$.HashMap.init"(ptr %map2, i32 16, float 7.500000e-01, ptr %49)
%50 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map2, i32 4, double 1.300000e+00)
%51 = call i8 @"std.collections.map$int$double$.HashMap.has_value"(ptr %map2, double 1.300000e+00)
store i8 %51, ptr %taddr30, align 1
%52 = insertvalue %"any*" undef, ptr %taddr30, 0
%53 = insertvalue %"any*" %52, i64 ptrtoint (ptr @"$ct.bool" to i64), 1
%54 = getelementptr inbounds [1 x %"any*"], ptr %varargslots29, i64 0, i64 0
store %"any*" %53, ptr %54, align 16
%55 = call i64 @std.io.printfn(ptr %retparam31, ptr @.str.9, i64 12, ptr %varargslots29, i64 1)
%56 = call i8 @"std.collections.map$int$double$.HashMap.has_value"(ptr %map2, double 1.200000e+00)
store i8 %56, ptr %taddr35, align 1
%57 = insertvalue %"any*" undef, ptr %taddr35, 0
%58 = insertvalue %"any*" %57, i64 ptrtoint (ptr @"$ct.bool" to i64), 1
%59 = getelementptr inbounds [1 x %"any*"], ptr %varargslots34, i64 0, i64 0
store %"any*" %58, ptr %59, align 16
%60 = call i64 @std.io.printfn(ptr %retparam36, ptr @.str.10, i64 12, ptr %varargslots34, i64 1)
%61 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map2, i32 100, double 3.400000e+00)
%62 = load ptr, ptr @std.core.mem.thread_allocator, align 8
%63 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.key_list"(ptr %map2, ptr %62)
store { ptr, i64 } %63, ptr %result40, align 8
%64 = insertvalue %"any*" undef, ptr %result40, 0
%65 = insertvalue %"any*" %64, i64 ptrtoint (ptr @"$ct.sa$int" to i64), 1
%66 = getelementptr inbounds [1 x %"any*"], ptr %varargslots39, i64 0, i64 0
store %"any*" %65, ptr %66, align 16
%67 = call i64 @std.io.printfn(ptr %retparam41, ptr @.str.11, i64 2, ptr %varargslots39, i64 1)
%68 = load ptr, ptr @std.core.mem.thread_allocator, align 8
%69 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.value_list"(ptr %map2, ptr %68)
store { ptr, i64 } %69, ptr %result45, align 8
%70 = insertvalue %"any*" undef, ptr %result45, 0
%71 = insertvalue %"any*" %70, i64 ptrtoint (ptr @"$ct.sa$double" to i64), 1
%72 = getelementptr inbounds [1 x %"any*"], ptr %varargslots44, i64 0, i64 0
store %"any*" %71, ptr %72, align 16
%73 = call i64 @std.io.printfn(ptr %retparam46, ptr @.str.12, i64 2, ptr %varargslots44, i64 1)
%74 = load ptr, ptr @std.core.mem.thread_temp_allocator, align 8
%not = icmp eq ptr %74, null
after_check14: ; preds = %entry, %after_check, %after_check
%29 = call i8 @"std.collections.map$int$test.Foo$.HashMap.has_key"(ptr %map, i32 1)
store i8 %29, ptr %taddr, align 1
%30 = insertvalue %"any*" undef, ptr %taddr, 0
%31 = insertvalue %"any*" %30, i64 ptrtoint (ptr @"$ct.bool" to i64), 1
%32 = getelementptr inbounds [1 x %"any*"], ptr %varargslots15, i64 0, i64 0
store %"any*" %31, ptr %32, align 16
%33 = call i64 @std.io.printfn(ptr %retparam16, ptr @.str.5, i64 9, ptr %varargslots15, i64 1)
%34 = call i8 @"std.collections.map$int$test.Foo$.HashMap.has_key"(ptr %map, i32 2)
store i8 %34, ptr %taddr20, align 1
%35 = insertvalue %"any*" undef, ptr %taddr20, 0
%36 = insertvalue %"any*" %35, i64 ptrtoint (ptr @"$ct.bool" to i64), 1
%37 = getelementptr inbounds [1 x %"any*"], ptr %varargslots19, i64 0, i64 0
store %"any*" %36, ptr %37, align 16
%38 = call i64 @std.io.printfn(ptr %retparam21, ptr @.str.6, i64 9, ptr %varargslots19, i64 1)
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal24, ptr align 8 @.__const.7, i32 16, i1 false)
%39 = getelementptr inbounds { i64, ptr }, ptr %literal24, i32 0, i32 0
%lo25 = load i64, ptr %39, align 8
%40 = getelementptr inbounds { i64, ptr }, ptr %literal24, i32 0, i32 1
%hi26 = load ptr, ptr %40, align 8
%41 = call i8 @"std.collections.map$int$test.Foo$.HashMap.set"(ptr %map, i32 7, i64 %lo25, ptr %hi26)
%lo28 = load i64, ptr @std.core.mem.thread_allocator, align 8
%hi29 = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8
%42 = call { ptr, i64 } @"std.collections.map$int$test.Foo$.HashMap.value_list"(ptr %map, i64 %lo28, ptr %hi29)
store { ptr, i64 } %42, ptr %result, align 8
%43 = insertvalue %"any*" undef, ptr %result, 0
%44 = insertvalue %"any*" %43, i64 ptrtoint (ptr @"$ct.sa$test.Foo" to i64), 1
%45 = getelementptr inbounds [1 x %"any*"], ptr %varargslots27, i64 0, i64 0
store %"any*" %44, ptr %45, align 16
%46 = call i64 @std.io.printfn(ptr %retparam30, ptr @.str.8, i64 10, ptr %varargslots27, i64 1)
call void @llvm.memset.p0.i64(ptr align 8 %map2, i8 0, i64 48, i1 false)
%lo33 = load i64, ptr @std.core.mem.thread_allocator, align 8
%hi34 = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8
call void @"std.collections.map$int$double$.HashMap.init"(ptr %map2, i32 16, float 7.500000e-01, i64 %lo33, ptr %hi34)
%47 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map2, i32 4, double 1.300000e+00)
%48 = call i8 @"std.collections.map$int$double$.HashMap.has_value"(ptr %map2, double 1.300000e+00)
store i8 %48, ptr %taddr36, align 1
%49 = insertvalue %"any*" undef, ptr %taddr36, 0
%50 = insertvalue %"any*" %49, i64 ptrtoint (ptr @"$ct.bool" to i64), 1
%51 = getelementptr inbounds [1 x %"any*"], ptr %varargslots35, i64 0, i64 0
store %"any*" %50, ptr %51, align 16
%52 = call i64 @std.io.printfn(ptr %retparam37, ptr @.str.9, i64 12, ptr %varargslots35, i64 1)
%53 = call i8 @"std.collections.map$int$double$.HashMap.has_value"(ptr %map2, double 1.200000e+00)
store i8 %53, ptr %taddr41, align 1
%54 = insertvalue %"any*" undef, ptr %taddr41, 0
%55 = insertvalue %"any*" %54, i64 ptrtoint (ptr @"$ct.bool" to i64), 1
%56 = getelementptr inbounds [1 x %"any*"], ptr %varargslots40, i64 0, i64 0
store %"any*" %55, ptr %56, align 16
%57 = call i64 @std.io.printfn(ptr %retparam42, ptr @.str.10, i64 12, ptr %varargslots40, i64 1)
%58 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map2, i32 100, double 3.400000e+00)
%lo46 = load i64, ptr @std.core.mem.thread_allocator, align 8
%hi47 = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8
%59 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.key_list"(ptr %map2, i64 %lo46, ptr %hi47)
store { ptr, i64 } %59, ptr %result48, align 8
%60 = insertvalue %"any*" undef, ptr %result48, 0
%61 = insertvalue %"any*" %60, i64 ptrtoint (ptr @"$ct.sa$int" to i64), 1
%62 = getelementptr inbounds [1 x %"any*"], ptr %varargslots45, i64 0, i64 0
store %"any*" %61, ptr %62, align 16
%63 = call i64 @std.io.printfn(ptr %retparam49, ptr @.str.11, i64 2, ptr %varargslots45, i64 1)
%lo53 = load i64, ptr @std.core.mem.thread_allocator, align 8
%hi54 = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8
%64 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.value_list"(ptr %map2, i64 %lo53, ptr %hi54)
store { ptr, i64 } %64, ptr %result55, align 8
%65 = insertvalue %"any*" undef, ptr %result55, 0
%66 = insertvalue %"any*" %65, i64 ptrtoint (ptr @"$ct.sa$double" to i64), 1
%67 = getelementptr inbounds [1 x %"any*"], ptr %varargslots52, i64 0, i64 0
store %"any*" %66, ptr %67, align 16
%68 = call i64 @std.io.printfn(ptr %retparam56, ptr @.str.12, i64 2, ptr %varargslots52, i64 1)
%69 = load ptr, ptr @std.core.mem.thread_temp_allocator, align 8
%not = icmp eq ptr %69, null
br i1 %not, label %if.then, label %if.exit
if.then: ; preds = %after_check12
if.then: ; preds = %after_check14
call void @std.core.mem.init_default_temp_allocators()
br label %if.exit
if.exit: ; preds = %if.then, %after_check12
%75 = load ptr, ptr @std.core.mem.thread_temp_allocator, align 8
store ptr %75, ptr %current, align 8
%76 = load ptr, ptr %current, align 8
%77 = getelementptr inbounds %TempAllocator, ptr %76, i32 0, i32 3
%78 = load i64, ptr %77, align 8
store i64 %78, ptr %mark, align 8
call void @llvm.memset.p0.i64(ptr align 8 %map3, i8 0, i64 40, i1 false)
%79 = load ptr, ptr @std.core.mem.thread_allocator, align 8
call void @"std.collections.map$int$double$.HashMap.init"(ptr %map3, i32 16, float 7.500000e-01, ptr %79)
%80 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map3, i32 5, double 3.200000e+00)
%81 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map3, i32 7, double 5.200000e+00)
%82 = load ptr, ptr @std.core.mem.thread_allocator, align 8
%83 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.key_list"(ptr %map3, ptr %82)
store { ptr, i64 } %83, ptr %result50, align 8
%84 = insertvalue %"any*" undef, ptr %result50, 0
%85 = insertvalue %"any*" %84, i64 ptrtoint (ptr @"$ct.sa$int" to i64), 1
%86 = getelementptr inbounds [1 x %"any*"], ptr %varargslots49, i64 0, i64 0
store %"any*" %85, ptr %86, align 16
%87 = call i64 @std.io.printfn(ptr %retparam51, ptr @.str.13, i64 2, ptr %varargslots49, i64 1)
%88 = load ptr, ptr %current, align 8
%89 = getelementptr inbounds %TempAllocator, ptr %88, i32 0, i32 0
%90 = load i64, ptr %mark, align 8
store i64 %90, ptr %mark54, align 8
%91 = getelementptr inbounds %Allocator, ptr %89, i32 0, i32 0
%92 = load ptr, ptr %91, align 8
%93 = load i64, ptr %mark54, align 8
%94 = call i64 %92(ptr %retparam55, ptr %89, i64 %93, i64 0, i64 0, ptr null, i32 8)
if.exit: ; preds = %if.then, %after_check14
%70 = load ptr, ptr @std.core.mem.thread_temp_allocator, align 8
store ptr %70, ptr %current, align 8
%71 = load ptr, ptr %current, align 8
%72 = getelementptr inbounds %TempAllocator, ptr %71, i32 0, i32 2
%73 = load i64, ptr %72, align 8
store i64 %73, ptr %mark, align 8
call void @llvm.memset.p0.i64(ptr align 8 %map3, i8 0, i64 48, i1 false)
%lo59 = load i64, ptr @std.core.mem.thread_allocator, align 8
%hi60 = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8
call void @"std.collections.map$int$double$.HashMap.init"(ptr %map3, i32 16, float 7.500000e-01, i64 %lo59, ptr %hi60)
%74 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map3, i32 5, double 3.200000e+00)
%75 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map3, i32 7, double 5.200000e+00)
%lo62 = load i64, ptr @std.core.mem.thread_allocator, align 8
%hi63 = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8
%76 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.key_list"(ptr %map3, i64 %lo62, ptr %hi63)
store { ptr, i64 } %76, ptr %result64, align 8
%77 = insertvalue %"any*" undef, ptr %result64, 0
%78 = insertvalue %"any*" %77, i64 ptrtoint (ptr @"$ct.sa$int" to i64), 1
%79 = getelementptr inbounds [1 x %"any*"], ptr %varargslots61, i64 0, i64 0
store %"any*" %78, ptr %79, align 16
%80 = call i64 @std.io.printfn(ptr %retparam65, ptr @.str.13, i64 2, ptr %varargslots61, i64 1)
%81 = load ptr, ptr %current, align 8
%82 = load i64, ptr %mark, align 8
call void @std.core.mem.allocator.TempAllocator.reset(ptr %81, i64 %82)
ret void
}
define internal void @.static_initialize.0() {

View File

@@ -67,13 +67,13 @@ fn void! Lexer.init(&self, Stream* reader, Ident ident, Allocator* using = mem::
assert(end.len > 0 && end.len <= ushort.max);
max_token = max(max_token, (ushort)end.len);
}
char* buf = using.alloc(max_token)!;
char* buf = using.alloc_checked(max_token)!;
*self = { .allocator = using, .reader = reader, .buf = buf[:max_token], .tokens = trie };
}
fn void! Lexer.free(&self)
{
self.allocator.free(self.buf)!;
self.allocator.free(self.buf);
*self = {};
}
@@ -680,20 +680,18 @@ fn void test()
/* #expect: lexer_test.ll
; ModuleID = 'lexer_test'
source_filename = "lexer_test"
target datalayout = "e-m:o-p270:32:32-p271:32
target triple = "x86_64-apple
%.introspect = type { i8, i64, ptr, i64, i64, i64, [0 x i64] }
%"char[]" = type { ptr, i64 }
%"any*" = type { ptr, i64 }
%"UintTest[]" = type { ptr, i64 }
%UintTest = type { %"char[]", i64 }
%ByteReader = type { %Stream, %"char[]", i64 }
%Stream = type { ptr }
%Lexer = type { ptr, ptr, %"char[]", %Trie, ptr, i8, i8, i32, i32, i32, %.anon }
%Lexer = type { %"any*", ptr, %"char[]", %Trie, ptr, i8, i8, i32, i32, i32, %.anon }
%Trie = type { %List }
%List = type { i64, i64, ptr, ptr }
%List = type { i64, i64, %"any*", ptr }
%.anon = type { %"char[]" }
@"$ct.lexer_test.UintTest" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 24, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8
@.enum.KEYWORD1 = internal constant [9 x i8] c"KEYWORD1\00", align 1
@.enum.KEYWORD2 = internal constant [9 x i8] c"KEYWORD2\00", align 1
@@ -711,34 +709,42 @@ target triple = "x86_64-apple
@.str.4 = private unnamed_addr constant [2 x i8] c"\0A\00", align 1
@.str.5 = private unnamed_addr constant [3 x i8] c"*/\00", align 1
@"lexer_test.Comment$end" = linkonce constant [2 x %"char[]"] [%"char[]" { ptr @.str.4, i64 1 }, %"char[]" { ptr @.str.5, i64 2 }], align 8
@std.core.mem.thread_allocator = external thread_local global ptr, align 8
@std.core.mem.thread_allocator = external thread_local global %"any*", align 8
; Function Attrs: nounwind
define zeroext i8 @lexer_test.is_ident_char(i64 %0, i8 zeroext %1) #0 {
entry:
%eq = icmp eq i64 0, %0
br i1 %eq, label %and.rhs, label %and.phi
and.rhs: ; preds = %entry
%2 = call i8 @char.is_alpha(i8 zeroext %1)
%3 = trunc i8 %2 to i1
br label %and.phi
and.phi: ; preds = %and.rhs, %entry
%val = phi i1 [ false, %entry ], [ %3, %and.rhs ]
br i1 %val, label %or.phi, label %or.rhs
or.rhs: ; preds = %and.phi
%lt = icmp ult i64 0, %0
br i1 %lt, label %and.rhs1, label %and.phi2
and.rhs1: ; preds = %or.rhs
%4 = call i8 @char.is_alnum(i8 zeroext %1)
%5 = trunc i8 %4 to i1
br label %and.phi2
and.phi2: ; preds = %and.rhs1, %or.rhs
%val3 = phi i1 [ false, %or.rhs ], [ %5, %and.rhs1 ]
br label %or.phi
or.phi: ; preds = %and.phi2, %and.phi
%val4 = phi i1 [ true, %and.phi ], [ %val3, %and.phi2 ]
%6 = zext i1 %val4 to i8
ret i8 %6
}
; Function Attrs: nounwind
define i64 @lexer_test.lex_uint() #0 {
entry:
@@ -750,7 +756,7 @@ entry:
%lex = alloca %Lexer, align 8
%error_var = alloca i64, align 8
%kind = alloca i8, align 1
%error_var2 = alloca i64, align 8
%error_var4 = alloca i64, align 8
%retparam = alloca i8, align 1
store %"UintTest[]" zeroinitializer, ptr %tcases, align 8
%0 = getelementptr inbounds %"UintTest[]", ptr %tcases, i32 0, i32 1
@@ -758,11 +764,13 @@ entry:
store i64 %1, ptr %.anon, align 8
store i64 0, ptr %.anon1, align 8
br label %loop.cond
loop.cond: ; preds = %noerr_block7, %entry
loop.cond: ; preds = %noerr_block9, %entry
%2 = load i64, ptr %.anon1, align 8
%3 = load i64, ptr %.anon, align 8
%lt = icmp ult i64 %2, %3
br i1 %lt, label %loop.body, label %loop.exit
loop.body: ; preds = %loop.cond
%4 = getelementptr inbounds %"UintTest[]", ptr %tcases, i32 0, i32 0
%5 = load ptr, ptr %4, align 8
@@ -770,49 +778,59 @@ loop.body: ; preds = %loop.cond
%ptroffset = getelementptr inbounds %UintTest, ptr %5, i64 %6
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %tc, ptr align 8 %ptroffset, i32 24, i1 false)
call void @llvm.memset.p0.i64(ptr align 8 %br, i8 0, i64 32, i1 false)
call void @llvm.memset.p0.i64(ptr align 8 %lex, i8 0, i64 104, i1 false)
call void @llvm.memset.p0.i64(ptr align 8 %lex, i8 0, i64 120, i1 false)
%7 = getelementptr inbounds %UintTest, ptr %tc, i32 0, i32 0
%8 = getelementptr inbounds %"char[]", ptr %7, i32 0, i32 0
%lo = load ptr, ptr %8, align 8
%9 = getelementptr inbounds %"char[]", ptr %7, i32 0, i32 1
%hi = load i64, ptr %9, align 8
%10 = call ptr @std.io.ByteReader.init(ptr %br, ptr %lo, i64 %hi)
%11 = load ptr, ptr @std.core.mem.thread_allocator, align 8
%12 = call i64 @"lexer$lexer_test.Token$lexer_test.Comment$.Lexer.init"(ptr %lex, ptr %10, ptr @lexer_test.is_ident_char, ptr %11)
%not_err = icmp eq i64 %12, 0
%13 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
br i1 %13, label %after_check, label %assign_optional
%lo2 = load i64, ptr @std.core.mem.thread_allocator, align 8
%hi3 = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8
%11 = call i64 @"lexer$lexer_test.Token$lexer_test.Comment$.Lexer.init"(ptr %lex, ptr %10, ptr @lexer_test.is_ident_char, i64 %lo2, ptr %hi3)
%not_err = icmp eq i64 %11, 0
%12 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
br i1 %12, label %after_check, label %assign_optional
assign_optional: ; preds = %loop.body
store i64 %12, ptr %error_var, align 8
store i64 %11, ptr %error_var, align 8
br label %guard_block
after_check: ; preds = %loop.body
br label %noerr_block
guard_block: ; preds = %assign_optional
%14 = load i64, ptr %error_var, align 8
ret i64 %14
%13 = load i64, ptr %error_var, align 8
ret i64 %13
noerr_block: ; preds = %after_check
%15 = call i64 @"lexer$lexer_test.Token$lexer_test.Comment$.Lexer.next"(ptr %retparam, ptr %lex)
%not_err3 = icmp eq i64 %15, 0
%16 = call i1 @llvm.expect.i1(i1 %not_err3, i1 true)
br i1 %16, label %after_check5, label %assign_optional4
assign_optional4: ; preds = %noerr_block
store i64 %15, ptr %error_var2, align 8
br label %guard_block6
after_check5: ; preds = %noerr_block
br label %noerr_block7
guard_block6: ; preds = %assign_optional4
%17 = load i64, ptr %error_var2, align 8
ret i64 %17
noerr_block7: ; preds = %after_check5
%18 = load i8, ptr %retparam, align 1
store i8 %18, ptr %kind, align 1
%19 = load i8, ptr %kind, align 1
%eq = icmp eq i8 %19, 1
%14 = call i64 @"lexer$lexer_test.Token$lexer_test.Comment$.Lexer.next"(ptr %retparam, ptr %lex)
%not_err5 = icmp eq i64 %14, 0
%15 = call i1 @llvm.expect.i1(i1 %not_err5, i1 true)
br i1 %15, label %after_check7, label %assign_optional6
assign_optional6: ; preds = %noerr_block
store i64 %14, ptr %error_var4, align 8
br label %guard_block8
after_check7: ; preds = %noerr_block
br label %noerr_block9
guard_block8: ; preds = %assign_optional6
%16 = load i64, ptr %error_var4, align 8
ret i64 %16
noerr_block9: ; preds = %after_check7
%17 = load i8, ptr %retparam, align 1
store i8 %17, ptr %kind, align 1
%18 = load i8, ptr %kind, align 1
%eq = icmp eq i8 %18, 1
call void @llvm.assume(i1 %eq)
%20 = load i64, ptr %.anon1, align 8
%add = add i64 %20, 1
%19 = load i64, ptr %.anon1, align 8
%add = add i64 %19, 1
store i64 %add, ptr %.anon1, align 8
br label %loop.cond
loop.exit: ; preds = %loop.cond
ret i64 0
}