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 macro void LinkedList.free_node(&self, Node* node) @private
{ {
self.allocator.free(node)!!; self.allocator.free(node);
} }
macro Node* LinkedList.alloc_node(&self) @private 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()) 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 load_factor > 0.0 "The load factor must be higher than 0"
* @require !map.allocator "Map was already initialized" * @require !map.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" * @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()) 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) 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()) 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 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 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. // a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem::allocator; module std::core::mem::allocator;
struct ArenaAllocator struct ArenaAllocator (Allocator)
{ {
inline Allocator allocator;
char[] data; char[] data;
usz used; usz used;
} }
@@ -15,83 +14,46 @@ struct ArenaAllocator
**/ **/
fn void ArenaAllocator.init(&self, char[] data) fn void ArenaAllocator.init(&self, char[] data)
{ {
self.function = &arena_allocator_function;
self.data = data; self.data = data;
self.used = 0; self.used = 0;
} }
fn void ArenaAllocator.reset(&self) fn void ArenaAllocator.clear(&self)
{ {
self.used = 0; self.used = 0;
} }
struct ArenaAllocatorHeader @local
module std::core::mem::allocator @private;
struct ArenaAllocatorHeader
{ {
usz size; usz size;
char[*] data; char[*] data;
} }
/** fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
* @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)
{ {
ArenaAllocator* arena = (ArenaAllocator*)data; if (!ptr) return;
bool clear = false; assert((uptr)ptr >= (uptr)self.data.ptr, "Pointer originates from a different allocator.");
switch (kind) ArenaAllocatorHeader* header = ptr - ArenaAllocatorHeader.sizeof;
// Reclaim memory if it's the last element.
if (ptr + header.size == &self.data[self.used])
{ {
case CALLOC: self.used -= header.size + ArenaAllocatorHeader.sizeof;
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;
} }
} }
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 !alignment || math::is_power_of_2(alignment)
* @require math::is_power_of_2(alignment)
* @require size > 0
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big` * @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big` * @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0 * @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset * @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; usz total_len = self.data.len;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?; if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
void* start_mem = self.data.ptr; void* start_mem = self.data.ptr;
@@ -103,20 +65,29 @@ fn void*! ArenaAllocator._alloc(&self, usz size, usz alignment, usz offset)
void* mem = aligned_pointer_to_offset - offset; void* mem = aligned_pointer_to_offset - offset;
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof; ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
header.size = size; header.size = size;
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
return mem; return mem;
} }
/** /**
* @require alignment > 0 `alignment must be non zero` * @require !alignment || math::is_power_of_2(alignment)
* @require math::is_power_of_2(alignment)
* @require size > 0
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big` * @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big` * @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0 * @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset * @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."); assert(old_pointer >= self.data.ptr, "Pointer originates from a different allocator.");
usz total_len = self.data.len; usz total_len = self.data.len;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?; if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
@@ -139,7 +110,7 @@ fn void*! ArenaAllocator._realloc(&self, void *old_pointer, usz size, usz alignm
return old_pointer; return old_pointer;
} }
// Otherwise just allocate new memory. // 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); mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem; return mem;
} }

View File

@@ -3,9 +3,8 @@
// a copy of which can be found in the LICENSE_STDLIB file. // a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem::allocator; module std::core::mem::allocator;
struct DynamicArenaAllocator struct DynamicArenaAllocator (Allocator)
{ {
inline Allocator allocator;
Allocator* backing_allocator; Allocator* backing_allocator;
DynamicArenaPage* page; DynamicArenaPage* page;
DynamicArenaPage* unused_page; DynamicArenaPage* unused_page;
@@ -17,7 +16,6 @@ struct DynamicArenaAllocator
**/ **/
fn void DynamicArenaAllocator.init(&self, usz page_size, Allocator* using = mem::heap()) fn void DynamicArenaAllocator.init(&self, usz page_size, Allocator* using = mem::heap())
{ {
self.function = &dynamic_arena_allocator_function;
self.page = null; self.page = null;
self.unused_page = null; self.unused_page = null;
self.page_size = page_size; self.page_size = page_size;
@@ -30,14 +28,14 @@ fn void DynamicArenaAllocator.free(&self)
while (page) while (page)
{ {
DynamicArenaPage* next_page = page.prev_arena; DynamicArenaPage* next_page = page.prev_arena;
free(page, .using = self.backing_allocator); self.backing_allocator.free(page);
page = next_page; page = next_page;
} }
page = self.unused_page; page = self.unused_page;
while (page) while (page)
{ {
DynamicArenaPage* next_page = page.prev_arena; DynamicArenaPage* next_page = page.prev_arena;
free(page, .using = self.backing_allocator); self.backing_allocator.free(page);
page = next_page; page = next_page;
} }
self.page = null; self.page = null;
@@ -59,11 +57,11 @@ struct DynamicArenaChunk @local
} }
/** /**
* @require ptr
* @require self.page `tried to free pointer on invalid allocator` * @require self.page `tried to free pointer on invalid allocator`
*/ */
fn void DynamicArenaAllocator.free_ptr(&self, void* ptr) @local fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
{ {
if (!ptr) return;
DynamicArenaPage* current_page = self.page; DynamicArenaPage* current_page = self.page;
if (ptr == current_page.current_stack_ptr) if (ptr == current_page.current_stack_ptr)
{ {
@@ -73,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` * @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; DynamicArenaPage* current_page = self.page;
alignment = alignment_for_allocation(alignment); alignment = alignment_for_allocation(alignment);
usz* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX; usz* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX;
@@ -101,13 +107,14 @@ fn void*! DynamicArenaAllocator._realloc(&self, void* old_pointer, usz size, usz
current_page.used += add_size; current_page.used += add_size;
return old_pointer; 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); mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
return new_mem; 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* page = self.page;
DynamicArenaPage** unused_page_ptr = &self.unused_page; DynamicArenaPage** unused_page_ptr = &self.unused_page;
while (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); 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) // 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); DynamicArenaPage*! page = malloc(DynamicArenaPage, .using = self.backing_allocator);
if (catch err = page) 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 !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); alignment = alignment_for_allocation(alignment);
DynamicArenaPage* page = self.page; DynamicArenaPage* page = self.page;
if (!page && self.unused_page) void* ptr = {|
{ 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))
{ {
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset; self.page = page = self.unused_page;
new_used = start + size - page.memory; self.unused_page = page.prev_arena;
if (page.total >= new_used) page.prev_arena = null;
{
self.unused_page = page.prev_arena;
page.prev_arena = self.page;
self.page = page;
break ALLOCATE_NEW;
}
} }
return self._alloc_new(size, alignment, offset); 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;
page.used = new_used; if ALLOCATE_NEW: (new_used > page.total)
assert(start + size == page.memory + page.used); {
void* mem = start; if ((page = self.unused_page))
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 (!old_pointer) return null; start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
allocator.free_ptr(old_pointer); new_used = start + size - page.memory;
return null; 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); return self._alloc_new(size, alignment, offset);
void* mem = allocator._realloc(old_pointer, size, alignment, offset)!; }
return mem; page.used = new_used;
case ALIGNED_FREE: assert(start + size == page.memory + page.used);
case FREE: void* mem = start;
if (!old_pointer) return null; DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem - 1;
allocator.free_ptr(old_pointer); chunk.size = size;
return null; return mem;
case MARK: |}!;
unreachable("Tried to mark a dynamic arena"); if (clear) mem::clear(ptr, size, mem::DEFAULT_MEM_ALIGNMENT);
case RESET: return ptr;
allocator.reset();
return null;
}
} }

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 // Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file. // a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem::allocator; module std::core::mem::allocator;
struct SimpleHeapAllocator struct SimpleHeapAllocator (Allocator)
{ {
inline Allocator allocator;
MemoryAllocFn alloc_fn; MemoryAllocFn alloc_fn;
Header* free_list; Header* free_list;
} }
@@ -19,47 +18,44 @@ struct SimpleHeapAllocator
fn void SimpleHeapAllocator.init(&self, MemoryAllocFn allocator) fn void SimpleHeapAllocator.init(&self, MemoryAllocFn allocator)
{ {
self.alloc_fn = allocator; self.alloc_fn = allocator;
static AllocatorFunction alloc_fn = &simple_heap_allocator_function;
self.allocator = { alloc_fn };
self.free_list = null; self.free_list = null;
} }
/** fn void*! SimpleHeapAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
* @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
{ {
SimpleHeapAllocator* heap = (SimpleHeapAllocator*)this; if (!size) return null;
switch (kind) if (clear)
{ {
case ALIGNED_ALLOC: return alignment > 0 ? @aligned_calloc(self._calloc, size, alignment, offset) : self._calloc(size);
return @aligned_alloc(heap._alloc, size, alignment, offset); }
case ALLOC: return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment, offset) : self._alloc(size);
return heap._alloc(size); }
case ALIGNED_CALLOC:
return @aligned_calloc(heap._calloc, size, alignment, offset); fn void*! SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
case CALLOC: {
return heap._calloc(size); if (!size)
case ALIGNED_REALLOC: {
if (!size) nextcase ALIGNED_FREE; self.release(old_pointer, alignment > 0);
if (!old_pointer) nextcase ALIGNED_CALLOC; return null;
return @aligned_realloc(heap._calloc, heap._free, old_pointer, size, alignment, offset); }
case REALLOC: if (!old_pointer)
if (!size) nextcase FREE; {
if (!old_pointer) nextcase CALLOC; return self.acquire(size, true, alignment, offset);
return heap._realloc(old_pointer, size); }
case RESET: return alignment > 0
unreachable("Reset unsupported"); ? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment, offset)
case ALIGNED_FREE: : self._realloc(old_pointer, size);
@aligned_free(heap._free, old_pointer)!; }
return null;
case FREE: fn void SimpleHeapAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
heap._free(old_pointer); {
return null; if (aligned)
default: {
unreachable(); @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)!; 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 fn void! SimpleHeapAllocator.add_block(&self, usz aligned_bytes) @local

View File

@@ -5,24 +5,6 @@
module std::core::mem::allocator; module std::core::mem::allocator;
import libc; 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 struct AlignedBlock
{ {
@@ -96,47 +78,56 @@ macro void! @aligned_free(#free_fn, void* old_pointer)
$endif $endif
} }
fn void*! libc_allocator_fn(Allocator* unused, usz bytes, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @inline distinct LibcAllocator (Allocator) = uptr;
{
if (!alignment) alignment = mem::DEFAULT_MEM_ALIGNMENT;
assert(math::is_power_of_2(alignment), "Alignment was not a power of 2");
void* data; fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
switch (kind) {
assert(alignment != 0 || offset == 0);
if (clear)
{ {
case ALIGNED_ALLOC: void* data = alignment ? @aligned_calloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment, offset)!! : libc::calloc(bytes, 1);
data = @aligned_alloc(libc::malloc, bytes, alignment, offset)!!; return data ?: AllocationFailure.OUT_OF_MEMORY?;
$if env::TESTING: }
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA; else
$endif {
case ALLOC: void* data = alignment ? @aligned_alloc(libc::malloc, bytes, alignment, offset)!! : libc::malloc(bytes);
data = libc::malloc(bytes); if (!data) return AllocationFailure.OUT_OF_MEMORY?;
$if env::TESTING: $if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA; for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
$endif $endif
case ALIGNED_CALLOC: return data;
data = @aligned_calloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment, offset)!!; }
case CALLOC: }
data = libc::calloc(bytes, 1);
case ALIGNED_REALLOC: fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
if (!bytes) nextcase ALIGNED_FREE; {
if (!old_pointer) nextcase ALIGNED_CALLOC; assert(alignment != 0 || offset == 0);
data = @aligned_realloc(fn void*(usz bytes) => libc::calloc(bytes, 1), libc::free, old_pointer, bytes, alignment, offset)!!; if (!new_bytes)
case REALLOC: {
if (!bytes) nextcase FREE; self.release(old_ptr, alignment > 0);
if (!old_pointer) nextcase CALLOC; return null;
data = libc::realloc(old_pointer, bytes); }
case RESET: if (!old_ptr)
unreachable("Reset unsupported"); {
case ALIGNED_FREE: return self.acquire(new_bytes, true, alignment, offset);
@aligned_free(libc::free, old_pointer)!!; }
return null; if (alignment)
case FREE: {
libc::free(old_pointer); void* data = @aligned_realloc(fn void*(usz bytes) => libc::calloc(bytes, 1), libc::free, old_ptr, new_bytes, alignment, offset)!!;
return null; return data ?: AllocationFailure.OUT_OF_MEMORY?;
default: }
unreachable(); 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; module std::core::mem::allocator;
struct OnStackAllocator struct OnStackAllocator (Allocator)
{ {
inline Allocator allocator;
Allocator* backing_allocator; Allocator* backing_allocator;
char[] data; char[] data;
usz used; usz used;
@@ -22,7 +21,6 @@ struct OnStackAllocatorExtraChunk @local
**/ **/
fn void OnStackAllocator.init(&self, char[] data, Allocator* using = mem::heap()) fn void OnStackAllocator.init(&self, char[] data, Allocator* using = mem::heap())
{ {
self.function = &on_stack_allocator_function;
self.data = data; self.data = data;
self.backing_allocator = using; self.backing_allocator = using;
self.used = 0; self.used = 0;
@@ -35,15 +33,15 @@ fn void OnStackAllocator.free(&self)
{ {
if (chunk.is_aligned) if (chunk.is_aligned)
{ {
self.backing_allocator.free_aligned(chunk.data)!!; self.backing_allocator.free_aligned(chunk.data);
} }
else else
{ {
self.backing_allocator.free(chunk.data)!!; self.backing_allocator.free(chunk.data);
} }
void* old = chunk; void* old = chunk;
chunk = chunk.prev; chunk = chunk.prev;
self.backing_allocator.free(old)!!; self.backing_allocator.free(old);
} }
self.chunk = null; self.chunk = null;
self.used = 0; self.used = 0;
@@ -55,48 +53,12 @@ struct OnStackAllocatorHeader
char[*] data; char[*] data;
} }
/** fn void OnStackAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
* @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
{ {
OnStackAllocator* allocator = (OnStackAllocator*)data; if (!old_pointer) return;
bool clear = false; if (allocation_in_stack_mem(self, old_pointer)) return;
switch (kind) on_stack_allocator_remove_chunk(self, old_pointer);
{ self.release(old_pointer, aligned);
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");
}
} }
fn bool allocation_in_stack_mem(OnStackAllocator* a, void* ptr) @local 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) if (chunk.data == ptr)
{ {
*addr = chunk.prev; *addr = chunk.prev;
a.backing_allocator.free(chunk)!!; a.backing_allocator.free(chunk);
return; return;
} }
addr = &chunk.prev; 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 alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big` * @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0 * @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset * @require mem::aligned_offset(offset, OnStackAllocatorExtraChunk.alignof) == offset
* @require a != null
**/ **/
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"); assert(chunk, "Tried to realloc pointer not belonging to the allocator");
if (aligned) return chunk.data = self.backing_allocator.resize(old_pointer, size, alignment, offset)!;
{
return chunk.data = a.backing_allocator.realloc_aligned(old_pointer, size, alignment, offset)!;
}
return chunk.data = a.backing_allocator.realloc(old_pointer, size)!;
} }
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof; OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
usz old_size = header.size; 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); mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem; return mem;
} }
import std::io;
/** /**
* @require size > 0
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big` * @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big` * @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0 * @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset * @require offset == 0 || alignment > 0
* @require a != null * @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); alignment = alignment_for_allocation(alignment);
usz total_len = a.data.len; usz total_len = self.data.len;
void* start_mem = a.data.ptr; void* start_mem = self.data.ptr;
void* unaligned_pointer_to_offset = start_mem + a.used + OnStackAllocatorHeader.sizeof + offset; 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); 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; usz end = (usz)(aligned_pointer_to_offset - self.data.ptr) + size - offset;
Allocator* backing_allocator = self.backing_allocator;
Allocator* backing_allocator = a.backing_allocator;
if (end > total_len) if (end > total_len)
{ {
OnStackAllocatorExtraChunk* chunk = backing_allocator.alloc(OnStackAllocatorExtraChunk.sizeof)!; OnStackAllocatorExtraChunk* chunk = backing_allocator.alloc_checked(OnStackAllocatorExtraChunk.sizeof)!;
defer catch backing_allocator.free(chunk)!!; defer catch backing_allocator.free(chunk);
defer try a.chunk = chunk; defer try self.chunk = chunk;
*chunk = { .prev = a.chunk, .is_aligned = aligned }; *chunk = { .prev = self.chunk, .is_aligned = aligned };
void* data @noinit; return chunk.data = backing_allocator.acquire(size, clear, aligned ? alignment : 0, offset)!;
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;
} }
a.used = end; self.used = end;
void *mem = aligned_pointer_to_offset - offset; void *mem = aligned_pointer_to_offset - offset;
OnStackAllocatorHeader* header = mem - OnStackAllocatorHeader.sizeof; OnStackAllocatorHeader* header = mem - OnStackAllocatorHeader.sizeof;
header.size = size; header.size = size;

View File

@@ -7,9 +7,8 @@ struct TempAllocatorChunk @local
char[*] data; char[*] data;
} }
struct TempAllocator struct TempAllocator (Allocator)
{ {
inline Allocator allocator;
Allocator* backing_allocator; Allocator* backing_allocator;
TempAllocatorPage* last_page; TempAllocatorPage* last_page;
usz used; usz used;
@@ -17,7 +16,6 @@ struct TempAllocator
char[*] data; char[*] data;
} }
const usz PAGE_IS_ALIGNED @private = (usz)isz.max + 1u; 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)!; TempAllocator* allocator = malloc_checked(TempAllocator, .using = using, .end_padding = size)!;
allocator.last_page = null; allocator.last_page = null;
allocator.function = &temp_allocator_function;
allocator.backing_allocator = using; allocator.backing_allocator = using;
allocator.used = 0; allocator.used = 0;
allocator.capacity = size; allocator.capacity = size;
return allocator; return allocator;
} }
/** fn usz TempAllocator.mark(&self) => self.used;
* @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 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); usz old_size = *(usz*)(old_pointer - DEFAULT_SIZE_PREFIX);
if (old_pointer + old_size == &self.data[self.used]) 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; 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; TempAllocatorPage *last_page = self.last_page;
while (last_page && last_page.mark > mark) while (last_page && last_page.mark > mark)
{ {
TempAllocatorPage *to_free = last_page; TempAllocatorPage *to_free = last_page;
last_page = last_page.prev_page; last_page = last_page.prev_page;
self._free_page(to_free)!; self._free_page(to_free)!!;
} }
self.last_page = last_page; self.last_page = last_page;
self.used = mark; self.used = mark;
@@ -128,21 +90,30 @@ fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size,
*pointer_to_prev = page.prev_page; *pointer_to_prev = page.prev_page;
usz page_size = page.pagesize(); usz page_size = page.pagesize();
// Clear on size > original size. // Clear on size > original size.
void* data = self._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); mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
if (page.is_aligned()) if (page.is_aligned())
{ {
self.backing_allocator.free_aligned(real_pointer)!; self.backing_allocator.free_aligned(real_pointer);
} }
else else
{ {
self.backing_allocator.free(real_pointer)!; self.backing_allocator.free(real_pointer);
} }
return data; 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; TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
if (chunk.size == (usz)-1) if (chunk.size == (usz)-1)
{ {
@@ -153,19 +124,20 @@ fn void*! TempAllocator._realloc(&self, void* pointer, usz size, usz alignment,
} }
// TODO optimize last allocation // 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); mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return data; return data;
} }
/** /**
* @require math::is_power_of_2(alignment) * @require !alignment || math::is_power_of_2(alignment)
* @require size > 0
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big` * @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* start_mem = &self.data;
void* starting_ptr = start_mem + self.used; void* starting_ptr = start_mem + self.used;
void* aligned_header_start = mem::aligned_pointer(starting_ptr, TempAllocatorChunk.alignof); 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 // Here we might need to pad
usz padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, mem::DEFAULT_MEM_ALIGNMENT); usz padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, mem::DEFAULT_MEM_ALIGNMENT);
usz total_alloc_size = padded_header_size + size; 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. // Find the page.
page = alloc + padded_header_size - TempAllocatorPage.sizeof; page = alloc + padded_header_size - TempAllocatorPage.sizeof;

View File

@@ -10,9 +10,8 @@ def PtrMap = HashMap(<uptr, usz>);
// A simple tracking allocator. // A simple tracking allocator.
// It tracks allocations using a hash map but // It tracks allocations using a hash map but
// is not compatible with allocators that uses mark() // is not compatible with allocators that uses mark()
struct TrackingAllocator struct TrackingAllocator (Allocator)
{ {
inline Allocator allocator;
Allocator* inner_allocator; Allocator* inner_allocator;
PtrMap map; PtrMap map;
usz mem_total; usz mem_total;
@@ -26,7 +25,7 @@ struct TrackingAllocator
**/ **/
fn void TrackingAllocator.init(&self, Allocator* using) 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); 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; fn usz TrackingAllocator.allocation_count(&self) => self.map.count;
/** fn void*! TrackingAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
* @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
{ {
TrackingAllocator* this = (TrackingAllocator*)data; void* data = self.inner_allocator.acquire(size, clear, alignment, offset)!;
void* result = this.inner_allocator.function(this.inner_allocator, size, alignment, offset, old_pointer, kind)!; self.allocs_total++;
switch (kind) if (data)
{ {
case CALLOC: self.map.set((uptr)data, size);
case ALIGNED_CALLOC: self.mem_total += size;
case ALLOC: self.allocs_total++;
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;
} }
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; const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
/** /**
* Load a vector from memory according to a mask assuming default alignment. * 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"; $assert !type_alloc_must_be_aligned($vatype(0)) : "Type must be allocated with malloc_aligned";
$if $vacount == 2: $if $vacount == 2:
usz size = $vaarg(1); 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 $else
return ($Type*)using.alloc($Type.sizeof + end_padding); return ($Type*)using.alloc_checked($Type.sizeof + end_padding);
$endif $endif
$else $else
return using.alloc($vaarg(0) + end_padding); return using.alloc_checked($vaarg(0) + end_padding);
$endif $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"; $assert !type_alloc_must_be_aligned($vatype(0)) : "Type must be allocated with calloc_aligned";
$if $vacount == 2: $if $vacount == 2:
usz size = $vaarg(1); 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 $else
return ($Type*)using.calloc($Type.sizeof + end_padding); return ($Type*)using.calloc_checked($Type.sizeof + end_padding);
$endif $endif
$else $else
return using.calloc($vaarg(0) + end_padding); return using.calloc_checked($vaarg(0) + end_padding);
$endif $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 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 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); return using.realloc_aligned(ptr, new_size, alignment);
} }
macro void free(void* ptr, Allocator* using = mem::heap()) @builtin => using.free(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_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(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_aligned_checked(void* ptr, Allocator* using = mem::heap()) @builtin => using.free_aligned(ptr);
/** /**
* Run with a specific allocator inside of the macro body. * 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); var $Type = $vatype(0);
$if $vacount == 2: $if $vacount == 2:
usz size = $vaarg(1); 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 $else
return ($Type*)temp().alloc_aligned($Type.sizeof + end_padding, alignment)!!; return ($Type*)temp().acquire($Type.sizeof + end_padding, false, alignment, 0)!!;
$endif $endif
$else $else
return temp().alloc_aligned($vaarg(0) + end_padding, alignment)!!; return temp().acquire($vaarg(0) + end_padding, false, alignment, 0)!!;
$endif $endif
} }
@@ -546,18 +544,18 @@ macro tcalloc(..., usz end_padding = 0, usz alignment = mem::DEFAULT_MEM_ALIGNME
var $Type = $vatype(0); var $Type = $vatype(0);
$if $vacount == 2: $if $vacount == 2:
usz size = $vaarg(1); 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 $else
return ($Type*)temp().calloc_aligned($Type.sizeof + end_padding, alignment)!!; return ($Type*)temp().acquire($Type.sizeof + end_padding, true, alignment, 0)!!;
$endif $endif
$else $else
return temp().calloc_aligned($vaarg(0) + end_padding, alignment)!!; return temp().acquire($vaarg(0) + end_padding, true, alignment, 0)!!;
$endif $endif
} }
fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin @inline 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 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); var $has_arg = !$checks(var $x = #other_temp);
$if $has_arg: $if $has_arg:
TempAllocator* original = current; TempAllocator* original = current;
if (current == #other_temp) current = temp_allocator_next(); if (current == (void*)#other_temp) current = temp_allocator_next();
$endif $endif
usz mark = current.used; usz mark = current.used;
defer defer
@@ -600,7 +598,7 @@ macro void @pool(TempAllocator* #other_temp = null; @body) @builtin
@body(); @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* thread_temp_allocator @private = null;
tlocal TempAllocator*[2] temp_allocator_pair @private; 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 = usz.sizeof;
const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof; const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof;
const Allocator* NULL_ALLOCATOR = &_NULL_ALLOCATOR; protocol Allocator
const Allocator* LIBC_ALLOCATOR = &_SYSTEM_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); def MemoryAllocFn = fn char[]!(usz);
macro bool is_allocator($Type) // Allocator "functions"
macro void*! Allocator.alloc_checked(&self, usz size)
{ {
return $checks( $if env::TESTING:
$Type mem, char* data = self.acquire(size, false, 0, 0)!;
usz sz = 1, mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
void*! x = mem.alloc(sz), return data;
void*! y = mem.calloc(sz), $else
void*! z = mem.realloc(x, sz), return self.acquire(size, false, 0, 0);
(void)mem.free(x) $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) || $if env::TESTING:
$checks( char* data = self.acquire(size, false, alignment, offset)!;
$Type mem, mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
usz sz = 1, return data;
void*! x = mem.alloc_aligned(sz, sz, az), $else
void*! y = mem.calloc_aligned(sz, sz, sz), return self.acquire(size, false, alignment, offset);
void*! z = mem.realloc_aligned(x, sz, sz, sz), $endif
(void)mem.free_aligned(x, sz, sz)
);
} }
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);
struct Allocator macro void Allocator.free(&self, void* ptr)
{ {
AllocatorFunction function; $if env::TESTING:
if (ptr) ((char*)ptr)[0] = 0xBA;
$endif
self.release(ptr, false);
} }
macro void Allocator.free_aligned(&self, void* ptr)
enum AllocationKind
{ {
ALLOC, $if env::TESTING:
CALLOC, if (ptr) ((char*)ptr)[0] = 0xBA;
REALLOC, $endif
FREE, self.release(ptr, true);
ALIGNED_ALLOC,
ALIGNED_CALLOC,
ALIGNED_REALLOC,
ALIGNED_FREE,
RESET,
MARK,
} }
fault AllocationFailure fault AllocationFailure
@@ -60,75 +68,9 @@ fault AllocationFailure
CHUNK_TOO_LARGE, 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 fn usz alignment_for_allocation(usz alignment) @inline @private
{ {
if (alignment < mem::DEFAULT_MEM_ALIGNMENT) return alignment < mem::DEFAULT_MEM_ALIGNMENT ? alignment = mem::DEFAULT_MEM_ALIGNMENT : alignment;
{
alignment = mem::DEFAULT_MEM_ALIGNMENT;
}
return alignment;
} }

View File

@@ -40,9 +40,9 @@ fn ByteBuffer*! ByteBuffer.init_with_buffer(&self, char[] buf)
return self; 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 = {}; *self = {};
} }

View File

@@ -12,7 +12,7 @@ struct ByteWriter
* @param [&inout] self * @param [&inout] self
* @param [&in] using * @param [&in] using
* @require self.bytes.len == 0 "Init may not run on on already initialized data" * @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()) fn ByteWriter* ByteWriter.init(&self, Allocator* using = mem::heap())
{ {

View File

@@ -13,7 +13,7 @@ struct Summary
struct StringData @private struct StringData @private
{ {
Allocator allocator; Allocator* allocator;
usz len; usz len;
usz capacity; usz capacity;
char[*] chars; 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_rvalue(c, value);
llvm_value = LLVMBuildIsNull(c->builder, value->value, "not"); llvm_value = LLVMBuildIsNull(c->builder, value->value, "not");
break; 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: default:
DEBUG_LOG("Unexpectedly tried to not %s", type_quoted_error_string(inner->type)); DEBUG_LOG("Unexpectedly tried to not %s", type_quoted_error_string(inner->type));
UNREACHABLE UNREACHABLE

View File

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

View File

@@ -1377,6 +1377,14 @@ static inline bool sema_call_check_contract_param_match(SemaContext *context, De
} }
return true; 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) 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 ) // 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: case VARDECL_PARAM_REF:
// &foo // &foo
if (!sema_analyse_expr_lvalue(context, arg)) return false; 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); if (!type_is_any_protocol_ptr(arg->type)) expr_insert_addr(arg);
*optional |= IS_OPTIONAL(arg); *optional |= IS_OPTIONAL(arg);
if (!sema_call_check_contract_param_match(context, param, arg)) return false; if (!sema_call_check_contract_param_match(context, param, arg)) return false;

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 @"$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 @.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 @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.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 @"$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 @"$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 { define void @inherit.main() #0 {
entry: entry:
%z = alloca %"any*", align 8 %z = alloca %"any*", align 8
%using = alloca ptr, align 8 %using = alloca %"any*", align 8
%error_var = alloca i64, align 8 %error_var = alloca i64, align 8
%using1 = alloca ptr, align 8 %using1 = alloca %"any*", align 8
%allocator = alloca ptr, align 8 %self = alloca %"any*", align 8
%.inlinecache = alloca ptr, align 8
%.cachedtype = alloca ptr, align 8
%retparam = alloca ptr, align 8 %retparam = alloca ptr, align 8
%varargslots = alloca [1 x %"any*"], align 16 %varargslots = alloca [1 x %"any*"], align 16
%indirectarg = alloca %"any*[]", align 8 %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 %.inlinecache2 = alloca ptr, align 8
%.cachedtype3 = 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 %.cachedtype3, align 8
store ptr null, ptr %.cachedtype, align 8 store ptr null, ptr %.cachedtype, align 8
%0 = load ptr, ptr @std.core.mem.thread_allocator, align 8 call void @llvm.memcpy.p0.p0.i32(ptr align 8 %using, ptr align 8 @std.core.mem.thread_allocator, i32 16, i1 false)
store ptr %0, ptr %using, align 8 call void @llvm.memcpy.p0.p0.i32(ptr align 8 %using1, ptr align 8 %using, i32 16, i1 false)
%1 = load ptr, ptr %using, align 8 call void @llvm.memcpy.p0.p0.i32(ptr align 8 %self, ptr align 8 %using1, i32 16, i1 false)
store ptr %1, ptr %using1, align 8 %0 = getelementptr inbounds %"any*", ptr %self, i32 0, i32 1
%2 = load ptr, ptr %using1, align 8 %1 = load i64, ptr %0, align 8
store ptr %2, ptr %allocator, align 8 %2 = getelementptr inbounds %"any*", ptr %self, i32 0, i32 0
%3 = load ptr, ptr %allocator, align 8 %3 = inttoptr i64 %1 to ptr
%4 = getelementptr inbounds %Allocator, ptr %3, i32 0, i32 0 %type = load ptr, ptr %.cachedtype, align 8
%5 = load ptr, ptr %4, align 8 %4 = icmp eq ptr %3, %type
%6 = load ptr, ptr %allocator, align 8 br i1 %4, label %cache_hit, label %cache_miss
%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 cache_miss: ; preds = %entry
store i64 %7, ptr %error_var, align 8 %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 br label %panic_block
after_check: ; preds = %entry after_check: ; preds = %match
%9 = load ptr, ptr %retparam, align 8 %14 = load ptr, ptr %retparam, align 8
br label %noerr_block br label %noerr_block
panic_block: ; preds = %assign_optional panic_block: ; preds = %assign_optional
%10 = insertvalue %"any*" undef, ptr %error_var, 0 %15 = insertvalue %"any*" undef, ptr %error_var, 0
%11 = insertvalue %"any*" %10, i64 ptrtoint (ptr @"$ct.anyfault" to i64), 1 %16 = insertvalue %"any*" %15, i64 ptrtoint (ptr @"$ct.anyfault" to i64), 1
%12 = getelementptr inbounds [1 x %"any*"], ptr %varargslots, i64 0, i64 0 %17 = getelementptr inbounds [1 x %"any*"], ptr %varargslots, i64 0, i64 0
store %"any*" %11, ptr %12, align 16 store %"any*" %16, ptr %17, align 16
%13 = insertvalue %"any*[]" undef, ptr %varargslots, 0 %18 = insertvalue %"any*[]" undef, ptr %varargslots, 0
%"$$temp" = insertvalue %"any*[]" %13, i64 1, 1 %"$$temp" = insertvalue %"any*[]" %18, i64 1, 1
store %"any*[]" %"$$temp", ptr %indirectarg, align 8 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 unreachable
noerr_block: ; preds = %after_check noerr_block: ; preds = %after_check
%14 = insertvalue %"any*" undef, ptr %9, 0 %19 = insertvalue %"any*" undef, ptr %14, 0
%15 = insertvalue %"any*" %14, i64 ptrtoint (ptr @"$ct.inherit.Test" to i64), 1 %20 = insertvalue %"any*" %19, i64 ptrtoint (ptr @"$ct.inherit.Test" to i64), 1
store %"any*" %15, ptr %z, align 8 store %"any*" %20, ptr %z, align 8
%16 = getelementptr inbounds %"any*", ptr %z, i32 0, i32 1 %21 = getelementptr inbounds %"any*", ptr %z, i32 0, i32 1
%17 = load i64, ptr %16, align 8 %22 = load i64, ptr %21, align 8
%18 = getelementptr inbounds %"any*", ptr %z, i32 0, i32 0 %23 = getelementptr inbounds %"any*", ptr %z, i32 0, i32 0
%19 = inttoptr i64 %17 to ptr %24 = inttoptr i64 %22 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
%type4 = load ptr, ptr %.cachedtype3, align 8 %type4 = load ptr, ptr %.cachedtype3, align 8
%33 = icmp eq ptr %32, %type4 %25 = icmp eq ptr %24, %type4
br i1 %33, label %cache_hit6, label %cache_miss5 br i1 %25, label %cache_hit6, label %cache_miss5
cache_miss5: ; preds = %match cache_miss5: ; preds = %noerr_block
%34 = getelementptr inbounds %.introspect, ptr %32, i32 0, i32 2 %26 = getelementptr inbounds %.introspect, ptr %24, i32 0, i32 2
%35 = load ptr, ptr %34, align 8 %27 = load ptr, ptr %26, align 8
%36 = call ptr @.dyn_search(ptr %35, ptr @"$sel.tesT") %28 = call ptr @.dyn_search(ptr %27, ptr @"$sel.tesT")
store ptr %36, ptr %.inlinecache2, align 8 store ptr %28, ptr %.inlinecache2, align 8
store ptr %32, ptr %.cachedtype3, align 8 store ptr %24, ptr %.cachedtype3, align 8
br label %37 br label %29
cache_hit6: ; preds = %match cache_hit6: ; preds = %noerr_block
%cache_hit_fn7 = load ptr, ptr %.inlinecache2, align 8 %cache_hit_fn7 = load ptr, ptr %.inlinecache2, align 8
br label %37 br label %29
37: ; preds = %cache_hit6, %cache_miss5 29: ; preds = %cache_hit6, %cache_miss5
%fn_phi8 = phi ptr [ %cache_hit_fn7, %cache_hit6 ], [ %36, %cache_miss5 ] %fn_phi8 = phi ptr [ %cache_hit_fn7, %cache_hit6 ], [ %28, %cache_miss5 ]
%38 = icmp eq ptr %fn_phi8, null %30 = icmp eq ptr %fn_phi8, null
br i1 %38, label %missing_function9, label %match10 br i1 %30, label %missing_function9, label %match10
missing_function9: ; preds = %37 missing_function9: ; preds = %29
%39 = load ptr, ptr @std.core.builtin.panic, align 8 %31 = 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) call void %31(ptr @.panic_msg.3, i64 41, ptr @.file.4, i64 10, ptr @.func, i64 4, i32 34)
unreachable unreachable
match10: ; preds = %37 match10: ; preds = %29
%40 = load ptr, ptr %31, align 8 %32 = load ptr, ptr %23, align 8
call void %fn_phi8(ptr %40) 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 ret void
} }

View File

@@ -34,15 +34,6 @@ fn void main()
/* #expect: overlap.ll /* #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 @"$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.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 @"$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 @"$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 { define void @overlap.main() #0 {
entry: entry:
%z = alloca %"any*", align 8 %z = alloca %"any*", align 8
%using = alloca ptr, align 8 %using = alloca %"any*", align 8
%error_var = alloca i64, align 8 %error_var = alloca i64, align 8
%using1 = alloca ptr, align 8 %using1 = alloca %"any*", align 8
%allocator = alloca ptr, align 8 %self = alloca %"any*", align 8
%.inlinecache = alloca ptr, align 8
%.cachedtype = alloca ptr, align 8
%retparam = alloca ptr, align 8 %retparam = alloca ptr, align 8
%varargslots = alloca [1 x %"any*"], align 16 %varargslots = alloca [1 x %"any*"], align 16
%indirectarg = alloca %"any*[]", align 8 %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 %.inlinecache2 = alloca ptr, align 8
%.cachedtype3 = 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 %.cachedtype3, align 8
store ptr null, ptr %.cachedtype, align 8 store ptr null, ptr %.cachedtype, align 8
%0 = load ptr, ptr @std.core.mem.thread_allocator, align 8 call void @llvm.memcpy.p0.p0.i32(ptr align 8 %using, ptr align 8 @std.core.mem.thread_allocator, i32 16, i1 false)
store ptr %0, ptr %using, align 8 call void @llvm.memcpy.p0.p0.i32(ptr align 8 %using1, ptr align 8 %using, i32 16, i1 false)
%1 = load ptr, ptr %using, align 8 call void @llvm.memcpy.p0.p0.i32(ptr align 8 %self, ptr align 8 %using1, i32 16, i1 false)
store ptr %1, ptr %using1, align 8 %0 = getelementptr inbounds %"any*", ptr %self, i32 0, i32 1
%2 = load ptr, ptr %using1, align 8 %1 = load i64, ptr %0, align 8
store ptr %2, ptr %allocator, align 8 %2 = getelementptr inbounds %"any*", ptr %self, i32 0, i32 0
%3 = load ptr, ptr %allocator, align 8 %3 = inttoptr i64 %1 to ptr
%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
%type = load ptr, ptr %.cachedtype, align 8 %type = load ptr, ptr %.cachedtype, align 8
%20 = icmp eq ptr %19, %type %4 = icmp eq ptr %3, %type
br i1 %20, label %cache_hit, label %cache_miss br i1 %4, label %cache_hit, label %cache_miss
cache_miss: ; preds = %entry
cache_miss: ; preds = %noerr_block %5 = getelementptr inbounds %.introspect, ptr %3, i32 0, i32 2
%21 = getelementptr inbounds %.introspect, ptr %19, i32 0, i32 2 %6 = load ptr, ptr %5, align 8
%22 = load ptr, ptr %21, align 8 %7 = call ptr @.dyn_search(ptr %6, ptr @"$sel.acquire")
%23 = call ptr @.dyn_search(ptr %22, ptr @"$sel.tesT") store ptr %7, ptr %.inlinecache, align 8
store ptr %23, ptr %.inlinecache, align 8 store ptr %3, ptr %.cachedtype, align 8
store ptr %19, ptr %.cachedtype, align 8 br label %8
br label %24 cache_hit: ; preds = %entry
cache_hit: ; preds = %noerr_block
%cache_hit_fn = load ptr, ptr %.inlinecache, align 8 %cache_hit_fn = load ptr, ptr %.inlinecache, align 8
br label %24 br label %8
8: ; preds = %cache_hit, %cache_miss
24: ; preds = %cache_hit, %cache_miss %fn_phi = phi ptr [ %cache_hit_fn, %cache_hit ], [ %7, %cache_miss ]
%fn_phi = phi ptr [ %cache_hit_fn, %cache_hit ], [ %23, %cache_miss ] %9 = icmp eq ptr %fn_phi, null
%25 = icmp eq ptr %fn_phi, null br i1 %9, label %missing_function, label %match
br i1 %25, label %missing_function, label %match missing_function: ; preds = %8
%10 = load ptr, ptr @std.core.builtin.panic, align 8
missing_function: ; preds = %24 call void %10(ptr @.panic_msg, i64 44, ptr @.file, i64 16, ptr @.func, i64 4, i32 28)
%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)
unreachable unreachable
match: ; preds = %8
match: ; preds = %24 %11 = load ptr, ptr %2, align 8
%27 = load ptr, ptr %18, align 8 %12 = call i64 %fn_phi(ptr %retparam, ptr %11, i64 8, i8 zeroext 0, i64 0, i64 0)
call void %fn_phi(ptr %27) %not_err = icmp eq i64 %12, 0
%28 = load %"any*", ptr %z, align 8 %13 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
store %"any*" %28, ptr %w, align 8 br i1 %13, label %after_check, label %assign_optional
%29 = getelementptr inbounds %"any*", ptr %w, i32 0, i32 1 assign_optional: ; preds = %match
%30 = load i64, ptr %29, align 8 store i64 %12, ptr %error_var, align 8
%31 = getelementptr inbounds %"any*", ptr %w, i32 0, i32 0 br label %panic_block
%32 = inttoptr i64 %30 to ptr 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 %type4 = load ptr, ptr %.cachedtype3, align 8
%33 = icmp eq ptr %32, %type4 %25 = icmp eq ptr %24, %type4
br i1 %33, label %cache_hit6, label %cache_miss5 br i1 %25, label %cache_hit6, label %cache_miss5
cache_miss5: ; preds = %noerr_block
cache_miss5: ; preds = %match %26 = getelementptr inbounds %.introspect, ptr %24, i32 0, i32 2
%34 = getelementptr inbounds %.introspect, ptr %32, i32 0, i32 2 %27 = load ptr, ptr %26, align 8
%35 = load ptr, ptr %34, align 8 %28 = call ptr @.dyn_search(ptr %27, ptr @"$sel.tesT")
%36 = call ptr @.dyn_search(ptr %35, ptr @"$sel.tesT") store ptr %28, ptr %.inlinecache2, align 8
store ptr %36, ptr %.inlinecache2, align 8 store ptr %24, ptr %.cachedtype3, align 8
store ptr %32, ptr %.cachedtype3, align 8 br label %29
br label %37 cache_hit6: ; preds = %noerr_block
cache_hit6: ; preds = %match
%cache_hit_fn7 = load ptr, ptr %.inlinecache2, align 8 %cache_hit_fn7 = load ptr, ptr %.inlinecache2, align 8
br label %37 br label %29
29: ; preds = %cache_hit6, %cache_miss5
37: ; preds = %cache_hit6, %cache_miss5 %fn_phi8 = phi ptr [ %cache_hit_fn7, %cache_hit6 ], [ %28, %cache_miss5 ]
%fn_phi8 = phi ptr [ %cache_hit_fn7, %cache_hit6 ], [ %36, %cache_miss5 ] %30 = icmp eq ptr %fn_phi8, null
%38 = icmp eq ptr %fn_phi8, null br i1 %30, label %missing_function9, label %match10
br i1 %38, label %missing_function9, label %match10 missing_function9: ; preds = %29
%31 = load ptr, ptr @std.core.builtin.panic, align 8
missing_function9: ; preds = %37 call void %31(ptr @.panic_msg.3, i64 41, ptr @.file.4, i64 23, ptr @.func, i64 4, i32 28)
%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)
unreachable unreachable
match10: ; preds = %29
match10: ; preds = %37 %32 = load ptr, ptr %23, align 8
%40 = load ptr, ptr %31, align 8 call void %fn_phi8(ptr %32)
call void %fn_phi8(ptr %40) %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 ret void
} }
define internal void @.static_initialize.0() { define internal void @.static_initialize.0() {
entry: entry:
br label %dtable_check br label %dtable_check
dtable_check: ; preds = %dtable_next, %entry 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_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 %dtable_ptr = load ptr, ptr %dtable_ref, align 8
%0 = icmp eq ptr %dtable_ptr, null %0 = icmp eq ptr %dtable_ptr, null
br i1 %0, label %dtable_found, label %dtable_next br i1 %0, label %dtable_found, label %dtable_next
dtable_next: ; preds = %dtable_check dtable_next: ; preds = %dtable_check
%next_dtable_ref = getelementptr inbounds { ptr, ptr, ptr }, ptr %dtable_ptr, i32 0, i32 2 %next_dtable_ref = getelementptr inbounds { ptr, ptr, ptr }, ptr %dtable_ptr, i32 0, i32 2
br label %dtable_check br label %dtable_check
dtable_found: ; preds = %dtable_check dtable_found: ; preds = %dtable_check
store ptr @"$ct.dyn.overlap.Test.tesT", ptr %dtable_ref, align 8 store ptr @"$ct.dyn.overlap.Test.tesT", ptr %dtable_ref, align 8
br label %dtable_check1 br label %dtable_check1
dtable_check1: ; preds = %dtable_next4, %dtable_found 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_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 %dtable_ptr3 = load ptr, ptr %dtable_ref2, align 8
%1 = icmp eq ptr %dtable_ptr3, null %1 = icmp eq ptr %dtable_ptr3, null
br i1 %1, label %dtable_found6, label %dtable_next4 br i1 %1, label %dtable_found6, label %dtable_next4
dtable_next4: ; preds = %dtable_check1 dtable_next4: ; preds = %dtable_check1
%next_dtable_ref5 = getelementptr inbounds { ptr, ptr, ptr }, ptr %dtable_ptr3, i32 0, i32 2 %next_dtable_ref5 = getelementptr inbounds { ptr, ptr, ptr }, ptr %dtable_ptr3, i32 0, i32 2
br label %dtable_check1 br label %dtable_check1
dtable_found6: ; preds = %dtable_check1 dtable_found6: ; preds = %dtable_check1
store ptr @"$ct.dyn.overlap.Test.foo", ptr %dtable_ref2, align 8 store ptr @"$ct.dyn.overlap.Test.foo", ptr %dtable_ref2, align 8
ret void 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.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.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 @"$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 @"$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 { 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; module testing;
import std::io; 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])!; src.read_all(data[:n])!;
} }

View File

@@ -239,8 +239,9 @@ fn Type getValue(Blob blob)
%Foo2 = type { i32 } %Foo2 = type { i32 }
%Bobo = type { i16, float, i16, i16, float, i16 } %Bobo = type { i16, float, i16, i16, float, i16 }
%"int[]" = type { ptr, i64 } %"int[]" = type { ptr, i64 }
%LinkedList = type { ptr, i64, ptr, ptr } %LinkedList = type { %"any*", i64, ptr, ptr }
%List = type { i64, i64, ptr, ptr } %"any*" = type { ptr, i64 }
%List = type { i64, i64, %"any*", ptr }
%Foo = type { i32, i32 } %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 @"$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() %1 = call i32 @test.test_static()
%2 = call i32 @test.test_static() %2 = call i32 @test.test_static()
call void @hello_world.hello() 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 10)
call void @"std.collections.linkedlist$int$.LinkedList.push"(ptr %list, i32 15) call void @"std.collections.linkedlist$int$.LinkedList.push"(ptr %list, i32 15)
call void @"std.collections.linkedlist$int$.LinkedList.push"(ptr %list, i32 30) 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) %10 = call i32 (ptr, ...) @printf(ptr @.str.3, i32 3)
store i32 3, ptr %elements, align 4 store i32 3, ptr %elements, align 4
%11 = call i32 (ptr, ...) @printf(ptr @.str.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 100)
call void @"std.collections.list$int$.List.append"(ptr %array, i32 200) call void @"std.collections.list$int$.List.append"(ptr %array, i32 200)
call void @"std.collections.list$int$.List.append"(ptr %array, i32 400) 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 } %Foo2 = type { i32 }
%Bobo = type { i16, float, i16, i16, float, i16 } %Bobo = type { i16, float, i16, i16, float, i16 }
%"int[]" = type { ptr, i64 } %"int[]" = type { ptr, i64 }
%LinkedList = type { ptr, i64, ptr, ptr } %LinkedList = type { %"any*", i64, ptr, ptr }
%List = type { i64, i64, ptr, ptr } %"any*" = type { ptr, i64 }
%List = type { i64, i64, %"any*", ptr }
%Foo = type { i32, i32 } %Foo = type { i32, i32 }
$"$ct.test.Bobo" = comdat any $"$ct.test.Bobo" = comdat any
@@ -476,7 +477,7 @@ entry:
%1 = call i32 @test.test_static() %1 = call i32 @test.test_static()
%2 = call i32 @test.test_static() %2 = call i32 @test.test_static()
call void @hello_world.hello() 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 10)
call void @"std.collections.linkedlist$int$.LinkedList.push"(ptr %list, i32 15) call void @"std.collections.linkedlist$int$.LinkedList.push"(ptr %list, i32 15)
call void @"std.collections.linkedlist$int$.LinkedList.push"(ptr %list, i32 30) 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) %10 = call i32 (ptr, ...) @printf(ptr @.str.3, i32 3)
store i32 3, ptr %elements, align 4 store i32 3, ptr %elements, align 4
%11 = call i32 (ptr, ...) @printf(ptr @.str.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 100)
call void @"std.collections.list$int$.List.append"(ptr %array, i32 200) call void @"std.collections.list$int$.List.append"(ptr %array, i32 200)
call void @"std.collections.list$int$.List.append"(ptr %array, i32 400) 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) fn void! TextTemplate.free(&self)
{ {
self.allocator.free(self.tags)!; self.allocator.free(self.tags);
*self = {}; *self = {};
} }
@@ -186,11 +186,12 @@ entry:
%ft = alloca %TextTemplate, align 8 %ft = alloca %TextTemplate, align 8
%error_var = alloca i64, align 8 %error_var = alloca i64, align 8
%indirectarg = alloca %"char[]", 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 %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 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 %0 = load ptr, ptr @std.core.mem.thread_temp_allocator, align 8
%not = icmp eq ptr %0, null %not = icmp eq ptr %0, null
br i1 %not, label %if.then, label %if.exit br i1 %not, label %if.then, label %if.exit
@@ -201,51 +202,54 @@ if.then: ; preds = %entry
if.exit: ; preds = %if.then, %entry if.exit: ; preds = %if.then, %entry
%1 = load ptr, ptr @std.core.mem.thread_temp_allocator, align 8 %1 = load ptr, ptr @std.core.mem.thread_temp_allocator, align 8
%2 = getelementptr inbounds %"char[]", ptr %foo_tmpl, i32 0, i32 0 %2 = insertvalue %"any*" undef, ptr %1, 0
%lo = load ptr, ptr %2, align 8 %3 = insertvalue %"any*" %2, i64 ptrtoint (ptr @"$ct.std.core.mem.allocator.TempAllocator" to i64), 1
%3 = getelementptr inbounds %"char[]", ptr %foo_tmpl, i32 0, i32 1 %4 = getelementptr inbounds %"char[]", ptr %foo_tmpl, i32 0, i32 0
%hi = load i64, ptr %3, align 8 %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 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) store %"any*" %3, ptr %indirectarg1, align 8
%not_err = icmp eq i64 %4, 0 %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)
%5 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) %not_err = icmp eq i64 %6, 0
br i1 %5, label %after_check, label %assign_optional %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 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 br label %guard_block
after_check: ; preds = %if.exit after_check: ; preds = %if.exit
br label %noerr_block br label %noerr_block
guard_block: ; preds = %assign_optional guard_block: ; preds = %assign_optional
%6 = load i64, ptr %error_var, align 8 %8 = load i64, ptr %error_var, align 8
ret i64 %6 ret i64 %8
noerr_block: ; preds = %after_check noerr_block: ; preds = %after_check
%7 = call i64 @"abc$text_test.Foo$.TextTemplate.free"(ptr %ft) %9 = call i64 @"abc$text_test.Foo$.TextTemplate.free"(ptr %ft)
%not_err2 = icmp eq i64 %7, 0 %not_err3 = icmp eq i64 %9, 0
%8 = call i1 @llvm.expect.i1(i1 %not_err2, i1 true) %10 = call i1 @llvm.expect.i1(i1 %not_err3, i1 true)
br i1 %8, label %after_check4, label %assign_optional3 br i1 %10, label %after_check5, label %assign_optional4
assign_optional3: ; preds = %noerr_block assign_optional4: ; preds = %noerr_block
store i64 %7, ptr %error_var1, align 8 store i64 %9, ptr %error_var2, align 8
br label %panic_block br label %panic_block
after_check4: ; preds = %noerr_block after_check5: ; preds = %noerr_block
br label %noerr_block6 br label %noerr_block7
panic_block: ; preds = %assign_optional3 panic_block: ; preds = %assign_optional4
%9 = insertvalue %"any*" undef, ptr %error_var1, 0 %11 = insertvalue %"any*" undef, ptr %error_var2, 0
%10 = insertvalue %"any*" %9, i64 ptrtoint (ptr @"$ct.anyfault" to i64), 1 %12 = insertvalue %"any*" %11, i64 ptrtoint (ptr @"$ct.anyfault" to i64), 1
%11 = getelementptr inbounds [1 x %"any*"], ptr %varargslots, i64 0, i64 0 %13 = getelementptr inbounds [1 x %"any*"], ptr %varargslots, i64 0, i64 0
store %"any*" %10, ptr %11, align 16 store %"any*" %12, ptr %13, align 16
%12 = insertvalue %"any*[]" undef, ptr %varargslots, 0 %14 = insertvalue %"any*[]" undef, ptr %varargslots, 0
%"$$temp" = insertvalue %"any*[]" %12, i64 1, 1 %"$$temp" = insertvalue %"any*[]" %14, i64 1, 1
store %"any*[]" %"$$temp", ptr %indirectarg5, align 8 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 %indirectarg5) 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 unreachable
noerr_block6: ; preds = %after_check4 noerr_block7: ; preds = %after_check5
ret i64 0 ret i64 0
} }

View File

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

View File

@@ -17,7 +17,7 @@ fn void main()
define void @test.main() #0 { define void @test.main() #0 {
entry: entry:
%map = alloca %HashMap, align 8 %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) %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) %1 = call i8 @"std.collections.map$sa$char$int$.HashMap.set"(ptr %map, ptr @.str.1, i64 3, i32 5)
ret void ret void

View File

@@ -52,32 +52,38 @@ fn void main()
/* #expect: test.ll /* #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, i64 %1, ptr %2) #0 {
define { ptr, i64 } @test.Foo.to_string(ptr %0, ptr %1) #0 {
entry: entry:
%allocator = alloca %"any*", align 8
%s = alloca ptr, align 8 %s = alloca ptr, align 8
%varargslots = alloca [2 x %"any*"], align 16 %varargslots = alloca [2 x %"any*"], align 16
%retparam = alloca i64, align 8 %retparam = alloca i64, align 8
%result = alloca %"char[]", align 8 %result = alloca %"char[]", align 8
%2 = call ptr @std.core.dstring.new_with_capacity(i64 128, ptr %1) store i64 %1, ptr %allocator, align 8
store ptr %2, ptr %s, align 8 %ptroffset = getelementptr inbounds ptr, ptr %allocator, i64 1
%3 = getelementptr inbounds %Foo, ptr %0, i32 0, i32 0 store ptr %2, ptr %ptroffset, align 8
%4 = insertvalue %"any*" undef, ptr %3, 0 %3 = getelementptr inbounds { i64, ptr }, ptr %allocator, i32 0, i32 0
%5 = insertvalue %"any*" %4, i64 ptrtoint (ptr @"$ct.int" to i64), 1 %lo = load i64, ptr %3, align 8
%6 = getelementptr inbounds [2 x %"any*"], ptr %varargslots, i64 0, i64 0 %4 = getelementptr inbounds { i64, ptr }, ptr %allocator, i32 0, i32 1
store %"any*" %5, ptr %6, align 16 %hi = load ptr, ptr %4, align 8
%7 = getelementptr inbounds %Foo, ptr %0, i32 0, i32 1 %5 = call ptr @std.core.dstring.new_with_capacity(i64 128, i64 %lo, ptr %hi)
%8 = insertvalue %"any*" undef, ptr %7, 0 store ptr %5, ptr %s, align 8
%9 = insertvalue %"any*" %8, i64 ptrtoint (ptr @"$ct.p$void" to i64), 1 %6 = getelementptr inbounds %Foo, ptr %0, i32 0, i32 0
%10 = getelementptr inbounds [2 x %"any*"], ptr %varargslots, i64 0, i64 1 %7 = insertvalue %"any*" undef, ptr %6, 0
store %"any*" %9, ptr %10, align 16 %8 = insertvalue %"any*" %7, i64 ptrtoint (ptr @"$ct.int" to i64), 1
%11 = call i64 @std.core.dstring.DString.printf(ptr %retparam, ptr %s, ptr @.str.14, i64 8, ptr %varargslots, i64 2) %9 = getelementptr inbounds [2 x %"any*"], ptr %varargslots, i64 0, i64 0
%12 = load ptr, ptr %s, align 8 store %"any*" %8, ptr %9, align 16
%13 = call { ptr, i64 } @std.core.dstring.DString.str_view(ptr %12) %10 = getelementptr inbounds %Foo, ptr %0, i32 0, i32 1
store { ptr, i64 } %13, ptr %result, align 8 %11 = insertvalue %"any*" undef, ptr %10, 0
%14 = load { ptr, i64 }, ptr %result, align 8 %12 = insertvalue %"any*" %11, i64 ptrtoint (ptr @"$ct.p$void" to i64), 1
ret { ptr, i64 } %14 %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 ; Function Attrs: nounwind
@@ -87,194 +93,194 @@ entry:
%varargslots = alloca [1 x %"any*"], align 16 %varargslots = alloca [1 x %"any*"], align 16
%retparam = alloca i64, align 8 %retparam = alloca i64, align 8
%literal = alloca %Foo, align 8 %literal = alloca %Foo, align 8
%varargslots1 = alloca [1 x %"any*"], align 16 %varargslots3 = alloca [1 x %"any*"], align 16
%retparam2 = alloca i64, align 8 %retparam4 = alloca i64, align 8
%literal3 = alloca %Foo, align 8 %literal5 = alloca %Foo, align 8
%varargslots6 = alloca [1 x %"any*"], align 16
%retparam7 = alloca i64, align 8
%varargslots8 = alloca [1 x %"any*"], align 16 %varargslots8 = alloca [1 x %"any*"], align 16
%retparam9 = alloca %Foo, align 8 %retparam9 = alloca i64, align 8
%retparam10 = alloca i64, align 8 %varargslots10 = alloca [1 x %"any*"], align 16
%varargslots13 = 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 %taddr = alloca i8, align 1
%retparam14 = alloca i64, align 8 %retparam16 = alloca i64, align 8
%varargslots17 = alloca [1 x %"any*"], align 16 %varargslots19 = alloca [1 x %"any*"], align 16
%taddr18 = alloca i8, align 1 %taddr20 = alloca i8, align 1
%retparam19 = alloca i64, align 8 %retparam21 = alloca i64, align 8
%literal22 = alloca %Foo, align 8 %literal24 = alloca %Foo, align 8
%varargslots25 = alloca [1 x %"any*"], align 16 %varargslots27 = alloca [1 x %"any*"], align 16
%result = alloca %"Foo[]", align 8 %result = alloca %"Foo[]", align 8
%retparam26 = alloca i64, align 8 %retparam30 = alloca i64, align 8
%map2 = alloca %HashMap.0, align 8 %map2 = alloca %HashMap.0, align 8
%varargslots29 = alloca [1 x %"any*"], align 16 %varargslots35 = alloca [1 x %"any*"], align 16
%taddr30 = alloca i8, align 1 %taddr36 = alloca i8, align 1
%retparam31 = alloca i64, align 8 %retparam37 = alloca i64, align 8
%varargslots34 = alloca [1 x %"any*"], align 16 %varargslots40 = alloca [1 x %"any*"], align 16
%taddr35 = alloca i8, align 1 %taddr41 = alloca i8, align 1
%retparam36 = alloca i64, align 8 %retparam42 = alloca i64, align 8
%varargslots39 = alloca [1 x %"any*"], align 16 %varargslots45 = alloca [1 x %"any*"], align 16
%result40 = alloca %"int[]", align 8 %result48 = alloca %"int[]", align 8
%retparam41 = alloca i64, align 8 %retparam49 = alloca i64, align 8
%varargslots44 = alloca [1 x %"any*"], align 16 %varargslots52 = alloca [1 x %"any*"], align 16
%result45 = alloca %"double[]", align 8 %result55 = alloca %"double[]", align 8
%retparam46 = alloca i64, align 8 %retparam56 = alloca i64, align 8
%current = alloca ptr, align 8 %current = alloca ptr, align 8
%mark = alloca i64, align 8 %mark = alloca i64, align 8
%map3 = alloca %HashMap.0, align 8 %map3 = alloca %HashMap.0, align 8
%varargslots49 = alloca [1 x %"any*"], align 16 %varargslots61 = alloca [1 x %"any*"], align 16
%result50 = alloca %"int[]", align 8 %result64 = alloca %"int[]", align 8
%retparam51 = alloca i64, align 8 %retparam65 = alloca i64, align 8
%mark54 = alloca i64, align 8 call void @llvm.memset.p0.i64(ptr align 8 %map, i8 0, i64 48, i1 false)
%retparam55 = alloca ptr, align 8 %lo = load i64, ptr @std.core.mem.thread_allocator, align 8
call void @llvm.memset.p0.i64(ptr align 8 %map, i8 0, i64 40, i1 false) %hi = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8
%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, i64 %lo, ptr %hi)
call void @"std.collections.map$int$test.Foo$.HashMap.init"(ptr %map, i32 16, float 7.500000e-01, ptr %0) %0 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2
%1 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2 %1 = insertvalue %"any*" undef, ptr %0, 0
%2 = insertvalue %"any*" undef, ptr %1, 0 %2 = insertvalue %"any*" %1, i64 ptrtoint (ptr @"$ct.uint" to i64), 1
%3 = insertvalue %"any*" %2, i64 ptrtoint (ptr @"$ct.uint" to i64), 1 %3 = getelementptr inbounds [1 x %"any*"], ptr %varargslots, i64 0, i64 0
%4 = getelementptr inbounds [1 x %"any*"], ptr %varargslots, i64 0, i64 0 store %"any*" %2, ptr %3, align 16
store %"any*" %3, ptr %4, align 16 %4 = call i64 @std.io.printfn(ptr %retparam, ptr @.str, i64 12, ptr %varargslots, i64 1)
%5 = 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) 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 %5 = getelementptr inbounds { i64, ptr }, ptr %literal, i32 0, i32 0
%lo = load i64, ptr %6, align 8 %lo1 = load i64, ptr %5, align 8
%7 = getelementptr inbounds { i64, ptr }, ptr %literal, i32 0, i32 1 %6 = getelementptr inbounds { i64, ptr }, ptr %literal, i32 0, i32 1
%hi = load ptr, ptr %7, align 8 %hi2 = load ptr, ptr %6, align 8
%8 = call i8 @"std.collections.map$int$test.Foo$.HashMap.set"(ptr %map, i32 1, i64 %lo, ptr %hi) %7 = call i8 @"std.collections.map$int$test.Foo$.HashMap.set"(ptr %map, i32 1, i64 %lo1, ptr %hi2)
%9 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2 %8 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2
%10 = insertvalue %"any*" undef, ptr %9, 0 %9 = insertvalue %"any*" undef, ptr %8, 0
%11 = insertvalue %"any*" %10, i64 ptrtoint (ptr @"$ct.uint" to i64), 1 %10 = insertvalue %"any*" %9, i64 ptrtoint (ptr @"$ct.uint" to i64), 1
%12 = getelementptr inbounds [1 x %"any*"], ptr %varargslots1, i64 0, i64 0 %11 = getelementptr inbounds [1 x %"any*"], ptr %varargslots3, i64 0, i64 0
store %"any*" %11, ptr %12, align 16 store %"any*" %10, ptr %11, align 16
%13 = call i64 @std.io.printfn(ptr %retparam2, ptr @.str.1, i64 12, ptr %varargslots1, i64 1) %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 %literal3, ptr align 8 @.__const.2, i32 16, i1 false) call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal5, ptr align 8 @.__const.2, i32 16, i1 false)
%14 = getelementptr inbounds { i64, ptr }, ptr %literal3, i32 0, i32 0 %13 = getelementptr inbounds { i64, ptr }, ptr %literal5, i32 0, i32 0
%lo4 = load i64, ptr %14, align 8 %lo6 = load i64, ptr %13, align 8
%15 = getelementptr inbounds { i64, ptr }, ptr %literal3, i32 0, i32 1 %14 = getelementptr inbounds { i64, ptr }, ptr %literal5, i32 0, i32 1
%hi5 = load ptr, ptr %15, align 8 %hi7 = load ptr, ptr %14, align 8
%16 = call i8 @"std.collections.map$int$test.Foo$.HashMap.set"(ptr %map, i32 1, i64 %lo4, ptr %hi5) %15 = call i8 @"std.collections.map$int$test.Foo$.HashMap.set"(ptr %map, i32 1, i64 %lo6, ptr %hi7)
%17 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2 %16 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2
%18 = insertvalue %"any*" undef, ptr %17, 0 %17 = insertvalue %"any*" undef, ptr %16, 0
%19 = insertvalue %"any*" %18, i64 ptrtoint (ptr @"$ct.uint" to i64), 1 %18 = insertvalue %"any*" %17, i64 ptrtoint (ptr @"$ct.uint" to i64), 1
%20 = getelementptr inbounds [1 x %"any*"], ptr %varargslots6, i64 0, i64 0 %19 = getelementptr inbounds [1 x %"any*"], ptr %varargslots8, i64 0, i64 0
store %"any*" %19, ptr %20, align 16 store %"any*" %18, ptr %19, align 16
%21 = call i64 @std.io.printfn(ptr %retparam7, ptr @.str.3, i64 12, ptr %varargslots6, i64 1) %20 = call i64 @std.io.printfn(ptr %retparam9, ptr @.str.3, i64 12, ptr %varargslots8, i64 1)
%22 = call i64 @"std.collections.map$int$test.Foo$.HashMap.get"(ptr %retparam9, ptr %map, i32 1) %21 = call i64 @"std.collections.map$int$test.Foo$.HashMap.get"(ptr %retparam11, ptr %map, i32 1)
%not_err = icmp eq i64 %22, 0 %not_err = icmp eq i64 %21, 0
%23 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) %22 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
br i1 %23, label %after_check, label %after_check12 br i1 %22, label %after_check, label %after_check14
after_check: ; preds = %entry after_check: ; preds = %entry
%24 = getelementptr inbounds %Foo, ptr %retparam9, i32 0, i32 0 %23 = getelementptr inbounds %Foo, ptr %retparam11, i32 0, i32 0
%25 = insertvalue %"any*" undef, ptr %24, 0 %24 = insertvalue %"any*" undef, ptr %23, 0
%26 = insertvalue %"any*" %25, i64 ptrtoint (ptr @"$ct.int" to i64), 1 %25 = insertvalue %"any*" %24, i64 ptrtoint (ptr @"$ct.int" to i64), 1
%27 = getelementptr inbounds [1 x %"any*"], ptr %varargslots8, i64 0, i64 0 %26 = getelementptr inbounds [1 x %"any*"], ptr %varargslots10, i64 0, i64 0
store %"any*" %26, ptr %27, align 16 store %"any*" %25, ptr %26, align 16
%28 = call i64 @std.io.printfn(ptr %retparam10, ptr @.str.4, i64 7, ptr %varargslots8, i64 1) %27 = call i64 @std.io.printfn(ptr %retparam12, ptr @.str.4, i64 7, ptr %varargslots10, i64 1)
%not_err11 = icmp eq i64 %28, 0 %not_err13 = icmp eq i64 %27, 0
%29 = call i1 @llvm.expect.i1(i1 %not_err11, i1 true) %28 = call i1 @llvm.expect.i1(i1 %not_err13, i1 true)
br i1 %29, label %after_check12, label %after_check12 br i1 %28, label %after_check14, label %after_check14
after_check12: ; preds = %entry, %after_check, %after_check after_check14: ; preds = %entry, %after_check, %after_check
%30 = call i8 @"std.collections.map$int$test.Foo$.HashMap.has_key"(ptr %map, i32 1) %29 = call i8 @"std.collections.map$int$test.Foo$.HashMap.has_key"(ptr %map, i32 1)
store i8 %30, ptr %taddr, align 1 store i8 %29, ptr %taddr, align 1
%31 = insertvalue %"any*" undef, ptr %taddr, 0 %30 = insertvalue %"any*" undef, ptr %taddr, 0
%32 = insertvalue %"any*" %31, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 %31 = insertvalue %"any*" %30, i64 ptrtoint (ptr @"$ct.bool" to i64), 1
%33 = getelementptr inbounds [1 x %"any*"], ptr %varargslots13, i64 0, i64 0 %32 = getelementptr inbounds [1 x %"any*"], ptr %varargslots15, i64 0, i64 0
store %"any*" %32, ptr %33, align 16 store %"any*" %31, ptr %32, align 16
%34 = call i64 @std.io.printfn(ptr %retparam14, ptr @.str.5, i64 9, ptr %varargslots13, i64 1) %33 = call i64 @std.io.printfn(ptr %retparam16, ptr @.str.5, i64 9, ptr %varargslots15, i64 1)
%35 = call i8 @"std.collections.map$int$test.Foo$.HashMap.has_key"(ptr %map, i32 2) %34 = call i8 @"std.collections.map$int$test.Foo$.HashMap.has_key"(ptr %map, i32 2)
store i8 %35, ptr %taddr18, align 1 store i8 %34, ptr %taddr20, align 1
%36 = insertvalue %"any*" undef, ptr %taddr18, 0 %35 = insertvalue %"any*" undef, ptr %taddr20, 0
%37 = insertvalue %"any*" %36, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 %36 = insertvalue %"any*" %35, i64 ptrtoint (ptr @"$ct.bool" to i64), 1
%38 = getelementptr inbounds [1 x %"any*"], ptr %varargslots17, i64 0, i64 0 %37 = getelementptr inbounds [1 x %"any*"], ptr %varargslots19, i64 0, i64 0
store %"any*" %37, ptr %38, align 16 store %"any*" %36, ptr %37, align 16
%39 = call i64 @std.io.printfn(ptr %retparam19, ptr @.str.6, i64 9, ptr %varargslots17, i64 1) %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 %literal22, ptr align 8 @.__const.7, i32 16, i1 false) call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal24, ptr align 8 @.__const.7, i32 16, i1 false)
%40 = getelementptr inbounds { i64, ptr }, ptr %literal22, i32 0, i32 0 %39 = getelementptr inbounds { i64, ptr }, ptr %literal24, i32 0, i32 0
%lo23 = load i64, ptr %40, align 8 %lo25 = load i64, ptr %39, align 8
%41 = getelementptr inbounds { i64, ptr }, ptr %literal22, i32 0, i32 1 %40 = getelementptr inbounds { i64, ptr }, ptr %literal24, i32 0, i32 1
%hi24 = load ptr, ptr %41, align 8 %hi26 = load ptr, ptr %40, align 8
%42 = call i8 @"std.collections.map$int$test.Foo$.HashMap.set"(ptr %map, i32 7, i64 %lo23, ptr %hi24) %41 = call i8 @"std.collections.map$int$test.Foo$.HashMap.set"(ptr %map, i32 7, i64 %lo25, ptr %hi26)
%43 = load ptr, ptr @std.core.mem.thread_allocator, align 8 %lo28 = load i64, 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) %hi29 = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8
store { ptr, i64 } %44, ptr %result, align 8 %42 = call { ptr, i64 } @"std.collections.map$int$test.Foo$.HashMap.value_list"(ptr %map, i64 %lo28, ptr %hi29)
%45 = insertvalue %"any*" undef, ptr %result, 0 store { ptr, i64 } %42, ptr %result, align 8
%46 = insertvalue %"any*" %45, i64 ptrtoint (ptr @"$ct.sa$test.Foo" to i64), 1 %43 = insertvalue %"any*" undef, ptr %result, 0
%47 = getelementptr inbounds [1 x %"any*"], ptr %varargslots25, i64 0, i64 0 %44 = insertvalue %"any*" %43, i64 ptrtoint (ptr @"$ct.sa$test.Foo" to i64), 1
store %"any*" %46, ptr %47, align 16 %45 = getelementptr inbounds [1 x %"any*"], ptr %varargslots27, i64 0, i64 0
%48 = call i64 @std.io.printfn(ptr %retparam26, ptr @.str.8, i64 10, ptr %varargslots25, i64 1) store %"any*" %44, ptr %45, align 16
call void @llvm.memset.p0.i64(ptr align 8 %map2, i8 0, i64 40, i1 false) %46 = call i64 @std.io.printfn(ptr %retparam30, ptr @.str.8, i64 10, ptr %varargslots27, i64 1)
%49 = load ptr, ptr @std.core.mem.thread_allocator, align 8 call void @llvm.memset.p0.i64(ptr align 8 %map2, i8 0, i64 48, i1 false)
call void @"std.collections.map$int$double$.HashMap.init"(ptr %map2, i32 16, float 7.500000e-01, ptr %49) %lo33 = load i64, ptr @std.core.mem.thread_allocator, align 8
%50 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map2, i32 4, double 1.300000e+00) %hi34 = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8
%51 = call i8 @"std.collections.map$int$double$.HashMap.has_value"(ptr %map2, double 1.300000e+00) call void @"std.collections.map$int$double$.HashMap.init"(ptr %map2, i32 16, float 7.500000e-01, i64 %lo33, ptr %hi34)
store i8 %51, ptr %taddr30, align 1 %47 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map2, i32 4, double 1.300000e+00)
%52 = insertvalue %"any*" undef, ptr %taddr30, 0 %48 = call i8 @"std.collections.map$int$double$.HashMap.has_value"(ptr %map2, double 1.300000e+00)
%53 = insertvalue %"any*" %52, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 store i8 %48, ptr %taddr36, align 1
%54 = getelementptr inbounds [1 x %"any*"], ptr %varargslots29, i64 0, i64 0 %49 = insertvalue %"any*" undef, ptr %taddr36, 0
store %"any*" %53, ptr %54, align 16 %50 = insertvalue %"any*" %49, i64 ptrtoint (ptr @"$ct.bool" to i64), 1
%55 = call i64 @std.io.printfn(ptr %retparam31, ptr @.str.9, i64 12, ptr %varargslots29, i64 1) %51 = getelementptr inbounds [1 x %"any*"], ptr %varargslots35, i64 0, i64 0
%56 = call i8 @"std.collections.map$int$double$.HashMap.has_value"(ptr %map2, double 1.200000e+00) store %"any*" %50, ptr %51, align 16
store i8 %56, ptr %taddr35, align 1 %52 = call i64 @std.io.printfn(ptr %retparam37, ptr @.str.9, i64 12, ptr %varargslots35, i64 1)
%57 = insertvalue %"any*" undef, ptr %taddr35, 0 %53 = call i8 @"std.collections.map$int$double$.HashMap.has_value"(ptr %map2, double 1.200000e+00)
%58 = insertvalue %"any*" %57, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 store i8 %53, ptr %taddr41, align 1
%59 = getelementptr inbounds [1 x %"any*"], ptr %varargslots34, i64 0, i64 0 %54 = insertvalue %"any*" undef, ptr %taddr41, 0
store %"any*" %58, ptr %59, align 16 %55 = insertvalue %"any*" %54, i64 ptrtoint (ptr @"$ct.bool" to i64), 1
%60 = call i64 @std.io.printfn(ptr %retparam36, ptr @.str.10, i64 12, ptr %varargslots34, i64 1) %56 = getelementptr inbounds [1 x %"any*"], ptr %varargslots40, i64 0, i64 0
%61 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map2, i32 100, double 3.400000e+00) store %"any*" %55, ptr %56, align 16
%62 = load ptr, ptr @std.core.mem.thread_allocator, align 8 %57 = call i64 @std.io.printfn(ptr %retparam42, ptr @.str.10, i64 12, ptr %varargslots40, i64 1)
%63 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.key_list"(ptr %map2, ptr %62) %58 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map2, i32 100, double 3.400000e+00)
store { ptr, i64 } %63, ptr %result40, align 8 %lo46 = load i64, ptr @std.core.mem.thread_allocator, align 8
%64 = insertvalue %"any*" undef, ptr %result40, 0 %hi47 = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8
%65 = insertvalue %"any*" %64, i64 ptrtoint (ptr @"$ct.sa$int" to i64), 1 %59 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.key_list"(ptr %map2, i64 %lo46, ptr %hi47)
%66 = getelementptr inbounds [1 x %"any*"], ptr %varargslots39, i64 0, i64 0 store { ptr, i64 } %59, ptr %result48, align 8
store %"any*" %65, ptr %66, align 16 %60 = insertvalue %"any*" undef, ptr %result48, 0
%67 = call i64 @std.io.printfn(ptr %retparam41, ptr @.str.11, i64 2, ptr %varargslots39, i64 1) %61 = insertvalue %"any*" %60, i64 ptrtoint (ptr @"$ct.sa$int" to i64), 1
%68 = load ptr, ptr @std.core.mem.thread_allocator, align 8 %62 = getelementptr inbounds [1 x %"any*"], ptr %varargslots45, i64 0, i64 0
%69 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.value_list"(ptr %map2, ptr %68) store %"any*" %61, ptr %62, align 16
store { ptr, i64 } %69, ptr %result45, align 8 %63 = call i64 @std.io.printfn(ptr %retparam49, ptr @.str.11, i64 2, ptr %varargslots45, i64 1)
%70 = insertvalue %"any*" undef, ptr %result45, 0 %lo53 = load i64, ptr @std.core.mem.thread_allocator, align 8
%71 = insertvalue %"any*" %70, i64 ptrtoint (ptr @"$ct.sa$double" to i64), 1 %hi54 = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8
%72 = getelementptr inbounds [1 x %"any*"], ptr %varargslots44, i64 0, i64 0 %64 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.value_list"(ptr %map2, i64 %lo53, ptr %hi54)
store %"any*" %71, ptr %72, align 16 store { ptr, i64 } %64, ptr %result55, align 8
%73 = call i64 @std.io.printfn(ptr %retparam46, ptr @.str.12, i64 2, ptr %varargslots44, i64 1) %65 = insertvalue %"any*" undef, ptr %result55, 0
%74 = load ptr, ptr @std.core.mem.thread_temp_allocator, align 8 %66 = insertvalue %"any*" %65, i64 ptrtoint (ptr @"$ct.sa$double" to i64), 1
%not = icmp eq ptr %74, null %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 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() call void @std.core.mem.init_default_temp_allocators()
br label %if.exit br label %if.exit
if.exit: ; preds = %if.then, %after_check12 if.exit: ; preds = %if.then, %after_check14
%75 = load ptr, ptr @std.core.mem.thread_temp_allocator, align 8 %70 = load ptr, ptr @std.core.mem.thread_temp_allocator, align 8
store ptr %75, ptr %current, align 8 store ptr %70, ptr %current, align 8
%76 = load ptr, ptr %current, align 8 %71 = load ptr, ptr %current, align 8
%77 = getelementptr inbounds %TempAllocator, ptr %76, i32 0, i32 3 %72 = getelementptr inbounds %TempAllocator, ptr %71, i32 0, i32 2
%78 = load i64, ptr %77, align 8 %73 = load i64, ptr %72, align 8
store i64 %78, ptr %mark, align 8 store i64 %73, ptr %mark, align 8
call void @llvm.memset.p0.i64(ptr align 8 %map3, i8 0, i64 40, i1 false) call void @llvm.memset.p0.i64(ptr align 8 %map3, i8 0, i64 48, i1 false)
%79 = load ptr, ptr @std.core.mem.thread_allocator, align 8 %lo59 = load i64, 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) %hi60 = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8
%80 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map3, i32 5, double 3.200000e+00) call void @"std.collections.map$int$double$.HashMap.init"(ptr %map3, i32 16, float 7.500000e-01, i64 %lo59, ptr %hi60)
%81 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map3, i32 7, double 5.200000e+00) %74 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map3, i32 5, double 3.200000e+00)
%82 = load ptr, ptr @std.core.mem.thread_allocator, align 8 %75 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map3, i32 7, double 5.200000e+00)
%83 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.key_list"(ptr %map3, ptr %82) %lo62 = load i64, ptr @std.core.mem.thread_allocator, align 8
store { ptr, i64 } %83, ptr %result50, align 8 %hi63 = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8
%84 = insertvalue %"any*" undef, ptr %result50, 0 %76 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.key_list"(ptr %map3, i64 %lo62, ptr %hi63)
%85 = insertvalue %"any*" %84, i64 ptrtoint (ptr @"$ct.sa$int" to i64), 1 store { ptr, i64 } %76, ptr %result64, align 8
%86 = getelementptr inbounds [1 x %"any*"], ptr %varargslots49, i64 0, i64 0 %77 = insertvalue %"any*" undef, ptr %result64, 0
store %"any*" %85, ptr %86, align 16 %78 = insertvalue %"any*" %77, i64 ptrtoint (ptr @"$ct.sa$int" to i64), 1
%87 = call i64 @std.io.printfn(ptr %retparam51, ptr @.str.13, i64 2, ptr %varargslots49, i64 1) %79 = getelementptr inbounds [1 x %"any*"], ptr %varargslots61, i64 0, i64 0
%88 = load ptr, ptr %current, align 8 store %"any*" %78, ptr %79, align 16
%89 = getelementptr inbounds %TempAllocator, ptr %88, i32 0, i32 0 %80 = call i64 @std.io.printfn(ptr %retparam65, ptr @.str.13, i64 2, ptr %varargslots61, i64 1)
%90 = load i64, ptr %mark, align 8 %81 = load ptr, ptr %current, align 8
store i64 %90, ptr %mark54, align 8 %82 = load i64, ptr %mark, align 8
%91 = getelementptr inbounds %Allocator, ptr %89, i32 0, i32 0 call void @std.core.mem.allocator.TempAllocator.reset(ptr %81, i64 %82)
%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)
ret void ret void
} }
define internal void @.static_initialize.0() { 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); assert(end.len > 0 && end.len <= ushort.max);
max_token = max(max_token, (ushort)end.len); 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 }; *self = { .allocator = using, .reader = reader, .buf = buf[:max_token], .tokens = trie };
} }
fn void! Lexer.free(&self) fn void! Lexer.free(&self)
{ {
self.allocator.free(self.buf)!; self.allocator.free(self.buf);
*self = {}; *self = {};
} }
@@ -680,20 +680,18 @@ fn void test()
/* #expect: lexer_test.ll /* #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] } %.introspect = type { i8, i64, ptr, i64, i64, i64, [0 x i64] }
%"char[]" = type { ptr, i64 } %"char[]" = type { ptr, i64 }
%"any*" = type { ptr, i64 }
%"UintTest[]" = type { ptr, i64 } %"UintTest[]" = type { ptr, i64 }
%UintTest = type { %"char[]", i64 } %UintTest = type { %"char[]", i64 }
%ByteReader = type { %Stream, %"char[]", i64 } %ByteReader = type { %Stream, %"char[]", i64 }
%Stream = type { ptr } %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 } %Trie = type { %List }
%List = type { i64, i64, ptr, ptr } %List = type { i64, i64, %"any*", ptr }
%.anon = type { %"char[]" } %.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 @"$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.KEYWORD1 = internal constant [9 x i8] c"KEYWORD1\00", align 1
@.enum.KEYWORD2 = internal constant [9 x i8] c"KEYWORD2\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.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 @.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 @"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 ; Function Attrs: nounwind
define zeroext i8 @lexer_test.is_ident_char(i64 %0, i8 zeroext %1) #0 { define zeroext i8 @lexer_test.is_ident_char(i64 %0, i8 zeroext %1) #0 {
entry: entry:
%eq = icmp eq i64 0, %0 %eq = icmp eq i64 0, %0
br i1 %eq, label %and.rhs, label %and.phi br i1 %eq, label %and.rhs, label %and.phi
and.rhs: ; preds = %entry and.rhs: ; preds = %entry
%2 = call i8 @char.is_alpha(i8 zeroext %1) %2 = call i8 @char.is_alpha(i8 zeroext %1)
%3 = trunc i8 %2 to i1 %3 = trunc i8 %2 to i1
br label %and.phi br label %and.phi
and.phi: ; preds = %and.rhs, %entry and.phi: ; preds = %and.rhs, %entry
%val = phi i1 [ false, %entry ], [ %3, %and.rhs ] %val = phi i1 [ false, %entry ], [ %3, %and.rhs ]
br i1 %val, label %or.phi, label %or.rhs br i1 %val, label %or.phi, label %or.rhs
or.rhs: ; preds = %and.phi or.rhs: ; preds = %and.phi
%lt = icmp ult i64 0, %0 %lt = icmp ult i64 0, %0
br i1 %lt, label %and.rhs1, label %and.phi2 br i1 %lt, label %and.rhs1, label %and.phi2
and.rhs1: ; preds = %or.rhs and.rhs1: ; preds = %or.rhs
%4 = call i8 @char.is_alnum(i8 zeroext %1) %4 = call i8 @char.is_alnum(i8 zeroext %1)
%5 = trunc i8 %4 to i1 %5 = trunc i8 %4 to i1
br label %and.phi2 br label %and.phi2
and.phi2: ; preds = %and.rhs1, %or.rhs and.phi2: ; preds = %and.rhs1, %or.rhs
%val3 = phi i1 [ false, %or.rhs ], [ %5, %and.rhs1 ] %val3 = phi i1 [ false, %or.rhs ], [ %5, %and.rhs1 ]
br label %or.phi br label %or.phi
or.phi: ; preds = %and.phi2, %and.phi or.phi: ; preds = %and.phi2, %and.phi
%val4 = phi i1 [ true, %and.phi ], [ %val3, %and.phi2 ] %val4 = phi i1 [ true, %and.phi ], [ %val3, %and.phi2 ]
%6 = zext i1 %val4 to i8 %6 = zext i1 %val4 to i8
ret i8 %6 ret i8 %6
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define i64 @lexer_test.lex_uint() #0 { define i64 @lexer_test.lex_uint() #0 {
entry: entry:
@@ -750,7 +756,7 @@ entry:
%lex = alloca %Lexer, align 8 %lex = alloca %Lexer, align 8
%error_var = alloca i64, align 8 %error_var = alloca i64, align 8
%kind = alloca i8, align 1 %kind = alloca i8, align 1
%error_var2 = alloca i64, align 8 %error_var4 = alloca i64, align 8
%retparam = alloca i8, align 1 %retparam = alloca i8, align 1
store %"UintTest[]" zeroinitializer, ptr %tcases, align 8 store %"UintTest[]" zeroinitializer, ptr %tcases, align 8
%0 = getelementptr inbounds %"UintTest[]", ptr %tcases, i32 0, i32 1 %0 = getelementptr inbounds %"UintTest[]", ptr %tcases, i32 0, i32 1
@@ -758,11 +764,13 @@ entry:
store i64 %1, ptr %.anon, align 8 store i64 %1, ptr %.anon, align 8
store i64 0, ptr %.anon1, align 8 store i64 0, ptr %.anon1, align 8
br label %loop.cond br label %loop.cond
loop.cond: ; preds = %noerr_block7, %entry
loop.cond: ; preds = %noerr_block9, %entry
%2 = load i64, ptr %.anon1, align 8 %2 = load i64, ptr %.anon1, align 8
%3 = load i64, ptr %.anon, align 8 %3 = load i64, ptr %.anon, align 8
%lt = icmp ult i64 %2, %3 %lt = icmp ult i64 %2, %3
br i1 %lt, label %loop.body, label %loop.exit br i1 %lt, label %loop.body, label %loop.exit
loop.body: ; preds = %loop.cond loop.body: ; preds = %loop.cond
%4 = getelementptr inbounds %"UintTest[]", ptr %tcases, i32 0, i32 0 %4 = getelementptr inbounds %"UintTest[]", ptr %tcases, i32 0, i32 0
%5 = load ptr, ptr %4, align 8 %5 = load ptr, ptr %4, align 8
@@ -770,49 +778,59 @@ loop.body: ; preds = %loop.cond
%ptroffset = getelementptr inbounds %UintTest, ptr %5, i64 %6 %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.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 %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 %7 = getelementptr inbounds %UintTest, ptr %tc, i32 0, i32 0
%8 = getelementptr inbounds %"char[]", ptr %7, i32 0, i32 0 %8 = getelementptr inbounds %"char[]", ptr %7, i32 0, i32 0
%lo = load ptr, ptr %8, align 8 %lo = load ptr, ptr %8, align 8
%9 = getelementptr inbounds %"char[]", ptr %7, i32 0, i32 1 %9 = getelementptr inbounds %"char[]", ptr %7, i32 0, i32 1
%hi = load i64, ptr %9, align 8 %hi = load i64, ptr %9, align 8
%10 = call ptr @std.io.ByteReader.init(ptr %br, ptr %lo, i64 %hi) %10 = call ptr @std.io.ByteReader.init(ptr %br, ptr %lo, i64 %hi)
%11 = load ptr, ptr @std.core.mem.thread_allocator, align 8 %lo2 = load i64, 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) %hi3 = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8
%not_err = icmp eq i64 %12, 0 %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)
%13 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) %not_err = icmp eq i64 %11, 0
br i1 %13, label %after_check, label %assign_optional %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 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 br label %guard_block
after_check: ; preds = %loop.body after_check: ; preds = %loop.body
br label %noerr_block br label %noerr_block
guard_block: ; preds = %assign_optional guard_block: ; preds = %assign_optional
%14 = load i64, ptr %error_var, align 8 %13 = load i64, ptr %error_var, align 8
ret i64 %14 ret i64 %13
noerr_block: ; preds = %after_check noerr_block: ; preds = %after_check
%15 = call i64 @"lexer$lexer_test.Token$lexer_test.Comment$.Lexer.next"(ptr %retparam, ptr %lex) %14 = call i64 @"lexer$lexer_test.Token$lexer_test.Comment$.Lexer.next"(ptr %retparam, ptr %lex)
%not_err3 = icmp eq i64 %15, 0 %not_err5 = icmp eq i64 %14, 0
%16 = call i1 @llvm.expect.i1(i1 %not_err3, i1 true) %15 = call i1 @llvm.expect.i1(i1 %not_err5, i1 true)
br i1 %16, label %after_check5, label %assign_optional4 br i1 %15, label %after_check7, label %assign_optional6
assign_optional4: ; preds = %noerr_block
store i64 %15, ptr %error_var2, align 8 assign_optional6: ; preds = %noerr_block
br label %guard_block6 store i64 %14, ptr %error_var4, align 8
after_check5: ; preds = %noerr_block br label %guard_block8
br label %noerr_block7
guard_block6: ; preds = %assign_optional4 after_check7: ; preds = %noerr_block
%17 = load i64, ptr %error_var2, align 8 br label %noerr_block9
ret i64 %17
noerr_block7: ; preds = %after_check5 guard_block8: ; preds = %assign_optional6
%18 = load i8, ptr %retparam, align 1 %16 = load i64, ptr %error_var4, align 8
store i8 %18, ptr %kind, align 1 ret i64 %16
%19 = load i8, ptr %kind, align 1
%eq = icmp eq i8 %19, 1 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) call void @llvm.assume(i1 %eq)
%20 = load i64, ptr %.anon1, align 8 %19 = load i64, ptr %.anon1, align 8
%add = add i64 %20, 1 %add = add i64 %19, 1
store i64 %add, ptr %.anon1, align 8 store i64 %add, ptr %.anon1, align 8
br label %loop.cond br label %loop.cond
loop.exit: ; preds = %loop.cond loop.exit: ; preds = %loop.cond
ret i64 0 ret i64 0
} }