Ver Fonte

Merge remote-tracking branch 'upstream/master' into prototype-fmt

Daniel Gavin há 4 anos atrás
pai
commit
f10f7ebbf1

+ 15 - 0
core/bytes/bytes.odin

@@ -16,6 +16,21 @@ ptr_from_slice :: proc(str: []byte) -> ^byte {
 	return d.data;
 }
 
+truncate_to_byte :: proc(str: []byte, b: byte) -> []byte {
+	n := index_byte(str, b);
+	if n < 0 {
+		n = len(str);
+	}
+	return str[:n];
+}
+truncate_to_rune :: proc(str: []byte, r: rune) -> []byte {
+	n := index_rune(str, r);
+	if n < 0 {
+		n = len(str);
+	}
+	return str[:n];
+}
+
 // Compares two strings, returning a value representing which one comes first lexiographically.
 // -1 for `a`; 1 for `b`, or 0 if they are equal.
 compare :: proc(lhs, rhs: []byte) -> int {

+ 0 - 3
core/fmt/fmt.odin

@@ -1638,9 +1638,6 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 		}
 
 	case runtime.Type_Info_Simd_Vector:
-		if info.is_x86_mmx {
-			io.write_string(fi.writer, "intrinsics.x86_mmx<>");
-		}
 		io.write_byte(fi.writer, '<');
 		defer io.write_byte(fi.writer, '>');
 		for i in 0..<info.count {

+ 11 - 6
core/intrinsics/intrinsics.odin

@@ -3,9 +3,6 @@
 package intrinsics
 
 // Types
-
-x86_mmx :: x86_mmx; // Specialized SIMD Vector type
-
 simd_vector :: proc($N: int, $T: typeid) -> type/#simd[N]T
 soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
 
@@ -13,8 +10,11 @@ soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
 volatile_load  :: proc(dst: ^$T) -> T ---
 volatile_store :: proc(dst: ^$T, val: T) -> T ---
 
-// Atomics
+// Trapping
+debug_trap :: proc() ---
+trap :: proc() -> ! ---
 
+// Atomics
 atomic_fence        :: proc() ---
 atomic_fence_acq    :: proc() ---
 atomic_fence_rel    :: proc() ---
@@ -89,9 +89,14 @@ atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*opti
 
 // Instructions
 
-alloca :: proc(size, align: int) -> ^u8 ---
+alloca             :: proc(size, align: int) -> ^u8 ---
+cpu_relax          :: proc() ---
+read_cycle_counter :: proc() -> i64 ---
+
+
+// Compiler Hints
+expect :: proc(val, expected_val: T) -> T ---
 
-cpu_relax :: proc() ---
 
 // Constant type tests
 

+ 94 - 13
core/mem/alloc.odin

@@ -28,11 +28,20 @@ Allocator_Query_Info :: struct {
 }
 */
 
+Allocator_Error :: runtime.Allocator_Error;
+/*
+Allocator_Error :: enum byte {
+	None             = 0,
+	Out_Of_Memory    = 1,
+	Invalid_Pointer  = 2,
+	Invalid_Argument = 3,
+}
+*/
 Allocator_Proc :: runtime.Allocator_Proc;
 /*
 Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
                              size, alignment: int,
-                             old_memory: rawptr, old_size: int, flags: u64 = 0, location := #caller_location) -> rawptr;
+                             old_memory: rawptr, old_size: int, location: Source_Code_Location = #caller_location) -> ([]byte, Allocator_Error);
 */
 
 Allocator :: runtime.Allocator;
@@ -52,23 +61,49 @@ alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := contex
 	if allocator.procedure == nil {
 		return nil;
 	}
-	return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, 0, loc);
+	data, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc);
+	_ = err;
+	return raw_data(data);
+}
+
+alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
+	if size == 0 {
+		return nil, nil;
+	}
+	if allocator.procedure == nil {
+		return nil, nil;
+	}
+	return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc);
 }
 
-free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) {
+free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
 	if ptr == nil {
-		return;
+		return nil;
 	}
 	if allocator.procedure == nil {
-		return;
+		return nil;
+	}
+	_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, loc);
+	return err;
+}
+
+free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
+	if bytes == nil {
+		return nil;
 	}
-	allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, 0, loc);
+	if allocator.procedure == nil {
+		return nil;
+	}
+	_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, raw_data(bytes), len(bytes), loc);
+	return err;
 }
 
-free_all :: proc(allocator := context.allocator, loc := #caller_location) {
+free_all :: proc(allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
 	if allocator.procedure != nil {
-		allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, 0, loc);
+		_, err := allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, loc);
+		return err;
 	}
+	return nil;
 }
 
 resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
@@ -77,18 +112,40 @@ resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_AL
 	}
 	if new_size == 0 {
 		if ptr != nil {
-			allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, 0, loc);
+			allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc);
 		}
 		return nil;
 	} else if ptr == nil {
-		return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, 0, loc);
+		_, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc);
+		_ = err;
+		return nil;
+	}
+	data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc);
+	_ = err;
+	return raw_data(data);
+}
+
+resize_bytes :: proc(old_data: []byte, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
+	if allocator.procedure == nil {
+		return nil, nil;
+	}
+	ptr := raw_data(old_data);
+	old_size := len(old_data);
+	if new_size == 0 {
+		if ptr != nil {
+			_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc);
+			return nil, err;
+		}
+		return nil, nil;
+	} else if ptr == nil {
+		return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc);
 	}
-	return allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, 0, loc);
+	return allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc);
 }
 
 query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) {
 	if allocator.procedure != nil {
-		allocator.procedure(allocator.data, Allocator_Mode.Query_Features, 0, 0, &set, 0, 0, loc);
+		allocator.procedure(allocator.data, Allocator_Mode.Query_Features, 0, 0, &set, 0, loc);
 		return set;
 	}
 	return nil;
@@ -97,7 +154,7 @@ query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: A
 query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_location) -> (props: Allocator_Query_Info) {
 	props.pointer = pointer;
 	if allocator.procedure != nil {
-		allocator.procedure(allocator.data, Allocator_Mode.Query_Info, 0, 0, &props, 0, 0, loc);
+		allocator.procedure(allocator.data, Allocator_Mode.Query_Info, 0, 0, &props, 0, loc);
 	}
 	return;
 }
@@ -218,4 +275,28 @@ default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment:
 	free(old_memory, allocator, loc);
 	return new_memory;
 }
+default_resize_bytes_align :: proc(old_data: []byte, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
+	old_memory := raw_data(old_data);
+	old_size := len(old_data);
+	if old_memory == nil {
+		return alloc_bytes(new_size, alignment, allocator, loc);
+	}
+
+	if new_size == 0 {
+		err := free_bytes(old_data, allocator, loc);
+		return nil, err;
+	}
+
+	if new_size == old_size {
+		return old_data, .None;
+	}
 
+	new_memory, err := alloc_bytes(new_size, alignment, allocator, loc);
+	if new_memory == nil || err != nil {
+		return nil, err;
+	}
+
+	runtime.copy(new_memory, old_data);
+	free_bytes(old_data, allocator, loc);
+	return new_memory, err;
+}

+ 154 - 174
core/mem/allocators.odin

@@ -5,8 +5,8 @@ import "core:runtime"
 
 nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
                            size, alignment: int,
-                           old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
-	return nil;
+                           old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
+	return nil, nil;
 }
 
 nil_allocator :: proc() -> Allocator {
@@ -47,7 +47,7 @@ arena_allocator :: proc(arena: ^Arena) -> Allocator {
 
 arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
                              size, alignment: int,
-                             old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
+                             old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error)  {
 	arena := cast(^Arena)allocator_data;
 
 	switch mode {
@@ -55,7 +55,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		total_size := size + alignment;
 
 		if arena.offset + total_size > len(arena.data) {
-			return nil;
+			return nil, .Out_Of_Memory;
 		}
 
 		#no_bounds_check end := &arena.data[arena.offset];
@@ -63,7 +63,8 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		ptr := align_forward(end, uintptr(alignment));
 		arena.offset += total_size;
 		arena.peak_used = max(arena.peak_used, arena.offset);
-		return zero(ptr, size);
+		zero(ptr, size);
+		return byte_slice(ptr, size), nil;
 
 	case .Free:
 		// NOTE(bill): Free all at once
@@ -73,20 +74,20 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		arena.offset = 0;
 
 	case .Resize:
-		return default_resize_align(old_memory, old_size, size, alignment, arena_allocator(arena));
+		return default_resize_bytes_align(byte_slice(old_memory, old_size), size, alignment, arena_allocator(arena));
 
 	case .Query_Features:
 		set := (^Allocator_Mode_Set)(old_memory);
 		if set != nil {
 			set^ = {.Alloc, .Free_All, .Resize, .Query_Features};
 		}
-		return set;
+		return nil, nil;
 
 	case .Query_Info:
-		return nil;
+		return nil, nil;
 	}
 
-	return nil;
+	return nil, nil;
 }
 
 begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
@@ -109,9 +110,9 @@ end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
 Scratch_Allocator :: struct {
 	data:               []byte,
 	curr_offset:        int,
-	prev_allocation:   rawptr,
+	prev_allocation:    rawptr,
 	backup_allocator:   Allocator,
-	leaked_allocations: [dynamic]rawptr,
+	leaked_allocations: [dynamic][]byte,
 }
 
 scratch_allocator_init :: proc(s: ^Scratch_Allocator, size: int, backup_allocator := context.allocator) {
@@ -127,7 +128,7 @@ scratch_allocator_destroy :: proc(s: ^Scratch_Allocator) {
 		return;
 	}
 	for ptr in s.leaked_allocations {
-		free(ptr, s.backup_allocator);
+		free_bytes(ptr, s.backup_allocator);
 	}
 	delete(s.leaked_allocations);
 	delete(s.data, s.backup_allocator);
@@ -136,7 +137,7 @@ scratch_allocator_destroy :: proc(s: ^Scratch_Allocator) {
 
 scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
                                size, alignment: int,
-                               old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
+                               old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
 
 	s := (^Scratch_Allocator)(allocator_data);
 
@@ -165,7 +166,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 			s.prev_allocation = rawptr(ptr);
 			offset := int(ptr - start);
 			s.curr_offset = offset + size;
-			return rawptr(ptr);
+			return byte_slice(rawptr(ptr), size), nil;
 
 		case size <= len(s.data):
 			start := uintptr(raw_data(s.data));
@@ -175,7 +176,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 			s.prev_allocation = rawptr(ptr);
 			offset := int(ptr - start);
 			s.curr_offset = offset + size;
-			return rawptr(ptr);
+			return byte_slice(rawptr(ptr), size), nil;
 		}
 		a := s.backup_allocator;
 		if a.procedure == nil {
@@ -183,9 +184,12 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 			s.backup_allocator = a;
 		}
 
-		ptr := alloc(size, alignment, a, loc);
+		ptr, err := alloc_bytes(size, alignment, a, loc);
+		if err != nil {
+			return ptr, err;
+		}
 		if s.leaked_allocations == nil {
-			s.leaked_allocations = make([dynamic]rawptr, a);
+			s.leaked_allocations = make([dynamic][]byte, a);
 		}
 		append(&s.leaked_allocations, ptr);
 
@@ -195,7 +199,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 			}
 		}
 
-		return ptr;
+		return ptr, err;
 
 	case .Free:
 		start := uintptr(raw_data(s.data));
@@ -205,30 +209,32 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		if s.prev_allocation == old_memory {
 			s.curr_offset = int(uintptr(s.prev_allocation) - start);
 			s.prev_allocation = nil;
-			return nil;
+			return nil, nil;
 		}
 
 		if start <= old_ptr && old_ptr < end {
 			// NOTE(bill): Cannot free this pointer but it is valid
-			return nil;
+			return nil, nil;
 		}
 
 		if len(s.leaked_allocations) != 0 {
-			for ptr, i in s.leaked_allocations {
+			for data, i in s.leaked_allocations {
+				ptr := raw_data(data);
 				if ptr == old_memory {
-					free(ptr, s.backup_allocator);
+					free_bytes(data, s.backup_allocator);
 					ordered_remove(&s.leaked_allocations, i);
-					return nil;
+					return nil, nil;
 				}
 			}
 		}
-		panic("invalid pointer passed to default_temp_allocator");
+		return nil, .Invalid_Pointer;
+		// panic("invalid pointer passed to default_temp_allocator");
 
 	case .Free_All:
 		s.curr_offset = 0;
 		s.prev_allocation = nil;
 		for ptr in s.leaked_allocations {
-			free(ptr, s.backup_allocator);
+			free_bytes(ptr, s.backup_allocator);
 		}
 		clear(&s.leaked_allocations);
 
@@ -238,26 +244,28 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		old_ptr := uintptr(old_memory);
 		if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end {
 			s.curr_offset = int(old_ptr-begin)+size;
-			return old_memory;
+			return byte_slice(old_memory, size), nil;
 		}
-		ptr := scratch_allocator_proc(allocator_data, .Alloc, size, alignment, old_memory, old_size, flags, loc);
-		copy(ptr, old_memory, old_size);
-		scratch_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, flags, loc);
-		return ptr;
+		data, err := scratch_allocator_proc(allocator_data, .Alloc, size, alignment, old_memory, old_size, loc);
+		if err != nil {
+			return data, err;
+		}
+		runtime.copy(data, byte_slice(old_memory, old_size));
+		_, err = scratch_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, loc);
+		return data, err;
 
 	case .Query_Features:
 		set := (^Allocator_Mode_Set)(old_memory);
 		if set != nil {
 			set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
 		}
-		return set;
+		return nil, nil;
 
 	case .Query_Info:
-		return nil;
+		return nil, nil;
 	}
 
-
-	return nil;
+	return nil, nil;
 }
 
 scratch_allocator :: proc(allocator: ^Scratch_Allocator) -> Allocator {
@@ -301,18 +309,18 @@ stack_allocator :: proc(stack: ^Stack) -> Allocator {
 
 stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
                              size, alignment: int,
-                             old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
+                             old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) {
 	s := cast(^Stack)allocator_data;
 
 	if s.data == nil {
-		return nil;
+		return nil, .Invalid_Argument;
 	}
 
-	raw_alloc :: proc(s: ^Stack, size, alignment: int) -> rawptr {
+	raw_alloc :: proc(s: ^Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
 		curr_addr := uintptr(raw_data(s.data)) + uintptr(s.curr_offset);
 		padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Stack_Allocation_Header));
 		if s.curr_offset + padding + size > len(s.data) {
-			return nil;
+			return nil, .Out_Of_Memory;
 		}
 		s.prev_offset = s.curr_offset;
 		s.curr_offset += padding;
@@ -326,7 +334,8 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 
 		s.peak_used = max(s.peak_used, s.curr_offset);
 
-		return zero(rawptr(next_addr), size);
+		zero(rawptr(next_addr), size);
+		return byte_slice(rawptr(next_addr), size), nil;
 	}
 
 	switch mode {
@@ -334,7 +343,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		return raw_alloc(s, size, alignment);
 	case .Free:
 		if old_memory == nil {
-			return nil;
+			return nil, nil;
 		}
 		start := uintptr(raw_data(s.data));
 		end := start + uintptr(len(s.data));
@@ -346,20 +355,20 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 
 		if curr_addr >= start+uintptr(s.curr_offset) {
 			// NOTE(bill): Allow double frees
-			return nil;
+			return nil, nil;
 		}
 
 		header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header));
 		old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
 
 		if old_offset != header.prev_offset {
-			panic("Out of order stack allocator free");
+			// panic("Out of order stack allocator free");
+			return nil, .Invalid_Pointer;
 		}
 
 		s.curr_offset = old_offset;
 		s.prev_offset = header.prev_offset;
 
-
 	case .Free_All:
 		s.prev_offset = 0;
 		s.curr_offset = 0;
@@ -369,7 +378,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 			return raw_alloc(s, size, alignment);
 		}
 		if size == 0 {
-			return nil;
+			return nil, nil;
 		}
 
 		start := uintptr(raw_data(s.data));
@@ -381,20 +390,22 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 
 		if curr_addr >= start+uintptr(s.curr_offset) {
 			// NOTE(bill): Allow double frees
-			return nil;
+			return nil, nil;
 		}
 
 		if old_size == size {
-			return old_memory;
+			return byte_slice(old_memory, size), nil;
 		}
 
 		header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header));
 		old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
 
 		if old_offset != header.prev_offset {
-			ptr := raw_alloc(s, size, alignment);
-			copy(ptr, old_memory, min(old_size, size));
-			return ptr;
+			data, err := raw_alloc(s, size, alignment);
+			if err == nil {
+				runtime.copy(data, byte_slice(old_memory, old_size));
+			}
+			return data, err;
 		}
 
 		old_memory_size := uintptr(s.curr_offset) - (curr_addr - start);
@@ -406,19 +417,19 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 			zero(rawptr(curr_addr + uintptr(diff)), diff);
 		}
 
-		return old_memory;
+		return byte_slice(old_memory, size), nil;
 
 	case .Query_Features:
 		set := (^Allocator_Mode_Set)(old_memory);
 		if set != nil {
 			set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
 		}
-		return set;
+		return nil, nil;
 	case .Query_Info:
-		return nil;
+		return nil, nil;
 	}
 
-	return nil;
+	return nil, nil;
 }
 
 
@@ -453,20 +464,20 @@ small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator {
 
 small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
                                    size, alignment: int,
-                                   old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
+                                   old_memory: rawptr, old_size: int, ocation := #caller_location) -> ([]byte, Allocator_Error) {
 	s := cast(^Small_Stack)allocator_data;
 
 	if s.data == nil {
-		return nil;
+		return nil, .Invalid_Argument;
 	}
 
 	align := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2);
 
-	raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> rawptr {
+	raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
 		curr_addr := uintptr(raw_data(s.data)) + uintptr(s.offset);
 		padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header));
 		if s.offset + padding + size > len(s.data) {
-			return nil;
+			return nil, .Out_Of_Memory;
 		}
 		s.offset += padding;
 
@@ -478,7 +489,8 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 
 		s.peak_used = max(s.peak_used, s.offset);
 
-		return zero(rawptr(next_addr), size);
+		zero(rawptr(next_addr), size);
+		return byte_slice(rawptr(next_addr), size), nil;
 	}
 
 	switch mode {
@@ -486,19 +498,20 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		return raw_alloc(s, size, align);
 	case .Free:
 		if old_memory == nil {
-			return nil;
+			return nil, nil;
 		}
 		start := uintptr(raw_data(s.data));
 		end := start + uintptr(len(s.data));
 		curr_addr := uintptr(old_memory);
 
 		if !(start <= curr_addr && curr_addr < end) {
-			panic("Out of bounds memory address passed to stack allocator (free)");
+			// panic("Out of bounds memory address passed to stack allocator (free)");
+			return nil, .Invalid_Pointer;
 		}
 
 		if curr_addr >= start+uintptr(s.offset) {
 			// NOTE(bill): Allow double frees
-			return nil;
+			return nil, nil;
 		}
 
 		header := (^Small_Stack_Allocation_Header)(curr_addr - size_of(Small_Stack_Allocation_Header));
@@ -514,41 +527,44 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 			return raw_alloc(s, size, align);
 		}
 		if size == 0 {
-			return nil;
+			return nil, nil;
 		}
 
 		start := uintptr(raw_data(s.data));
 		end := start + uintptr(len(s.data));
 		curr_addr := uintptr(old_memory);
 		if !(start <= curr_addr && curr_addr < end) {
-			panic("Out of bounds memory address passed to stack allocator (resize)");
+			// panic("Out of bounds memory address passed to stack allocator (resize)");
+			return nil, .Invalid_Pointer;
 		}
 
 		if curr_addr >= start+uintptr(s.offset) {
 			// NOTE(bill): Treat as a double free
-			return nil;
+			return nil, nil;
 		}
 
 		if old_size == size {
-			return old_memory;
+			return byte_slice(old_memory, size), nil;
 		}
 
-		ptr := raw_alloc(s, size, align);
-		copy(ptr, old_memory, min(old_size, size));
-		return ptr;
+		data, err := raw_alloc(s, size, align);
+		if err == nil {
+			runtime.copy(data, byte_slice(old_memory, old_size));
+		}
+		return data, err;
 
 	case .Query_Features:
 		set := (^Allocator_Mode_Set)(old_memory);
 		if set != nil {
 			set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
 		}
-		return set;
+		return nil, nil;
 
 	case .Query_Info:
-		return nil;
+		return nil, nil;
 	}
 
-	return nil;
+	return nil, nil;
 }
 
 
@@ -579,42 +595,44 @@ DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: 6554;
 
 dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
                                     size, alignment: int,
-                                    old_memory: rawptr, old_size: int,
-                                    flags: u64 = 0, loc := #caller_location) -> rawptr {
+                                    old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
 	pool := (^Dynamic_Pool)(allocator_data);
 
 	switch mode {
 	case .Alloc:
-		return dynamic_pool_alloc(pool, size);
+		return dynamic_pool_alloc_bytes(pool, size);
 	case .Free:
-		//
+		return nil, nil;
 	case .Free_All:
 		dynamic_pool_free_all(pool);
+		return nil, nil;
 	case .Resize:
 		if old_size >= size {
-			return old_memory;
+			return byte_slice(old_memory, size), nil;
+		}
+		data, err := dynamic_pool_alloc_bytes(pool, size);
+		if err == nil {
+			runtime.copy(data, byte_slice(old_memory, old_size));
 		}
-		ptr := dynamic_pool_alloc(pool, size);
-		copy(ptr, old_memory, old_size);
-		return ptr;
+		return data, err;
 
 	case .Query_Features:
 		set := (^Allocator_Mode_Set)(old_memory);
 		if set != nil {
 			set^ = {.Alloc, .Free_All, .Resize, .Query_Features, .Query_Info};
 		}
-		return set;
+		return nil, nil;
 
 	case .Query_Info:
 		info := (^Allocator_Query_Info)(old_memory);
 		if info != nil && info.pointer != nil {
 			info.size = pool.block_size;
 			info.alignment = pool.alignment;
-			return info;
+			return byte_slice(info, size_of(info^)), nil;
 		}
-		return nil;
+		return nil, nil;
 	}
-	return nil;
+	return nil, nil;
 }
 
 
@@ -649,8 +667,14 @@ dynamic_pool_destroy :: proc(using pool: ^Dynamic_Pool) {
 }
 
 
-dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
-	cycle_new_block :: proc(using pool: ^Dynamic_Pool) {
+dynamic_pool_alloc :: proc(pool: ^Dynamic_Pool, bytes: int) -> rawptr {
+	data, err := dynamic_pool_alloc_bytes(pool, bytes);
+	assert(err == nil);
+	return raw_data(data);
+}
+
+dynamic_pool_alloc_bytes :: proc(using pool: ^Dynamic_Pool, bytes: int) -> ([]byte, Allocator_Error) {
+	cycle_new_block :: proc(using pool: ^Dynamic_Pool) -> (err: Allocator_Error) {
 		if block_allocator.procedure == nil {
 			panic("You must call pool_init on a Pool before using it");
 		}
@@ -663,14 +687,17 @@ dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
 		if len(unused_blocks) > 0 {
 			new_block = pop(&unused_blocks);
 		} else {
-			new_block = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
-			                                      block_size, alignment,
-			                                      nil, 0);
+			data: []byte;
+			data, err = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
+			                                           block_size, alignment,
+			                                           nil, 0);
+			new_block = raw_data(data);
 		}
 
 		bytes_left = block_size;
 		current_pos = new_block;
 		current_block = new_block;
+		return;
 	}
 
 	n := bytes;
@@ -678,26 +705,29 @@ dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
 	n += extra;
 	if n >= out_band_size {
 		assert(block_allocator.procedure != nil);
-		memory := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
+		memory, err := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
 			                                block_size, alignment,
 			                                nil, 0);
 		if memory != nil {
-			append(&out_band_allocations, (^byte)(memory));
+			append(&out_band_allocations, raw_data(memory));
 		}
-		return memory;
+		return memory, err;
 	}
 
 	if bytes_left < n {
-		cycle_new_block(pool);
+		err := cycle_new_block(pool);
+		if err != nil {
+			return nil, err;
+		}
 		if current_block == nil {
-			return nil;
+			return nil, .Out_Of_Memory;
 		}
 	}
 
 	memory := current_pos;
 	current_pos = ptr_offset((^byte)(current_pos), n);
 	bytes_left -= n;
-	return memory;
+	return byte_slice(memory, bytes), nil;
 }
 
 
@@ -730,7 +760,7 @@ dynamic_pool_free_all :: proc(using pool: ^Dynamic_Pool) {
 
 panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
                              size, alignment: int,
-                             old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
+                             old_memory: rawptr, old_size: int,loc := #caller_location) -> ([]byte, Allocator_Error) {
 
 	switch mode {
 	case .Alloc:
@@ -753,13 +783,13 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		if set != nil {
 			set^ = {.Query_Features};
 		}
-		return set;
+		return nil, nil;
 
 	case .Query_Info:
-		return nil;
+		return nil, nil;
 	}
 
-	return nil;
+	return nil, nil;
 }
 
 panic_allocator :: proc() -> Allocator {
@@ -770,70 +800,12 @@ panic_allocator :: proc() -> Allocator {
 }
 
 
-alloca_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
-                              size, alignment: int,
-                              old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
-	switch mode {
-	case .Alloc:
-		switch alignment {
-		case:   return intrinsics.alloca(size, 2*align_of(uintptr));
-		case 0: return intrinsics.alloca(size, 0);
-
-		case 1:     return intrinsics.alloca(size, 1);
-		case 2:     return intrinsics.alloca(size, 2);
-		case 4:     return intrinsics.alloca(size, 4);
-		case 8:     return intrinsics.alloca(size, 8);
-		case 16:    return intrinsics.alloca(size, 16);
-		case 32:    return intrinsics.alloca(size, 32);
-		case 64:    return intrinsics.alloca(size, 64);
-		case 128:   return intrinsics.alloca(size, 128);
-		case 256:   return intrinsics.alloca(size, 256);
-		case 512:   return intrinsics.alloca(size, 512);
-		case 1024:  return intrinsics.alloca(size, 1024);
-		case 2048:  return intrinsics.alloca(size, 2048);
-		case 4096:  return intrinsics.alloca(size, 4096);
-		case 8192:  return intrinsics.alloca(size, 8192);
-		case 16384: return intrinsics.alloca(size, 16384);
-		case 32768: return intrinsics.alloca(size, 32768);
-		case 65536: return intrinsics.alloca(size, 65536);
-		}
-	case .Resize:
-		return default_resize_align(old_memory, old_size, size, alignment, alloca_allocator());
-
-	case .Free:
-		// Do nothing
-	case .Free_All:
-		// Do nothing
-
-	case .Query_Features:
-		set := (^Allocator_Mode_Set)(old_memory);
-		if set != nil {
-			set^ = {.Alloc, .Resize, .Query_Features};
-		}
-		return set;
-
-	case .Query_Info:
-		return nil;
-	}
-	return nil;
-}
-
-alloca_allocator :: proc() -> Allocator {
-	return Allocator{
-		procedure = alloca_allocator_proc,
-		data = nil,
-	};
-}
-
-
-
-
-
 Tracking_Allocator_Entry :: struct {
 	memory:    rawptr,
 	size:      int,
 	alignment: int,
-	location: runtime.Source_Code_Location,
+	err:       Allocator_Error,
+	location:  runtime.Source_Code_Location,
 }
 Tracking_Allocator_Bad_Free_Entry :: struct {
 	memory:   rawptr,
@@ -864,7 +836,9 @@ tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
 	};
 }
 
-tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
+tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
+                                size, alignment: int,
+                                old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
 	data := (^Tracking_Allocator)(allocator_data);
 	if mode == .Query_Info {
 		info := (^Allocator_Query_Info)(old_memory);
@@ -872,23 +846,27 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, si
 			if entry, ok := data.allocation_map[info.pointer]; ok {
 				info.size = entry.size;
 				info.alignment = entry.alignment;
-				return info;
 			}
 			info.pointer = nil;
 		}
 
-		return nil;
+		return nil, nil;
 	}
 
-	result: rawptr;
+	result: []byte;
+	err: Allocator_Error;
 	if mode == .Free && old_memory not_in data.allocation_map {
 		append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
 			memory = old_memory,
 			location = loc,
 		});
 	} else {
-		result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, flags, loc);
+		result, err = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc);
+		if err != nil {
+			return result, err;
+		}
 	}
+	result_ptr := raw_data(result);
 
 	if data.allocation_map.allocator.procedure == nil {
 		data.allocation_map.allocator = context.allocator;
@@ -896,22 +874,24 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, si
 
 	switch mode {
 	case .Alloc:
-		data.allocation_map[result] = Tracking_Allocator_Entry{
-			memory = result,
+		data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
+			memory = result_ptr,
 			size = size,
 			alignment = alignment,
+			err = err,
 			location = loc,
 		};
 	case .Free:
 		delete_key(&data.allocation_map, old_memory);
 	case .Resize:
-		if old_memory != result {
+		if old_memory != result_ptr {
 			delete_key(&data.allocation_map, old_memory);
 		}
-		data.allocation_map[result] = Tracking_Allocator_Entry{
-			memory = result,
+		data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
+			memory = result_ptr,
 			size = size,
 			alignment = alignment,
+			err = err,
 			location = loc,
 		};
 
@@ -925,13 +905,13 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, si
 		if set != nil {
 			set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features, .Query_Info};
 		}
-		return set;
+		return nil, nil;
 
 	case .Query_Info:
-		unreachable();
+		return nil, nil;
 	}
 
-	return result;
+	return result, err;
 }
 
 
@@ -1021,13 +1001,13 @@ small_allocator :: proc(s: ^$S/Small_Allocator, backing := context.allocator) ->
 
 
 		case .Query_Features:
-			return nil;
+			return nil, nil;
 
 		case .Query_Info:
-			return nil;
+			return nil, nil;
 		}
 
-		return nil;
+		return nil, nil;
 	};
 	return a;
 }

+ 6 - 3
core/mem/mem.odin

@@ -139,11 +139,14 @@ slice_ptr :: proc(ptr: ^$T, len: int) -> []T {
 	return transmute([]T)Raw_Slice{data = ptr, len = len};
 }
 
-slice_ptr_to_bytes :: proc(ptr: rawptr, len: int) -> []byte {
-	assert(len >= 0);
-	return transmute([]byte)Raw_Slice{data = ptr, len = len};
+byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
+	return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
+}
+slice_ptr_to_bytes :: proc(data: rawptr, len: int) -> []byte {
+	return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
 }
 
+
 slice_to_bytes :: proc(slice: $E/[]$T) -> []byte {
 	s := transmute(Raw_Slice)slice;
 	s.len *= size_of(T);

+ 1 - 0
core/odin/ast/ast.odin

@@ -6,6 +6,7 @@ Proc_Tag :: enum {
 	Bounds_Check,
 	No_Bounds_Check,
 	Optional_Ok,
+	Optional_Second,
 }
 Proc_Tags :: distinct bit_set[Proc_Tag; u32];
 

+ 0 - 5
core/odin/doc-format/doc_format.odin

@@ -212,11 +212,6 @@ Type_Flag_Bit_Set :: enum u32le {
 	Underlying_Type  = 4,
 }
 
-Type_Flags_SimdVector :: distinct bit_set[Type_Flag_SimdVector; u32le];
-Type_Flag_SimdVector :: enum u32le {
-	x86_mmx = 1,
-}
-
 from_array :: proc(base: ^Header_Base, a: $A/Array($T)) -> []T {
 	s: mem.Raw_Slice;
 	s.data = rawptr(uintptr(base) + uintptr(a.offset));

+ 4 - 6
core/odin/parser/parser.odin

@@ -1910,12 +1910,10 @@ parse_proc_tags :: proc(p: ^Parser) -> (tags: ast.Proc_Tags) {
 		ident := expect_token(p, .Ident);
 
 		switch ident.text {
-		case "bounds_check":
-			tags |= {.Bounds_Check};
-		case "no_bounds_check":
-			tags |= {.No_Bounds_Check};
-		case "optional_ok":
-			tags |= {.Optional_Ok};
+		case "bounds_check":    tags |= {.Bounds_Check};
+		case "no_bounds_check": tags |= {.No_Bounds_Check};
+		case "optional_ok":     tags |= {.Optional_Ok};
+		case "optional_second": tags |= {.Optional_Second};
 		case:
 		}
 	}

+ 9 - 10
core/os/os.odin

@@ -133,8 +133,7 @@ read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
 
 heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
                             size, alignment: int,
-                            old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
-
+                            old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, mem.Allocator_Error) {
 	//
 	// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
 	// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
@@ -142,7 +141,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
 	// the pointer we return to the user.
 	//
 
-	aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> rawptr {
+	aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
 		a := max(alignment, align_of(rawptr));
 		space := size + a - 1;
 
@@ -159,13 +158,13 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
 		aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a);
 		diff := int(aligned_ptr - ptr);
 		if (size + diff) > space {
-			return nil;
+			return nil, .Out_Of_Memory;
 		}
 
 		aligned_mem = rawptr(aligned_ptr);
 		mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
 
-		return aligned_mem;
+		return mem.byte_slice(aligned_mem, size), nil;
 	}
 
 	aligned_free :: proc(p: rawptr) {
@@ -174,9 +173,9 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
 		}
 	}
 
-	aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> rawptr {
+	aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, mem.Allocator_Error) {
 		if p == nil {
-			return nil;
+			return nil, nil;
 		}
 		return aligned_alloc(new_size, new_alignment, p);
 	}
@@ -202,13 +201,13 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
 		if set != nil {
 			set^ = {.Alloc, .Free, .Resize, .Query_Features};
 		}
-		return set;
+		return nil, nil;
 
 	case .Query_Info:
-		return nil;
+		return nil, nil;
 	}
 
-	return nil;
+	return nil, nil;
 }
 
 heap_allocator :: proc() -> mem.Allocator {

+ 2 - 2
core/path/filepath/path_unix.odin

@@ -34,8 +34,8 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
 	defer _unix_free(path_ptr);
 
 	path_cstr := cstring(path_ptr);
-	path = strings.clone(string(path_cstr), allocator);
-	return path, true;
+	path_str := strings.clone(string(path_cstr), allocator);
+	return path_str, true;
 }
 
 join :: proc(elems: ..string, allocator := context.allocator) -> string {

+ 4 - 8
core/reflect/types.odin

@@ -565,14 +565,10 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info) -> (n: int) {
 		n += _n(io.write_byte(w, ']'));
 
 	case Type_Info_Simd_Vector:
-		if info.is_x86_mmx {
-			n += write_string(w, "intrinsics.x86_mmx");
-		} else {
-			n += write_string(w, "#simd[");
-			n += _n(io.write_i64(w, i64(info.count)));
-			n += _n(io.write_byte(w, ']'));
-			n += write_type(w, info.elem);
-		}
+		n += write_string(w, "#simd[");
+		n += _n(io.write_i64(w, i64(info.count)));
+		n += _n(io.write_byte(w, ']'));
+		n += write_type(w, info.elem);
 
 	case Type_Info_Relative_Pointer:
 		n += write_string(w, "#relative(");

+ 9 - 6
core/runtime/core.odin

@@ -144,7 +144,6 @@ Type_Info_Simd_Vector :: struct {
 	elem:       ^Type_Info,
 	elem_size:  int,
 	count:      int,
-	is_x86_mmx: bool,
 };
 Type_Info_Relative_Pointer :: struct {
 	pointer:      ^Type_Info,
@@ -252,7 +251,6 @@ Source_Code_Location :: struct {
 
 Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location);
 
-
 // Allocation Stuff
 Allocator_Mode :: enum byte {
 	Alloc,
@@ -271,9 +269,17 @@ Allocator_Query_Info :: struct {
 	alignment: Maybe(int),
 }
 
+Allocator_Error :: enum byte {
+	None             = 0,
+	Out_Of_Memory    = 1,
+	Invalid_Pointer  = 2,
+	Invalid_Argument = 3,
+}
+
 Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
                              size, alignment: int,
-                             old_memory: rawptr, old_size: int, flags: u64 = 0, location: Source_Code_Location = #caller_location) -> rawptr;
+                             old_memory: rawptr, old_size: int,
+                             location: Source_Code_Location = #caller_location) -> ([]byte, Allocator_Error);
 Allocator :: struct {
 	procedure: Allocator_Proc,
 	data:      rawptr,
@@ -426,9 +432,6 @@ typeid_base_without_enum :: typeid_core;
 
 @(default_calling_convention = "none")
 foreign {
-	@(link_name="llvm.assume")
-	assume :: proc(cond: bool) ---;
-
 	@(link_name="llvm.debugtrap")
 	debug_trap :: proc() ---;
 

+ 44 - 40
core/runtime/core_builtin.odin

@@ -127,26 +127,30 @@ free_all :: proc{mem_free_all};
 
 
 @builtin
-delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
-	mem_free(raw_data(str), allocator, loc);
+delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
+	return mem_free(raw_data(str), allocator, loc);
 }
 @builtin
-delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
-	mem_free((^byte)(str), allocator, loc);
+delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
+	return mem_free((^byte)(str), allocator, loc);
 }
 @builtin
-delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
-	mem_free(raw_data(array), array.allocator, loc);
+delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
+	return mem_free(raw_data(array), array.allocator, loc);
 }
 @builtin
-delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
-	mem_free(raw_data(array), allocator, loc);
+delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
+	return mem_free(raw_data(array), allocator, loc);
 }
 @builtin
-delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
+delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
 	raw := transmute(Raw_Map)m;
-	delete_slice(raw.hashes, raw.entries.allocator, loc);
-	mem_free(raw.entries.data, raw.entries.allocator, loc);
+	err := delete_slice(raw.hashes, raw.entries.allocator, loc);
+	err1 := mem_free(raw.entries.data, raw.entries.allocator, loc);
+	if err == nil {
+		err = err1;
+	}
+	return err;
 }
 
 
@@ -163,57 +167,57 @@ delete :: proc{
 // The new built-in procedure allocates memory. The first argument is a type, not a value, and the value
 // return is a pointer to a newly allocated value of that type using the specified allocator, default is context.allocator
 @builtin
-new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
-	ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
-	if ptr != nil { ptr^ = T{}; }
-	return ptr;
+new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> (^T, Allocator_Error) #optional_second {
+	ptr, err := mem_alloc(size_of(T), align_of(T), allocator, loc);
+	return (^T)(ptr), err;
 }
 
 @builtin
-new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
-	ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
-	if ptr != nil { ptr^ = data; }
-	return ptr;
+new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (^T, Allocator_Error) #optional_second {
+	ptr, err := mem_alloc(size_of(T), align_of(T), allocator, loc);
+	res := (^T)(ptr);
+	if ptr != nil && err != .Out_Of_Memory {
+		res^ = data;
+	}
+	return res, err;
 }
 
 DEFAULT_RESERVE_CAPACITY :: 16;
 
-make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T {
+make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
 	make_slice_error_loc(loc, len);
-	data := mem_alloc(size_of(E)*len, alignment, allocator, loc);
+	data, err := mem_alloc_bytes(size_of(E)*len, alignment, allocator, loc);
 	if data == nil && size_of(E) != 0 {
-		return nil;
+		return nil, err;
 	}
-	// mem_zero(data, size_of(E)*len);
-	s := Raw_Slice{data, len};
-	return transmute(T)s;
+	s := Raw_Slice{raw_data(data), len};
+	return transmute(T)s, err;
 }
 
 @builtin
-make_slice :: proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
+make_slice :: proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
 	return make_aligned(T, len, align_of(E), allocator, loc);
 }
 
 @builtin
-make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T {
+make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
 	return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc);
 }
 
 @builtin
-make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
+make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
 	return make_dynamic_array_len_cap(T, len, len, allocator, loc);
 }
 
 @builtin
-make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> T {
+make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
 	make_dynamic_array_error_loc(loc, len, cap);
-	data := mem_alloc(size_of(E)*cap, align_of(E), allocator, loc);
+	data, err := mem_alloc(size_of(E)*cap, align_of(E), allocator, loc);
 	s := Raw_Dynamic_Array{data, len, cap, allocator};
 	if data == nil && size_of(E) != 0 {
 		s.len, s.cap = 0, 0;
 	}
-	// mem_zero(data, size_of(E)*cap);
-	return transmute(T)s;
+	return transmute(T)s, err;
 }
 
 @builtin
@@ -449,15 +453,15 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
 	new_size  := capacity * size_of(E);
 	allocator := a.allocator;
 
-	new_data := allocator.procedure(
+	new_data, err := allocator.procedure(
 		allocator.data, .Resize, new_size, align_of(E),
-		a.data, old_size, 0, loc,
+		a.data, old_size, loc,
 	);
-	if new_data == nil {
+	if new_data == nil || err != nil {
 		return false;
 	}
 
-	a.data = new_data;
+	a.data = raw_data(new_data);
 	a.cap = capacity;
 	return true;
 }
@@ -483,15 +487,15 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
 	new_size  := length * size_of(E);
 	allocator := a.allocator;
 
-	new_data := allocator.procedure(
+	new_data, err := allocator.procedure(
 		allocator.data, .Resize, new_size, align_of(E),
-		a.data, old_size, 0, loc,
+		a.data, old_size, loc,
 	);
-	if new_data == nil {
+	if new_data == nil || err != nil {
 		return false;
 	}
 
-	a.data = new_data;
+	a.data = raw_data(new_data);
 	a.len = length;
 	a.cap = length;
 	return true;

+ 13 - 10
core/runtime/core_builtin_soa.odin

@@ -74,7 +74,7 @@ raw_soa_footer :: proc{
 
 
 @builtin
-make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
+make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_second {
 	if length <= 0 {
 		return;
 	}
@@ -106,13 +106,15 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
 	}
 	assert(allocator.procedure != nil);
 
-	new_data := allocator.procedure(
+	new_bytes: []byte;
+	new_bytes, err = allocator.procedure(
 		allocator.data, .Alloc, total_size, max_align,
-		nil, 0, 0, loc,
+		nil, 0, loc,
 	);
-	if new_data == nil {
+	if new_bytes == nil || err != nil {
 		return;
 	}
+	new_data := raw_data(new_bytes);
 
 	data := uintptr(&array);
 	offset := 0;
@@ -131,7 +133,7 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
 }
 
 @builtin
-make_soa_slice :: proc($T: typeid/#soa[]$E, length: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
+make_soa_slice :: proc($T: typeid/#soa[]$E, length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_second {
 	return make_soa_aligned(T, length, align_of(E), allocator, loc);
 }
 
@@ -226,13 +228,14 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
 
 	old_data := (^rawptr)(array)^;
 
-	new_data := array.allocator.procedure(
+	new_bytes, err := array.allocator.procedure(
 		array.allocator.data, .Alloc, new_size, max_align,
-		nil, old_size, 0, loc,
+		nil, old_size, loc,
 	);
-	if new_data == nil {
+	if new_bytes == nil || err != nil {
 		return false;
 	}
+	new_data := raw_data(new_bytes);
 
 
 	footer.cap = capacity;
@@ -256,9 +259,9 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
 		new_offset += type.size * capacity;
 	}
 
-	array.allocator.procedure(
+	_, err = array.allocator.procedure(
 		array.allocator.data, .Free, 0, max_align,
-		old_data, old_size, 0, loc,
+		old_data, old_size, loc,
 	);
 
 	return true;

+ 39 - 27
core/runtime/default_allocators.odin

@@ -5,8 +5,8 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR || ODIN_OS == "freestanding" {
 
 	default_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
 	                               size, alignment: int,
-	                               old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
-		return nil;
+	                               old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
+		return nil, .None;
 	}
 
 	default_allocator :: proc() -> Allocator {
@@ -26,6 +26,11 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR || ODIN_OS == "freestanding" {
 	}
 }
 
+@(private)
+byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
+	return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
+}
+
 
 DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 1<<22);
 
@@ -35,7 +40,7 @@ Default_Temp_Allocator :: struct {
 	curr_offset:        int,
 	prev_allocation:    rawptr,
 	backup_allocator:   Allocator,
-	leaked_allocations: [dynamic]rawptr,
+	leaked_allocations: [dynamic][]byte,
 }
 
 default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {
@@ -51,7 +56,7 @@ default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
 		return;
 	}
 	for ptr in s.leaked_allocations {
-		free(ptr, s.backup_allocator);
+		free(raw_data(ptr), s.backup_allocator);
 	}
 	delete(s.leaked_allocations);
 	delete(s.data, s.backup_allocator);
@@ -60,7 +65,7 @@ default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
 
 default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
                                     size, alignment: int,
-                                    old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
+                                    old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
 
 	s := (^Default_Temp_Allocator)(allocator_data);
 
@@ -84,7 +89,7 @@ default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
 			s.prev_allocation = rawptr(ptr);
 			offset := int(ptr - start);
 			s.curr_offset = offset + size;
-			return rawptr(ptr);
+			return byte_slice(rawptr(ptr), size), .None;
 
 		case size <= len(s.data):
 			start := uintptr(raw_data(s.data));
@@ -94,7 +99,7 @@ default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
 			s.prev_allocation = rawptr(ptr);
 			offset := int(ptr - start);
 			s.curr_offset = offset + size;
-			return rawptr(ptr);
+			return byte_slice(rawptr(ptr), size), .None;
 		}
 		a := s.backup_allocator;
 		if a.procedure == nil {
@@ -102,11 +107,14 @@ default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
 			s.backup_allocator = a;
 		}
 
-		ptr := mem_alloc(size, alignment, a, loc);
+		data, err := mem_alloc_bytes(size, alignment, a, loc);
+		if err != nil {
+			return data, err;
+		}
 		if s.leaked_allocations == nil {
-			s.leaked_allocations = make([dynamic]rawptr, a);
+			s.leaked_allocations = make([dynamic][]byte, a);
 		}
-		append(&s.leaked_allocations, ptr);
+		append(&s.leaked_allocations, data);
 
 		// TODO(bill): Should leaks be notified about?
 		if logger := context.logger; logger.lowest_level <= .Warning {
@@ -115,11 +123,11 @@ default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
 			}
 		}
 
-		return ptr;
+		return data, .None;
 
 	case .Free:
 		if old_memory == nil {
-			return nil;
+			return nil, .None;
 		}
 
 		start := uintptr(raw_data(s.data));
@@ -129,30 +137,32 @@ default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
 		if s.prev_allocation == old_memory {
 			s.curr_offset = int(uintptr(s.prev_allocation) - start);
 			s.prev_allocation = nil;
-			return nil;
+			return nil, .None;
 		}
 
 		if start <= old_ptr && old_ptr < end {
 			// NOTE(bill): Cannot free this pointer but it is valid
-			return nil;
+			return nil, .None;
 		}
 
 		if len(s.leaked_allocations) != 0 {
-			for ptr, i in s.leaked_allocations {
+			for data, i in s.leaked_allocations {
+				ptr := raw_data(data);
 				if ptr == old_memory {
 					free(ptr, s.backup_allocator);
 					ordered_remove(&s.leaked_allocations, i);
-					return nil;
+					return nil, .None;
 				}
 			}
 		}
-		panic("invalid pointer passed to default_temp_allocator");
+		return nil, .Invalid_Pointer;
+		// panic("invalid pointer passed to default_temp_allocator");
 
 	case .Free_All:
 		s.curr_offset = 0;
 		s.prev_allocation = nil;
-		for ptr in s.leaked_allocations {
-			free(ptr, s.backup_allocator);
+		for data in s.leaked_allocations {
+			free(raw_data(data), s.backup_allocator);
 		}
 		clear(&s.leaked_allocations);
 
@@ -163,26 +173,28 @@ default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
 		if old_memory == s.prev_allocation && old_ptr & uintptr(alignment)-1 == 0 {
 			if old_ptr+uintptr(size) < end {
 				s.curr_offset = int(old_ptr-begin)+size;
-				return old_memory;
+				return byte_slice(old_memory, size), .None;
 			}
 		}
-		ptr := default_temp_allocator_proc(allocator_data, .Alloc, size, alignment, old_memory, old_size, flags, loc);
-		mem_copy(ptr, old_memory, old_size);
-		default_temp_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, flags, loc);
-		return ptr;
+		ptr, err := default_temp_allocator_proc(allocator_data, .Alloc, size, alignment, old_memory, old_size, loc);
+		if err == .None {
+			copy(ptr, byte_slice(old_memory, old_size));
+			_, err = default_temp_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, loc);
+		}
+		return ptr, err;
 
 	case .Query_Features:
 		set := (^Allocator_Mode_Set)(old_memory);
 		if set != nil {
 			set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
 		}
-		return set;
+		return nil, nil;
 
 	case .Query_Info:
-		return nil;
+		return nil, .None;
 	}
 
-	return nil;
+	return nil, .None;
 }
 
 default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator {

+ 6 - 3
core/runtime/dynamic_array_internal.odin

@@ -29,10 +29,13 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap:
 	new_size  := cap * elem_size;
 	allocator := array.allocator;
 
-	new_data := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, 0, loc);
+	new_data, err := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, loc);
+	if err != nil {
+		return false;
+	}
 	if new_data != nil || elem_size == 0 {
-		array.data = new_data;
-		array.cap = cap;
+		array.data = raw_data(new_data);
+		array.cap = min(cap, len(new_data)/elem_size);
 		return true;
 	}
 	return false;

+ 2 - 2
core/runtime/dynamic_map_internal.odin

@@ -173,8 +173,8 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l
 	old_size := array.len*size_of(T);
 	new_size := new_count*size_of(T);
 
-	new_data := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc);
-	if new_data == nil {
+	new_data, err := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc);
+	if new_data == nil || err != nil {
 		return false;
 	}
 	array.data = new_data;

+ 32 - 16
core/runtime/internal.odin

@@ -159,43 +159,59 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r
 
 DEFAULT_ALIGNMENT :: 2*align_of(rawptr);
 
-mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
+mem_alloc_bytes :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
 	if size == 0 {
-		return nil;
+		return nil, nil;
 	}
 	if allocator.procedure == nil {
-		return nil;
+		return nil, nil;
 	}
-	return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, 0, loc);
+	return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc);
 }
 
-mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) {
+mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (rawptr, Allocator_Error) {
+	if size == 0 {
+		return nil, nil;
+	}
+	if allocator.procedure == nil {
+		return nil, nil;
+	}
+	data, err := allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc);
+	return raw_data(data), err;
+}
+
+mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
 	if ptr == nil {
-		return;
+		return .None;
 	}
 	if allocator.procedure == nil {
-		return;
+		return .None;
 	}
-	allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, 0, loc);
+	_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, loc);
+	return err;
 }
 
-mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #caller_location) {
+mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) {
 	if allocator.procedure != nil {
-		allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, 0, loc);
+		_, err = allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, loc);
 	}
+	return;
 }
 
-mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
+mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (new_ptr: rawptr, err: Allocator_Error) {
+	new_data: []byte;
 	switch {
 	case allocator.procedure == nil:
-		return nil;
+		return;
 	case new_size == 0:
-		allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, 0, loc);
-		return nil;
+		new_data, err = allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, loc);
 	case ptr == nil:
-		return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, 0, loc);
+		new_data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc);
+	case:
+		new_data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc);
 	}
-	return allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, 0, loc);
+	new_ptr = raw_data(new_data);
+	return;
 }
 memory_equal :: proc "contextless" (a, b: rawptr, n: int) -> bool {
 	return memory_compare(a, b, n) == 0;

+ 9 - 9
core/runtime/os_specific_windows.odin

@@ -88,7 +88,7 @@ heap_free :: proc "contextless" (ptr: rawptr) {
 
 default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
                                size, alignment: int,
-                               old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
+                               old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
 
 	//
 	// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
@@ -97,7 +97,7 @@ default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 	// the pointer we return to the user.
 	//
 
-	aligned_alloc :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil) -> rawptr {
+	aligned_alloc :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, Allocator_Error) {
 		a := max(alignment, align_of(rawptr));
 		space := size + a - 1;
 
@@ -114,13 +114,13 @@ default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a);
 		diff := int(aligned_ptr - ptr);
 		if (size + diff) > space {
-			return nil;
+			return nil, .Out_Of_Memory;
 		}
 
 		aligned_mem = rawptr(aligned_ptr);
 		ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
 
-		return aligned_mem;
+		return byte_slice(aligned_mem, size), nil;
 	}
 
 	aligned_free :: proc "contextless" (p: rawptr) {
@@ -129,9 +129,9 @@ default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		}
 	}
 
-	aligned_resize :: proc "contextless" (p: rawptr, old_size: int, new_size: int, new_alignment: int) -> rawptr {
+	aligned_resize :: proc "contextless" (p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, Allocator_Error) {
 		if p == nil {
-			return nil;
+			return nil, nil;
 		}
 		return aligned_alloc(new_size, new_alignment, p);
 	}
@@ -157,13 +157,13 @@ default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		if set != nil {
 			set^ = {.Alloc, .Free, .Resize, .Query_Features};
 		}
-		return set;
+		return nil, nil;
 
 	case .Query_Info:
-		return nil;
+		return nil, nil;
 	}
 
-	return nil;
+	return nil, nil;
 }
 
 default_allocator :: proc() -> Allocator {

+ 4 - 8
core/runtime/print.odin

@@ -347,14 +347,10 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
 
 
 	case Type_Info_Simd_Vector:
-		if info.is_x86_mmx {
-			print_string("intrinsics.x86_mmx");
-		} else {
-			print_string("#simd[");
-			print_u64(u64(info.count));
-			print_byte(']');
-			print_type(info.elem);
-		}
+		print_string("#simd[");
+		print_u64(u64(info.count));
+		print_byte(']');
+		print_type(info.elem);
 
 	case Type_Info_Relative_Pointer:
 		print_string("#relative(");

+ 15 - 0
core/strings/strings.odin

@@ -33,6 +33,21 @@ unsafe_string_to_cstring :: proc(str: string) -> cstring {
 	return cstring(d.data);
 }
 
+truncate_to_byte :: proc(str: string, b: byte) -> string {
+	n := index_byte(str, b);
+	if n < 0 {
+		n = len(str);
+	}
+	return str[:n];
+}
+truncate_to_rune :: proc(str: string, r: rune) -> string {
+	n := index_rune(str, r);
+	if n < 0 {
+		n = len(str);
+	}
+	return str[:n];
+}
+
 // Compares two strings, returning a value representing which one comes first lexiographically.
 // -1 for `a`; 1 for `b`, or 0 if they are equal.
 compare :: proc(lhs, rhs: string) -> int {

+ 1 - 0
core/sync/sync2/extended.odin

@@ -228,6 +228,7 @@ once_do :: proc(o: ^Once, fn: proc()) {
 	}
 }
 
+@(cold)
 _once_do_slow :: proc(o: ^Once, fn: proc()) {
 	mutex_lock(&o.m);
 	defer mutex_unlock(&o.m);

+ 3 - 2
src/build_settings.cpp

@@ -104,6 +104,7 @@ enum BuildModeKind {
 	BuildMode_DynamicLibrary,
 	BuildMode_Object,
 	BuildMode_Assembly,
+	BuildMode_LLVM_IR,
 };
 
 enum CommandKind : u32 {
@@ -113,7 +114,7 @@ enum CommandKind : u32 {
 	Command_query   = 1<<4,
 	Command_doc     = 1<<5,
 	Command_version = 1<<6,
-	Command_test     = 1<<7,
+	Command_test    = 1<<7,
 
 	Command__does_check = Command_run|Command_build|Command_check|Command_query|Command_doc|Command_test,
 	Command__does_build = Command_run|Command_build|Command_test,
@@ -838,7 +839,7 @@ void init_build_context(TargetMetrics *cross_target) {
 			bc->link_flags = str_lit("-arch arm64 ");
 			break;
 		}
-		if (!bc->use_llvm_api) {
+		if ((bc->command_kind & Command__does_build) != 0 && !bc->use_llvm_api) {
 			gb_printf_err("The arm64 architecture is only supported with -llvm-api\n");;
 			gb_exit(1);
 		}

+ 39 - 15
src/check_decl.cpp

@@ -343,17 +343,31 @@ void override_entity_in_scope(Entity *original_entity, Entity *new_entity) {
 		return;
 	}
 
-	// IMPORTANT TODO(bill)
-	// Date: 2018-09-29
-	// This assert fails on `using import` if the name of the alias is the same. What should be the expected behaviour?
-	// Namespace collision or override? Overridding is the current behaviour
+	// IMPORTANT NOTE(bill, 2021-04-10): Overriding behaviour was flawed in that the
+	// original entity was still used check checked, but the checking was only
+	// relying on "constant" data such as the Entity.type and Entity.Constant.value
 	//
-	//     using import "foo"
-	//     bar :: foo.bar;
-
-	// GB_ASSERT_MSG(found_entity == original_entity, "%.*s == %.*s", LIT(found_entity->token.string), LIT(new_entity->token.string));
+	// Therefore two things can be done: the type can be assigned to state that it
+	// has been "evaluated" and the variant data can be copied across
 
 	string_map_set(&found_scope->elements, original_name, new_entity);
+
+	original_entity->type = new_entity->type;
+
+	if (original_entity->identifier == nullptr) {
+		original_entity->identifier = new_entity->identifier;
+	}
+	if (original_entity->identifier != nullptr &&
+	    original_entity->identifier->kind == Ast_Ident) {
+		original_entity->identifier->Ident.entity = new_entity;
+	}
+	original_entity->flags |= EntityFlag_Overridden;
+
+	// IMPORTANT NOTE(bill, 2021-04-10): copy only the variants
+	// This is most likely NEVER required, but it does not at all hurt to keep
+	isize offset = cast(u8 *)&original_entity->Dummy.start - cast(u8 *)original_entity;
+	isize size = gb_size_of(*original_entity) - offset;
+	gb_memmove(cast(u8 *)original_entity, cast(u8 *)new_entity, size);
 }
 
 
@@ -374,6 +388,7 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
 
 	Operand operand = {};
 
+	Entity *other_entity = nullptr;
 	if (init != nullptr) {
 		Entity *entity = nullptr;
 		if (init->kind == Ast_Ident) {
@@ -412,7 +427,6 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
 			GB_ASSERT(operand.proc_group->kind == Entity_ProcGroup);
 			// NOTE(bill, 2020-06-10): It is better to just clone the contents than overriding the entity in the scope
 			// Thank goodness I made entities a tagged union to allow for this implace patching
-			// override_entity_in_scope(e, operand.proc_group);
 			e->kind = Entity_ProcGroup;
 			e->ProcGroup.entities = array_clone(heap_allocator(), operand.proc_group->ProcGroup.entities);
 			return;
@@ -454,7 +468,6 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
 							error(decl->attributes[0], "Constant alias declarations cannot have attributes");
 						}
 					}
-
 					return;
 				}
 			}
@@ -694,6 +707,18 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 		e->flags |= EntityFlag_Cold;
 	}
 
+	e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode;
+
+
+	switch (e->Procedure.optimization_mode) {
+	case ProcedureOptimizationMode_None:
+	case ProcedureOptimizationMode_Minimal:
+		if (pl->inlining == ProcInlining_inline) {
+			error(e->token, "#force_inline cannot be used in conjunction with the attribute 'optimization_mode' with neither \"none\" nor \"minimal\"");
+		}
+		break;
+	}
+
 	e->Procedure.is_export = ac.is_export;
 	e->deprecated_message = ac.deprecated_message;
 	ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
@@ -718,11 +743,10 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 			error(e->token, "Procedure type of 'main' was expected to be 'proc()', got %s", str);
 			gb_string_free(str);
 		}
-		if (pt->calling_convention != ProcCC_Odin &&
-		    pt->calling_convention != ProcCC_Contextless) {
+		if (pt->calling_convention != ProcCC_Odin) {
 			error(e->token, "Procedure 'main' cannot have a custom calling convention");
 		}
-		pt->calling_convention = ProcCC_Contextless;
+		pt->calling_convention = ProcCC_Odin;
 		if (e->pkg->kind == Package_Init) {
 			if (ctx->info->entry_point != nullptr) {
 				error(e->token, "Redeclaration of the entry pointer procedure 'main'");
@@ -846,7 +870,7 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 	}
 }
 
-void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_expr) {
+void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, Ast *init_expr) {
 	GB_ASSERT(e->type == nullptr);
 	GB_ASSERT(e->kind == Entity_Variable);
 
@@ -946,7 +970,7 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast *type_expr,
 	check_init_variable(ctx, e, &o, str_lit("variable declaration"));
 }
 
-void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) {
+void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) {
 	GB_ASSERT(pg_entity->kind == Entity_ProcGroup);
 	auto *pge = &pg_entity->ProcGroup;
 	String proc_group_name = pg_entity->token.string;

+ 376 - 114
src/check_expr.cpp

@@ -79,15 +79,12 @@ void     check_expr_with_type_hint      (CheckerContext *c, Operand *o, Ast *e,
 Type *   check_type                     (CheckerContext *c, Ast *expression);
 Type *   check_type_expr                (CheckerContext *c, Ast *expression, Type *named_type);
 Type *   make_optional_ok_type          (Type *value, bool typed=true);
-void     check_type_decl                (CheckerContext *c, Entity *e, Ast *type_expr, Type *def);
 Entity * check_selector                 (CheckerContext *c, Operand *operand, Ast *node, Type *type_hint);
 Entity * check_ident                    (CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name);
 Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure);
 void     check_not_tuple                (CheckerContext *c, Operand *operand);
 void     convert_to_typed               (CheckerContext *c, Operand *operand, Type *target_type);
 gbString expr_to_string                 (Ast *expression);
-void     check_entity_decl              (CheckerContext *c, Entity *e, DeclInfo *decl, Type *named_type);
-void     check_const_decl               (CheckerContext *c, Entity *e, Ast *type_expr, Ast *init_expr, Type *named_type);
 void     check_proc_body                (CheckerContext *c, Token token, DeclInfo *decl, Type *type, Ast *body);
 void     update_expr_type               (CheckerContext *c, Ast *e, Type *type, bool final);
 bool     check_is_terminating           (Ast *node, String const &label);
@@ -654,13 +651,27 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
 	}
 
 	Ast *expr = unparen_expr(operand->expr);
-	if (expr != nullptr && expr->kind == Ast_AutoCast) {
-		Operand x = *operand;
-		x.expr = expr->AutoCast.expr;
-		bool ok = check_cast_internal(c, &x, type);
-		if (ok) {
-			return MAXIMUM_TYPE_DISTANCE;
-		}
+	if (expr != nullptr) {
+		if (expr->kind == Ast_AutoCast) {
+			Operand x = *operand;
+			x.expr = expr->AutoCast.expr;
+			bool ok = check_cast_internal(c, &x, type);
+			if (ok) {
+				return MAXIMUM_TYPE_DISTANCE;
+			}
+		} /*else if (expr->kind == Ast_CallExpr) {
+			// NOTE(bill, 2021-04-19): Allow assignment of procedure calls with #optional_ok
+			ast_node(ce, CallExpr, expr);
+			Type *pt = base_type(type_of_expr(ce->proc));
+			if (pt->kind == Type_Proc && pt->Proc.optional_ok) {
+				Operand x = *operand;
+				x.type = pt->Proc.results->Tuple.variables[0]->type;
+				i64 res = check_distance_between_types(c, &x, type);
+				if (res >= 0) {
+					return res+1;
+				}
+			}
+		}*/
 	}
 
 	return -1;
@@ -774,6 +785,8 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co
 			      LIT(context_name));
 			operand->mode = Addressing_Invalid;
 		}
+
+
 		return;
 	}
 
@@ -1714,12 +1727,14 @@ void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
 }
 
 
-void check_is_expressible(CheckerContext *c, Operand *o, Type *type) {
+void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
 	GB_ASSERT(o->mode == Addressing_Constant);
-	if (!is_type_constant_type(type) || !check_representable_as_constant(c, o->value, type, &o->value)) {
+	if (!is_type_constant_type(type) || !check_representable_as_constant(ctx, o->value, type, &o->value)) {
 		gbString a = expr_to_string(o->expr);
 		gbString b = type_to_string(type);
+		gbString c = type_to_string(o->type);
 		defer(
+			gb_string_free(c);
 			gb_string_free(b);
 			gb_string_free(a);
 			o->mode = Addressing_Invalid;
@@ -1729,12 +1744,12 @@ void check_is_expressible(CheckerContext *c, Operand *o, Type *type) {
 			if (!is_type_integer(o->type) && is_type_integer(type)) {
 				error(o->expr, "'%s' truncated to '%s'", a, b);
 			} else {
-				error(o->expr, "Cannot convert '%s' to '%s'", a, b);
-				check_assignment_error_suggestion(c, o, type);
+				error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c);
+				check_assignment_error_suggestion(ctx, o, type);
 			}
 		} else {
-			error(o->expr, "Cannot convert '%s' to '%s'", a, b);
-			check_assignment_error_suggestion(c, o, type);
+			error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c);
+			check_assignment_error_suggestion(ctx, o, type);
 		}
 	}
 }
@@ -2224,6 +2239,26 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
 		return true;
 	}
 
+	// if (is_type_tuple(src)) {
+	// 	Ast *expr = unparen_expr(operand->expr);
+	// 	if (expr && expr->kind == Ast_CallExpr) {
+	// 		// NOTE(bill, 2021-04-19): Allow casting procedure calls with #optional_ok
+	// 		ast_node(ce, CallExpr, expr);
+	// 		Type *pt = base_type(type_of_expr(ce->proc));
+	// 		if (pt->kind == Type_Proc && pt->Proc.optional_ok) {
+	// 			if (pt->Proc.result_count > 0) {
+	// 				Operand op = *operand;
+	// 				op.type = pt->Proc.results->Tuple.variables[0]->type;
+	// 				bool ok = check_is_castable_to(c, &op, y);
+	// 				if (ok) {
+	// 					ce->optional_ok_one = true;
+	// 				}
+	// 				return ok;
+	// 			}
+	// 		}
+	// 	}
+	// }
+
 	if (is_constant && is_type_untyped(src) && is_type_string(src)) {
 		if (is_type_u8_array(dst)) {
 			String s = operand->value.value_string;
@@ -2339,6 +2374,7 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
 	if (is_type_rawptr(src) && is_type_proc(dst)) {
 		return true;
 	}
+
 	return false;
 }
 
@@ -2728,31 +2764,27 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
 		ExactValue a = x->value;
 		ExactValue b = y->value;
 
-		// Type *type = base_type(x->type);
-		Type *type = x->type;
-		if (is_type_pointer(type)) {
-			GB_ASSERT(op.kind == Token_Sub);
-			i64 bytes = a.value_pointer - b.value_pointer;
-			i64 diff = bytes/type_size_of(type);
-			x->value = exact_value_pointer(diff);
-			return;
-		}
-
-		if (!is_type_constant_type(type)) {
+		if (!is_type_constant_type(x->type)) {
+		#if 0
 			gbString xt = type_to_string(x->type);
 			gbString err_str = expr_to_string(node);
 			error(op, "Invalid type, '%s', for constant binary expression '%s'", xt, err_str);
 			gb_string_free(err_str);
 			gb_string_free(xt);
 			x->mode = Addressing_Invalid;
+		#else
+			// NOTE(bill, 2021-04-21): The above is literally a useless error message.
+			// Why did I add it in the first place?!
+			x->mode = Addressing_Value;
+		#endif
 			return;
 		}
 
-		if (op.kind == Token_Quo && is_type_integer(type)) {
+		if (op.kind == Token_Quo && is_type_integer(x->type)) {
 			op.kind = Token_QuoEq; // NOTE(bill): Hack to get division of integers
 		}
 
-		if (is_type_bit_set(type)) {
+		if (is_type_bit_set(x->type)) {
 			switch (op.kind) {
 			case Token_Add: op.kind = Token_Or;     break;
 			case Token_Sub: op.kind = Token_AndNot; break;
@@ -2761,11 +2793,11 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
 
 		x->value = exact_binary_operator_value(op.kind, a, b);
 
-		if (is_type_typed(type)) {
+		if (is_type_typed(x->type)) {
 			if (node != nullptr) {
 				x->expr = node;
 			}
-			check_is_expressible(c, x, type);
+			check_is_expressible(c, x, x->type);
 		}
 		return;
 	} else if (is_type_string(x->type)) {
@@ -2797,8 +2829,14 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
 
 
 void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) {
+	GB_ASSERT(e != nullptr);
 	ExprInfo *found = check_get_expr_info(&c->checker->info, e);
 	if (found == nullptr) {
+		if (type != nullptr && type != t_invalid) {
+			if (e->tav.type == nullptr || e->tav.type == t_invalid) {
+				add_type_and_value(&c->checker->info, e, e->tav.mode, type ? type : e->tav.type, e->tav.value);
+			}
+		}
 		return;
 	}
 	ExprInfo old = *found;
@@ -2865,6 +2903,7 @@ void update_expr_value(CheckerContext *c, Ast *e, ExactValue value) {
 void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_type) {
 	gbString expr_str = expr_to_string(operand->expr);
 	gbString type_str = type_to_string(target_type);
+	gbString from_type_str = type_to_string(operand->type);
 	char const *extra_text = "";
 
 	if (operand->mode == Addressing_Constant) {
@@ -2875,8 +2914,9 @@ void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_typ
 			}
 		}
 	}
-	error(operand->expr, "Cannot convert '%s' to '%s'%s", expr_str, type_str, extra_text);
+	error(operand->expr, "Cannot convert '%s' to '%s' from '%s'%s", expr_str, type_str, from_type_str, extra_text);
 
+	gb_string_free(from_type_str);
 	gb_string_free(type_str);
 	gb_string_free(expr_str);
 	operand->mode = Addressing_Invalid;
@@ -5717,8 +5757,131 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		operand->mode = Addressing_NoValue;
 		break;
 
+	case BuiltinProc_trap:
+	case BuiltinProc_debug_trap:
+		if (!build_context.use_llvm_api) {
+			error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
+		}
+		operand->mode = Addressing_NoValue;
+		break;
+
+	case BuiltinProc_read_cycle_counter:
+		if (!build_context.use_llvm_api) {
+			error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
+		}
+		operand->mode = Addressing_Value;
+		operand->type = t_i64;
+		break;
+
+	case BuiltinProc_count_ones:
+	case BuiltinProc_trailing_zeros:
+	case BuiltinProc_reverse_bits:
+		if (!build_context.use_llvm_api) {
+			error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
+			// continue anyway
+		}
+		{
+			Operand x = {};
+			check_expr(c, &x, ce->args[0]);
+			if (x.mode == Addressing_Invalid) {
+				return false;
+			}
+
+			if (!is_type_integer_like(x.type)) {
+				gbString xts = type_to_string(x.type);
+				error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_procs[id].name), xts);
+				gb_string_free(xts);
+			} else if (x.type == t_llvm_bool) {
+				gbString xts = type_to_string(x.type);
+				error(x.expr, "Invalid type passed to '%.*s', got %s", LIT(builtin_procs[id].name), xts);
+				gb_string_free(xts);
+			}
+
+			operand->mode = Addressing_Value;
+			operand->type = default_type(x.type);
+		}
+		break;
+
+	case BuiltinProc_byte_swap:
+		if (!build_context.use_llvm_api) {
+			error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
+			// continue anyway
+		}
+		{
+			Operand x = {};
+			check_expr(c, &x, ce->args[0]);
+			if (x.mode == Addressing_Invalid) {
+				return false;
+			}
+
+			if (!is_type_integer_like(x.type) && !is_type_float(x.type)) {
+				gbString xts = type_to_string(x.type);
+				error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set) or float, got %s", LIT(builtin_procs[id].name), xts);
+				gb_string_free(xts);
+			} else if (x.type == t_llvm_bool) {
+				gbString xts = type_to_string(x.type);
+				error(x.expr, "Invalid type passed to '%.*s', got %s", LIT(builtin_procs[id].name), xts);
+				gb_string_free(xts);
+			}
+			i64 sz = type_size_of(x.type);
+			if (sz < 2) {
+				gbString xts = type_to_string(x.type);
+				error(x.expr, "Type passed to '%.*s' must be at least 2 bytes, got %s with size of %lld", LIT(builtin_procs[id].name), xts, sz);
+				gb_string_free(xts);
+			}
 
+			operand->mode = Addressing_Value;
+			operand->type = default_type(x.type);
+		}
+		break;
+
+	case BuiltinProc_overflow_add:
+	case BuiltinProc_overflow_sub:
+	case BuiltinProc_overflow_mul:
+		if (!build_context.use_llvm_api) {
+			error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
+			// continue anyway
+		}
+		{
+			Operand x = {};
+			Operand y = {};
+			check_expr(c, &x, ce->args[0]);
+			check_expr(c, &y, ce->args[1]);
+			if (x.mode == Addressing_Invalid) {
+				return false;
+			}
+			if (y.mode == Addressing_Invalid) {
+				return false;
+			}
+			convert_to_typed(c, &y, x.type);
+			convert_to_typed(c, &x, y.type);
+			if (is_type_untyped(x.type)) {
+				gbString xts = type_to_string(x.type);
+				error(x.expr, "Expected a typed integer for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
+				gb_string_free(xts);
+				return false;
+			}
+			if (!is_type_integer(x.type)) {
+				gbString xts = type_to_string(x.type);
+				error(x.expr, "Expected an integer for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
+				gb_string_free(xts);
+				return false;
+			}
+			Type *ct = core_type(x.type);
+			if (is_type_different_to_arch_endianness(ct)) {
+				GB_ASSERT(ct->kind == Type_Basic);
+				if (ct->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) {
+					gbString xts = type_to_string(x.type);
+					error(x.expr, "Expected an integer which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
+					gb_string_free(xts);
+					return false;
+				}
+			}
 
+			operand->mode = Addressing_Value;
+			operand->type = make_optional_ok_type(default_type(x.type), false); // Just reusing this procedure, it's not optional
+		}
+		break;
 
 	case BuiltinProc_atomic_fence:
 	case BuiltinProc_atomic_fence_acq:
@@ -5859,8 +6022,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 	case BuiltinProc_fixed_point_div_sat:
 		{
 			if (!build_context.use_llvm_api) {
-				error(ce->args[0], "%.*s is not supported on this backend", LIT(builtin_procs[id].name));
-				return false;
+				error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
+				// continue anyway
 			}
 
 			Operand x = {};
@@ -5885,7 +6048,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			if (!are_types_identical(x.type, y.type)) {
 				gbString xts = type_to_string(x.type);
 				gbString yts = type_to_string(y.type);
-				error(x.expr, "Mismatched types for %.*s, %s vs %s", LIT(builtin_procs[id].name), xts, yts);
+				error(x.expr, "Mismatched types for '%.*s', %s vs %s", LIT(builtin_procs[id].name), xts, yts);
 				gb_string_free(yts);
 				gb_string_free(xts);
 				return false;
@@ -5893,7 +6056,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 			if (!is_type_integer(x.type) || is_type_untyped(x.type)) {
 				gbString xts = type_to_string(x.type);
-				error(x.expr, "Expected an integer type for %.*s, got %s", LIT(builtin_procs[id].name), xts);
+				error(x.expr, "Expected an integer type for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
 				gb_string_free(xts);
 				return false;
 			}
@@ -5903,17 +6066,17 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				return false;
 			}
 			if (z.mode != Addressing_Constant || !is_type_integer(z.type)) {
-				error(z.expr, "Expected a constant integer for the scale in %.*s", LIT(builtin_procs[id].name));
+				error(z.expr, "Expected a constant integer for the scale in '%.*s'", LIT(builtin_procs[id].name));
 				return false;
 			}
 			i64 n = exact_value_to_i64(z.value);
 			if (n <= 0) {
-				error(z.expr, "Scale parameter in %.*s must be positive, got %lld", LIT(builtin_procs[id].name), n);
+				error(z.expr, "Scale parameter in '%.*s' must be positive, got %lld", LIT(builtin_procs[id].name), n);
 				return false;
 			}
 			i64 sz = 8*type_size_of(x.type);
 			if (n > sz) {
-				error(z.expr, "Scale parameter in %.*s is larger than the base integer bit width, got %lld, expected a maximum of %lld", LIT(builtin_procs[id].name), n, sz);
+				error(z.expr, "Scale parameter in '%.*s' is larger than the base integer bit width, got %lld, expected a maximum of %lld", LIT(builtin_procs[id].name), n, sz);
 				return false;
 			}
 
@@ -5923,6 +6086,59 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		break;
 
 
+	case BuiltinProc_expect:
+		if (!build_context.use_llvm_api) {
+			error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
+			// continue anyway
+		}
+		{
+			Operand x = {};
+			Operand y = {};
+			check_expr(c, &x, ce->args[0]);
+			check_expr(c, &y, ce->args[1]);
+			if (x.mode == Addressing_Invalid) {
+				return false;
+			}
+			if (y.mode == Addressing_Invalid) {
+				return false;
+			}
+			convert_to_typed(c, &y, x.type);
+			convert_to_typed(c, &x, y.type);
+			if (!are_types_identical(x.type, y.type)) {
+				gbString xts = type_to_string(x.type);
+				gbString yts = type_to_string(y.type);
+				error(x.expr, "Mismatched types for '%.*s', %s vs %s", LIT(builtin_procs[id].name), xts, yts);
+				gb_string_free(yts);
+				gb_string_free(xts);
+				*operand = x; // minimize error propagation
+				return true;
+			}
+
+			if (!is_type_integer_like(x.type)) {
+				gbString xts = type_to_string(x.type);
+				error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_procs[id].name), xts);
+				gb_string_free(xts);
+				*operand = x;
+				return true;
+			}
+
+			if (y.mode != Addressing_Constant) {
+				error(y.expr, "Second argument to '%.*s' must be constant as it is the expected value", LIT(builtin_procs[id].name));
+			}
+
+			if (x.mode == Addressing_Constant) {
+				// NOTE(bill): just completely ignore this intrinsic entirely
+				*operand = x;
+				return true;
+			}
+
+			operand->mode = Addressing_Value;
+			operand->type = x.type;
+		}
+		break;
+
+
+
 
 	case BuiltinProc_type_base_type:
 		if (operand->mode != Addressing_Type) {
@@ -6462,17 +6678,38 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs,
 		if (o.type == nullptr || o.type->kind != Type_Tuple) {
 			if (lhs.count == 2 && rhs.count == 1 &&
 			    (o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) {
-				Type *tuple = make_optional_ok_type(o.type);
-				add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
+			    	bool do_normal = true;
+				Ast *expr = unparen_expr(o.expr);
 
-				Operand val = o;
-				Operand ok = o;
-				val.mode = Addressing_Value;
-				ok.mode  = Addressing_Value;
-				ok.type  = t_untyped_bool;
-				array_add(operands, val);
-				array_add(operands, ok);
+				Operand val0 = o;
+				Operand val1 = o;
+				val0.mode = Addressing_Value;
+				val1.mode = Addressing_Value;
+				val1.type = t_untyped_bool;
 
+
+				if (expr->kind == Ast_CallExpr) {
+					Type *pt = base_type(type_of_expr(expr->CallExpr.proc));
+					if (is_type_proc(pt)) {
+						do_normal = false;
+						Type *tuple = pt->Proc.results;
+						add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
+
+						if (pt->Proc.result_count >= 2) {
+							Type *t1 = tuple->Tuple.variables[1]->type;
+							val1.type = t1;
+						}
+						expr->CallExpr.optional_ok_one = false;
+					}
+				}
+
+				if (do_normal) {
+					Type *tuple = make_optional_ok_type(o.type);
+					add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
+				}
+
+				array_add(operands, val0);
+				array_add(operands, val1);
 				optional_ok = true;
 				tuple_index += 2;
 			} else if (o.mode == Addressing_OptionalOk && is_type_tuple(o.type)) {
@@ -6493,27 +6730,12 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs,
 			}
 		} else {
 			TypeTuple *tuple = &o.type->Tuple;
-			if (o.mode == Addressing_OptionalOk  && is_type_tuple(o.type) && lhs.count == 1) {
-				GB_ASSERT(tuple->variables.count == 2);
-				Ast *expr = unparen_expr(o.expr);
-				if (expr->kind == Ast_CallExpr) {
-					expr->CallExpr.optional_ok_one = true;
-				}
-				Operand val = o;
-				val.type = tuple->variables[0]->type;
-				val.mode = Addressing_Value;
-				array_add(operands, val);
-				tuple_index += tuple->variables.count;
-
-				add_type_and_value(c->info, val.expr, val.mode, val.type, val.value);
-			} else {
-				for_array(j, tuple->variables) {
-					o.type = tuple->variables[j]->type;
-					array_add(operands, o);
-				}
-
-				tuple_index += tuple->variables.count;
+			for_array(j, tuple->variables) {
+				o.type = tuple->variables[j]->type;
+				array_add(operands, o);
 			}
+
+			tuple_index += tuple->variables.count;
 		}
 	}
 
@@ -6570,18 +6792,38 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
 		if (o.type == nullptr || o.type->kind != Type_Tuple) {
 			if (allow_ok && lhs_count == 2 && rhs.count == 1 &&
 			    (o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) {
-				Type *tuple = make_optional_ok_type(o.type);
-				add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
+				bool do_normal = true;
+				Ast *expr = unparen_expr(o.expr);
 
-				Operand val = o;
-				Operand ok = o;
-				val.mode = Addressing_Value;
-				ok.mode  = Addressing_Value;
-				// ok.type  = t_bool;
-				ok.type  = t_untyped_bool;
-				array_add(operands, val);
-				array_add(operands, ok);
+				Operand val0 = o;
+				Operand val1 = o;
+				val0.mode = Addressing_Value;
+				val1.mode = Addressing_Value;
+				val1.type = t_untyped_bool;
 
+
+				if (expr->kind == Ast_CallExpr) {
+					Type *pt = base_type(type_of_expr(expr->CallExpr.proc));
+					if (is_type_proc(pt)) {
+						do_normal = false;
+						Type *tuple = pt->Proc.results;
+						add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
+
+						if (pt->Proc.result_count >= 2) {
+							Type *t1 = tuple->Tuple.variables[1]->type;
+							val1.type = t1;
+						}
+						expr->CallExpr.optional_ok_one = false;
+					}
+				}
+
+				if (do_normal) {
+					Type *tuple = make_optional_ok_type(o.type);
+					add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
+				}
+
+				array_add(operands, val0);
+				array_add(operands, val1);
 				optional_ok = true;
 				tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, 2);
 			} else {
@@ -6590,30 +6832,13 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
 			}
 		} else {
 			TypeTuple *tuple = &o.type->Tuple;
-			if (o.mode == Addressing_OptionalOk && lhs_count == 1) {
-				GB_ASSERT(tuple->variables.count == 2);
-				Ast *expr = unparen_expr(o.expr);
-				if (expr->kind == Ast_CallExpr) {
-					expr->CallExpr.optional_ok_one = true;
-				}
-				Operand val = o;
-				val.type = tuple->variables[0]->type;
-				val.mode = Addressing_Value;
-				array_add(operands, val);
-
-				isize count = tuple->variables.count;
-				tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count);
-
-				add_type_and_value(c->info, val.expr, val.mode, val.type, val.value);
-			} else {
-				for_array(j, tuple->variables) {
-					o.type = tuple->variables[j]->type;
-					array_add(operands, o);
-				}
-
-				isize count = tuple->variables.count;
-				tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count);
+			for_array(j, tuple->variables) {
+				o.type = tuple->variables[j]->type;
+				array_add(operands, o);
 			}
+
+			isize count = tuple->variables.count;
+			tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count);
 		}
 	}
 
@@ -6863,6 +7088,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 		data->score = score;
 		data->result_type = final_proc_type->Proc.results;
 		data->gen_entity = gen_entity;
+		add_type_and_value(c->info, ce->proc, Addressing_Value, final_proc_type, {});
 	}
 
 	return err;
@@ -7080,6 +7306,7 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
 		data->score = score;
 		data->result_type = pt->results;
 		data->gen_entity = gen_entity;
+		add_type_and_value(c->info, ce->proc, Addressing_Value, proc_type, {});
 	}
 
 	return err;
@@ -7333,7 +7560,6 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
 			lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic);
 			check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, is_variadic);
 
-
 			CallArgumentData data = {};
 			CallArgumentError err = call_checker(c, call, e->type, e, operands, CallArgumentMode_ShowErrors, &data);
 			if (err != CallArgumentError_None) {
@@ -7341,7 +7567,9 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
 			}
 			Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
 			add_entity_use(c, ident, entity_to_use);
-
+			if (entity_to_use != nullptr) {
+				update_expr_type(c, operand->expr, entity_to_use->type, true);
+			}
 			return data;
 		}
 
@@ -7613,6 +7841,9 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
 			CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
 			Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
 			add_entity_use(c, ident, entity_to_use);
+			if (entity_to_use != nullptr) {
+				update_expr_type(c, operand->expr, entity_to_use->type, true);
+			}
 
 			if (data.gen_entity != nullptr) {
 				Entity *e = data.gen_entity;
@@ -7628,7 +7859,6 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
 				evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
 				decl->where_clauses_evaluated = true;
 			}
-
 			return data;
 		}
 	} else {
@@ -7644,6 +7874,9 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
 		CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
 		Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
 		add_entity_use(c, ident, entity_to_use);
+		if (entity_to_use != nullptr) {
+			update_expr_type(c, operand->expr, entity_to_use->type, true);
+		}
 
 		if (data.gen_entity != nullptr) {
 			Entity *e = data.gen_entity;
@@ -7659,10 +7892,10 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
 			evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
 			decl->where_clauses_evaluated = true;
 		}
-
 		return data;
 	}
 
+
 	CallArgumentData data = {};
 	data.result_type = t_invalid;
 	return data;
@@ -8147,6 +8380,14 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
 	}
 
 	Type *pt = base_type(proc_type);
+	if (pt == t_invalid) {
+		if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) {
+			pt = type_of_expr(operand->expr->CallExpr.proc);
+		}
+		if (pt == t_invalid && data.gen_entity) {
+			pt = data.gen_entity->type;
+		}
+	}
 
 	if (pt->kind == Type_Proc && pt->Proc.calling_convention == ProcCC_Odin) {
 		if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
@@ -8175,7 +8416,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
 	}
 
 	switch (inlining) {
-	case ProcInlining_inline: {
+	case ProcInlining_inline:
 		if (proc != nullptr) {
 			Entity *e = entity_from_expr(proc);
 			if (e != nullptr && e->kind == Entity_Procedure) {
@@ -8189,16 +8430,31 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
 			}
 		}
 		break;
-	}
-
 	case ProcInlining_no_inline:
 		break;
 	}
 
 	operand->expr = call;
 
-	if (pt->kind == Type_Proc && pt->Proc.optional_ok) {
-		operand->mode = Addressing_OptionalOk;
+	{
+		if (proc_type == t_invalid) {
+			// gb_printf_err("%s\n", expr_to_string(operand->expr));
+		}
+		Type *type = nullptr;
+		if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) {
+			type = type_of_expr(operand->expr->CallExpr.proc);
+		}
+		if (type == nullptr) {
+			type = pt;
+		}
+		type = base_type(type);
+		if (type->kind == Type_Proc && type->Proc.optional_ok) {
+			operand->mode = Addressing_OptionalOk;
+			operand->type = type->Proc.results->Tuple.variables[0]->type;
+			if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) {
+				operand->expr->CallExpr.optional_ok_one = true;
+			}
+		}
 	}
 
 	// add_type_and_value(c->info, operand->expr, operand->mode, operand->type, operand->value);
@@ -9179,9 +9435,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 				if (!is_constant) {
 					error(node, "Expected all constant elements for a simd vector");
 				}
-				if (t->SimdVector.is_x86_mmx) {
-					error(node, "Compound literals are not allowed with intrinsics.x86_mmx");
-				}
 			}
 
 
@@ -9822,7 +10075,16 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 			return kind;
 		}
 		if (type_hint) {
+			Type *type = type_of_expr(ac->expr);
 			check_cast(c, o, type_hint);
+			if (is_type_typed(type) && are_types_identical(type, type_hint)) {
+				if (build_context.vet) {
+					error(node, "Redundant 'auto_cast' applied to expression");
+				} else {
+					warning(node, "Redundant 'auto_cast' applied to expression");
+				}
+			}
+
 		}
 		o->expr = node;
 		return Expr_Expr;
@@ -10563,7 +10825,7 @@ void check_not_tuple(CheckerContext *c, Operand *o) {
 		if (o->type->kind == Type_Tuple) {
 			isize count = o->type->Tuple.variables.count;
 			error(o->expr,
-			      "%td-valued tuple found where single value expected", count);
+			      "%td-valued expression found where single value expected", count);
 			o->mode = Addressing_Invalid;
 			GB_ASSERT(count != 1);
 		}

+ 16 - 4
src/check_stmt.cpp

@@ -10,7 +10,19 @@ bool is_diverging_stmt(Ast *stmt) {
 		String name = expr->CallExpr.proc->BasicDirective.name;
 		return name == "panic";
 	}
-	Type *t = type_of_expr(expr->CallExpr.proc);
+	Ast *proc = unparen_expr(expr->CallExpr.proc);
+	TypeAndValue tv = proc->tav;
+	if (tv.mode == Addressing_Builtin) {
+		Entity *e = entity_of_node(proc);
+		BuiltinProcId id = BuiltinProc_Invalid;
+		if (e != nullptr) {
+			id = cast(BuiltinProcId)e->Builtin.id;
+		} else {
+			id = BuiltinProc_DIRECTIVE;
+		}
+		return builtin_procs[id].diverging;
+	}
+	Type *t = tv.type;
 	t = base_type(t);
 	return t != nullptr && t->kind == Type_Proc && t->Proc.diverging;
 }
@@ -1751,7 +1763,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 						Type *cond_type = t->Tuple.variables[count-1]->type;
 						if (!is_type_boolean(cond_type)) {
 							gbString s = type_to_string(cond_type);
-							error(operand.expr, "The final type of %td-valued tuple must be a boolean, got %s", count, s);
+							error(operand.expr, "The final type of %td-valued expression must be a boolean, got %s", count, s);
 							gb_string_free(s);
 							break;
 						}
@@ -1762,14 +1774,14 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 
 						if (rs->vals.count > 1 && rs->vals[1] != nullptr && count < 3) {
 							gbString s = type_to_string(t);
-							error(operand.expr, "Expected a 3-value tuple on the rhs, got (%s)", s);
+							error(operand.expr, "Expected a 3-valued expression on the rhs, got (%s)", s);
 							gb_string_free(s);
 							break;
 						}
 
 						if (rs->vals.count > 0 && rs->vals[0] != nullptr && count < 2) {
 							gbString s = type_to_string(t);
-							error(operand.expr, "Expected at least a 2-values tuple on the rhs, got (%s)", s);
+							error(operand.expr, "Expected at least a 2-valued expression on the rhs, got (%s)", s);
 							gb_string_free(s);
 							break;
 						}

+ 35 - 7
src/check_type.cpp

@@ -2101,8 +2101,15 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall
 			}
 			break;
 		}
-		case Type_Pointer: break;
-		case Type_Proc:    break; // NOTE(bill): Just a pointer
+		case Type_Pointer:
+			if (is_type_struct(bt->Pointer.elem)) {
+				// Force to a raw pointer
+				new_type = t_rawptr;
+			}
+			break;
+		case Type_Proc:
+			new_type = t_rawptr;
+			break; // NOTE(bill): Just a pointer
 
 		// Odin specific
 		case Type_Slice:
@@ -2194,6 +2201,10 @@ Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCal
 		return new_type;
 	}
 
+	if (is_type_pointer(single_type)) {
+		// NOTE(bill): Force a cast to prevent a possible type cycle
+		return t_rawptr;
+	}
 
 	if (build_context.ODIN_OS == "windows") {
 		if (build_context.ODIN_ARCH == "amd64") {
@@ -2450,6 +2461,24 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
 			}
 		}
 	}
+	if (pt->tags & ProcTag_optional_second) {
+		if (optional_ok) {
+			error(proc_type_node, "A procedure type cannot have both an #optional_ok tag and #optional_second");
+		}
+		optional_ok = true;
+		if (result_count != 2) {
+			error(proc_type_node, "A procedure type with the #optional_second tag requires 2 return values, got %td", result_count);
+		} else {
+			bool ok = false;
+			if (proc_type_node->file && proc_type_node->file->pkg) {
+				ok = proc_type_node->file->pkg->scope == ctx->info->runtime_package->scope;
+			}
+
+			if (!ok) {
+				error(proc_type_node, "A procedure type with the #optional_second may only be allowed within 'package runtime'");
+			}
+		}
+	}
 
 	type->Proc.node                 = proc_type_node;
 	type->Proc.scope                = c->scope;
@@ -2590,12 +2619,11 @@ i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) {
 }
 
 Type *make_optional_ok_type(Type *value, bool typed) {
-	// LEAK TODO(bill): probably don't reallocate everything here and reuse the same one for the same type if possible
-	gbAllocator a = heap_allocator();
+	gbAllocator a = permanent_allocator();
 	Type *t = alloc_type_tuple();
-	array_init(&t->Tuple.variables, a, 0, 2);
-	array_add (&t->Tuple.variables, alloc_entity_field(nullptr, blank_token, value,  false, 0));
-	array_add (&t->Tuple.variables, alloc_entity_field(nullptr, blank_token, typed ? t_bool : t_untyped_bool, false, 1));
+	array_init(&t->Tuple.variables, a, 2);
+	t->Tuple.variables[0] = alloc_entity_field(nullptr, blank_token, value,  false, 0);
+	t->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, typed ? t_bool : t_untyped_bool, false, 1);
 	return t;
 }
 

+ 40 - 20
src/checker.cpp

@@ -783,15 +783,6 @@ void init_universal(void) {
 		}
 	}
 
-	// TODO(bill): Set the correct arch for this
-	if (bc->metrics.arch == TargetArch_amd64 || bc->metrics.arch == TargetArch_386) {
-		t_vector_x86_mmx = alloc_type(Type_SimdVector);
-		t_vector_x86_mmx->SimdVector.is_x86_mmx = true;
-
-		Entity *entity = alloc_entity(Entity_TypeName, nullptr, make_token_ident(str_lit("x86_mmx")), t_vector_x86_mmx);
-		add_global_entity(entity, intrinsics_pkg->scope);
-	}
-
 	bool defined_values_double_declaration = false;
 	for_array(i, bc->defined_values.entries) {
 		char const *name = cast(char const *)cast(uintptr)bc->defined_values.entries[i].key.key;
@@ -1782,20 +1773,28 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
 		str_lit("memory_equal"),
 		str_lit("memory_compare"),
 		str_lit("memory_compare_zero"),
-
-		str_lit("bswap_16"),
-		str_lit("bswap_32"),
-		str_lit("bswap_64"),
-		str_lit("bswap_128"),
-
-		str_lit("bswap_f16"),
-		str_lit("bswap_f32"),
-		str_lit("bswap_f64"),
 	};
 	for (isize i = 0; i < gb_count_of(required_runtime_entities); i++) {
 		force_add_dependency_entity(c, c->info.runtime_package->scope, required_runtime_entities[i]);
 	}
 
+	if (!build_context.use_llvm_api) {
+		String other_required_runtime_entities[] = {
+			str_lit("bswap_16"),
+			str_lit("bswap_32"),
+			str_lit("bswap_64"),
+			str_lit("bswap_128"),
+
+			str_lit("bswap_f16"),
+			str_lit("bswap_f32"),
+			str_lit("bswap_f64"),
+		};
+
+		for (isize i = 0; i < gb_count_of(other_required_runtime_entities); i++) {
+			force_add_dependency_entity(c, c->info.runtime_package->scope, other_required_runtime_entities[i]);
+		}
+	}
+
 	if (build_context.no_crt) {
 		String required_no_crt_entities[] = {
 			// NOTE(bill): Only if these exist
@@ -2190,8 +2189,6 @@ Type *check_poly_path_pop(CheckerContext *c) {
 
 
 
-void check_entity_decl(CheckerContext *c, Entity *e, DeclInfo *d, Type *named_type);
-
 Array<Entity *> proc_group_entities(CheckerContext *c, Operand o) {
 	Array<Entity *> procs = {};
 	if (o.mode == Addressing_ProcGroup) {
@@ -2574,6 +2571,29 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 			}
 		}
 		return true;
+	} else if (name == "optimization_mode") {
+		ExactValue ev = check_decl_attribute_value(c, value);
+		if (ev.kind == ExactValue_String) {
+			String mode = ev.value_string;
+			if (mode == "none") {
+				ac->optimization_mode = ProcedureOptimizationMode_None;
+			} else if (mode == "minimal") {
+				ac->optimization_mode = ProcedureOptimizationMode_Minimal;
+			} else if (mode == "size") {
+				ac->optimization_mode = ProcedureOptimizationMode_Size;
+			} else if (mode == "speed") {
+				ac->optimization_mode = ProcedureOptimizationMode_Speed;
+			} else {
+				error(elem, "Invalid optimization_mode for '%.*s'. Valid modes:", LIT(name));
+				error_line("\tnone\n");
+				error_line("\tminimal\n");
+				error_line("\tsize\n");
+				error_line("\tspeed\n");
+			}
+		} else {
+			error(elem, "Expected a string for '%.*s'", LIT(name));
+		}
+		return true;
 	}
 	return false;
 }

+ 6 - 0
src/checker.hpp

@@ -59,6 +59,7 @@ struct BuiltinProc {
 	bool     variadic;
 	ExprKind kind;
 	BuiltinProcPkg pkg;
+	bool diverging;
 };
 
 
@@ -112,6 +113,7 @@ struct AttributeContext {
 	String  thread_local_model;
 	String  deprecated_message;
 	DeferredProcedure deferred_procedure;
+	u32 optimization_mode; // ProcedureOptimizationMode
 	struct TypeAtomOpTable *atom_op_table;
 };
 
@@ -396,6 +398,10 @@ void check_add_import_decl(CheckerContext *c, Ast *decl);
 void check_add_foreign_import_decl(CheckerContext *c, Ast *decl);
 
 
+void check_entity_decl(CheckerContext *c, Entity *e, DeclInfo *d, Type *named_type);
+void check_const_decl(CheckerContext *c, Entity *e, Ast *type_expr, Ast *init_expr, Type *named_type);
+void check_type_decl(CheckerContext *c, Entity *e, Ast *type_expr, Type *def);
+
 bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_global = false);
 void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes);
 void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws);

+ 29 - 0
src/checker_builtin_procs.hpp

@@ -41,6 +41,18 @@ enum BuiltinProcId {
 
 	BuiltinProc_alloca,
 	BuiltinProc_cpu_relax,
+	BuiltinProc_trap,
+	BuiltinProc_debug_trap,
+	BuiltinProc_read_cycle_counter,
+
+	BuiltinProc_count_ones,
+	BuiltinProc_trailing_zeros,
+	BuiltinProc_reverse_bits,
+	BuiltinProc_byte_swap,
+
+	BuiltinProc_overflow_add,
+	BuiltinProc_overflow_sub,
+	BuiltinProc_overflow_mul,
 
 	BuiltinProc_volatile_store,
 	BuiltinProc_volatile_load,
@@ -122,6 +134,7 @@ enum BuiltinProcId {
 	BuiltinProc_fixed_point_mul_sat,
 	BuiltinProc_fixed_point_div_sat,
 
+	BuiltinProc_expect,
 
 	// Constant type tests
 
@@ -246,6 +259,19 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("alloca"),    2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("cpu_relax"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
 
+	{STR_LIT("trap"),               0, false, Expr_Stmt, BuiltinProcPkg_intrinsics, /*diverging*/true},
+	{STR_LIT("debug_trap"),         0, false, Expr_Stmt, BuiltinProcPkg_intrinsics, /*diverging*/false},
+	{STR_LIT("read_cycle_counter"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+	{STR_LIT("count_ones"),     1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("trailing_zeros"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("reverse_bits"),   1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("byte_swap"),      1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+	{STR_LIT("overflow_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("overflow_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("overflow_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
 	{STR_LIT("volatile_store"),  2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
 	{STR_LIT("volatile_load"),   1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
@@ -327,6 +353,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("fixed_point_mul_sat"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("fixed_point_div_sat"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
+	{STR_LIT("expect"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+
 	{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_base_type"),            1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_core_type"),            1, false, Expr_Expr, BuiltinProcPkg_intrinsics},

+ 0 - 4
src/docs_format.cpp

@@ -114,10 +114,6 @@ enum OdinDocTypeFlag_BitSet : u32 {
 	OdinDocTypeFlag_BitSet_UnderlyingType = 1<<4,
 };
 
-enum OdinDocTypeFlag_SimdVector : u32 {
-	OdinDocTypeFlag_BitSet_x86_mmx = 1<<1,
-};
-
 enum {
 	// constants
 	OdinDocType_ElemsCap = 4,

+ 3 - 7
src/docs_writer.cpp

@@ -724,13 +724,9 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
 		break;
 	case Type_SimdVector:
 		doc_type.kind = OdinDocType_SimdVector;
-		if (type->SimdVector.is_x86_mmx) {
-			doc_type.flags |= OdinDocTypeFlag_BitSet_x86_mmx;
-		} else {
-			doc_type.elem_count_len = 1;
-			doc_type.elem_counts[0] = type->SimdVector.count;
-			doc_type.types = odin_doc_type_as_slice(w, type->SimdVector.elem);
-		}
+		doc_type.elem_count_len = 1;
+		doc_type.elem_counts[0] = type->SimdVector.count;
+		doc_type.types = odin_doc_type_as_slice(w, type->SimdVector.elem);
 		// TODO(bill):
 		break;
 	case Type_RelativePointer:

+ 40 - 26
src/entity.cpp

@@ -33,39 +33,41 @@ String const entity_strings[] = {
 };
 
 enum EntityFlag : u64 {
-	EntityFlag_Visited       = 1<<0,
-	EntityFlag_Used          = 1<<1,
-	EntityFlag_Using         = 1<<2,
-	EntityFlag_Field         = 1<<3,
-	EntityFlag_Param         = 1<<4,
-	EntityFlag_Result        = 1<<5,
-	EntityFlag_ArrayElem     = 1<<6,
-	EntityFlag_Ellipsis      = 1<<7,
-	EntityFlag_NoAlias       = 1<<8,
-	EntityFlag_TypeField     = 1<<9,
-	EntityFlag_Value         = 1<<10,
-	EntityFlag_Sret          = 1<<11,
-	EntityFlag_ByVal         = 1<<12,
-	EntityFlag_BitFieldValue = 1<<13,
-	EntityFlag_PolyConst     = 1<<14,
-	EntityFlag_NotExported   = 1<<15,
-	EntityFlag_ConstInput    = 1<<16,
+	EntityFlag_Visited       = 1ull<<0,
+	EntityFlag_Used          = 1ull<<1,
+	EntityFlag_Using         = 1ull<<2,
+	EntityFlag_Field         = 1ull<<3,
+	EntityFlag_Param         = 1ull<<4,
+	EntityFlag_Result        = 1ull<<5,
+	EntityFlag_ArrayElem     = 1ull<<6,
+	EntityFlag_Ellipsis      = 1ull<<7,
+	EntityFlag_NoAlias       = 1ull<<8,
+	EntityFlag_TypeField     = 1ull<<9,
+	EntityFlag_Value         = 1ull<<10,
+	EntityFlag_Sret          = 1ull<<11,
+	EntityFlag_ByVal         = 1ull<<12,
+	EntityFlag_BitFieldValue = 1ull<<13,
+	EntityFlag_PolyConst     = 1ull<<14,
+	EntityFlag_NotExported   = 1ull<<15,
+	EntityFlag_ConstInput    = 1ull<<16,
 
-	EntityFlag_Static        = 1<<17,
+	EntityFlag_Static        = 1ull<<17,
 
-	EntityFlag_ImplicitReference = 1<<18, // NOTE(bill): equivalent to `const &` in C++
+	EntityFlag_ImplicitReference = 1ull<<18, // NOTE(bill): equivalent to `const &` in C++
 
-	EntityFlag_SoaPtrField   = 1<<19, // to allow s.x[0] where `s.x` is a pointer rather than a slice
+	EntityFlag_SoaPtrField   = 1ull<<19, // to allow s.x[0] where `s.x` is a pointer rather than a slice
 
-	EntityFlag_ProcBodyChecked = 1<<20,
+	EntityFlag_ProcBodyChecked = 1ull<<20,
 
-	EntityFlag_CVarArg       = 1<<21,
-	EntityFlag_AutoCast      = 1<<22,
+	EntityFlag_CVarArg       = 1ull<<21,
+	EntityFlag_AutoCast      = 1ull<<22,
 
-	EntityFlag_Disabled      = 1<<24,
-	EntityFlag_Cold          = 1<<25, // procedure is rarely called
+	EntityFlag_Disabled      = 1ull<<24,
+	EntityFlag_Cold          = 1ull<<25, // procedure is rarely called
 
-	EntityFlag_Test          = 1<<30,
+	EntityFlag_Test          = 1ull<<30,
+
+	EntityFlag_Overridden    = 1ull<<63,
 
 };
 
@@ -97,6 +99,14 @@ enum EntityConstantFlags : u32 {
 	EntityConstantFlag_ImplicitEnumValue = 1<<0,
 };
 
+enum ProcedureOptimizationMode : u32 {
+	ProcedureOptimizationMode_Default,
+	ProcedureOptimizationMode_None,
+	ProcedureOptimizationMode_Minimal,
+	ProcedureOptimizationMode_Size,
+	ProcedureOptimizationMode_Speed,
+};
+
 // An Entity is a named "thing" in the language
 struct Entity {
 	EntityKind  kind;
@@ -125,6 +135,9 @@ struct Entity {
 	// IMPORTANT NOTE(bill): This must be a discriminated union because of patching
 	// later entity kinds
 	union {
+		struct {
+			u8 start;
+		} Dummy;
 		struct {
 			ExactValue value;
 			ParameterValue param_value;
@@ -160,6 +173,7 @@ struct Entity {
 			DeferredProcedure deferred_procedure;
 			bool    is_foreign;
 			bool    is_export;
+			ProcedureOptimizationMode optimization_mode;
 		} Procedure;
 		struct {
 			Array<Entity *> entities;

+ 2 - 2
src/gb/gb.h

@@ -491,8 +491,8 @@ typedef i32 b32; // NOTE(bill): Prefer this!!!
 	#define USIZE_MIX U32_MIN
 	#define USIZE_MAX U32_MAX
 
-	#define ISIZE_MIX S32_MIN
-	#define ISIZE_MAX S32_MAX
+	#define ISIZE_MIX I32_MIN
+	#define ISIZE_MAX I32_MAX
 #elif defined(GB_ARCH_64_BIT)
 	#define USIZE_MIX U64_MIN
 	#define USIZE_MAX U64_MAX

+ 5 - 8
src/ir.cpp

@@ -8257,7 +8257,8 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
 	case_end;
 
 	case_ast_node(ac, AutoCast, expr);
-		return ir_build_expr(proc, ac->expr);
+		irValue *value = ir_build_expr(proc, ac->expr);
+		return ir_emit_conv(proc, value, tv.type);
 	case_end;
 
 	case_ast_node(ue, UnaryExpr, expr);
@@ -12564,13 +12565,9 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
 		case Type_SimdVector:
 			ir_emit_comment(proc, str_lit("Type_SimdVector"));
 			tag = ir_emit_conv(proc, variant_ptr, t_type_info_simd_vector_ptr);
-			if (t->SimdVector.is_x86_mmx) {
-				ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), v_true);
-			} else {
-				ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), ir_get_type_info_ptr(proc, t->SimdVector.elem));
-				ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), ir_const_int(type_size_of(t->SimdVector.elem)));
-				ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), ir_const_int(t->SimdVector.count));
-			}
+			ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), ir_get_type_info_ptr(proc, t->SimdVector.elem));
+			ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), ir_const_int(type_size_of(t->SimdVector.elem)));
+			ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), ir_const_int(t->SimdVector.count));
 			break;
 
 		case Type_RelativePointer:

+ 3 - 7
src/ir_print.cpp

@@ -624,13 +624,9 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
 	}
 
 	case Type_SimdVector:
-		if (t->SimdVector.is_x86_mmx) {
-			ir_write_str_lit(f, "x86_mmx");
-		} else {
-			ir_fprintf(f, "<%lld x ", t->SimdVector.count);;
-			ir_print_type(f, m, t->SimdVector.elem);
-			ir_write_byte(f, '>');
-		}
+		ir_fprintf(f, "<%lld x ", t->SimdVector.count);;
+		ir_print_type(f, m, t->SimdVector.elem);
+		ir_write_byte(f, '>');
 		return;
 
 	case Type_RelativePointer:

+ 352 - 114
src/llvm_backend.cpp

@@ -391,11 +391,37 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
 		lb_insert_dynamic_map_key_and_value(p, addr, addr.map.type, addr.map.key, value, p->curr_stmt);
 		return;
 	} else if (addr.kind == lbAddr_Context) {
-		lbValue old = lb_addr_load(p, lb_find_or_generate_context_ptr(p));
-		lbAddr next_addr = lb_add_local_generated(p, t_context, true);
-		lb_addr_store(p, next_addr, old);
-		lb_push_context_onto_stack(p, next_addr);
-		lbValue next = lb_addr_get_ptr(p, next_addr);
+		lbAddr old_addr = lb_find_or_generate_context_ptr(p);
+
+
+		// IMPORTANT NOTE(bill, 2021-04-22): reuse unused 'context' variables to minimize stack usage
+		// This has to be done manually since the optimizer cannot determine when this is possible
+		bool create_new = true;
+		for_array(i, p->context_stack) {
+			lbContextData *ctx_data = &p->context_stack[i];
+			if (ctx_data->ctx.addr.value == old_addr.addr.value) {
+				if (ctx_data->uses > 0) {
+					create_new = true;
+				} else if (p->scope_index > ctx_data->scope_index) {
+					create_new = true;
+				} else {
+					// gb_printf_err("%.*s (curr:%td) (ctx:%td) (uses:%td)\n", LIT(p->name), p->scope_index, ctx_data->scope_index, ctx_data->uses);
+					create_new = false;
+				}
+				break;
+			}
+		}
+
+		lbValue next = {};
+		if (create_new) {
+			lbValue old = lb_addr_load(p, old_addr);
+			lbAddr next_addr = lb_add_local_generated(p, t_context, true);
+			lb_addr_store(p, next_addr, old);
+			lb_push_context_onto_stack(p, next_addr);
+			next = next_addr.addr;
+		} else {
+			next = old_addr.addr;
+		}
 
 		if (addr.ctx.sel.index.count > 0) {
 			lbValue lhs = lb_emit_deep_field_gep(p, next, addr.ctx.sel);
@@ -623,6 +649,13 @@ lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
 		}
 	} else if (addr.kind == lbAddr_Context) {
 		lbValue a = addr.addr;
+		for_array(i, p->context_stack) {
+			lbContextData *ctx_data = &p->context_stack[i];
+			if (ctx_data->ctx.addr.value == a.value) {
+				ctx_data->uses += 1;
+				break;
+			}
+		}
 		a.value = LLVMBuildPointerCast(p->builder, a.value, lb_type(p->module, t_context_ptr), "");
 
 		if (addr.ctx.sel.index.count > 0) {
@@ -1445,9 +1478,6 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 		}
 
 	case Type_SimdVector:
-		if (type->SimdVector.is_x86_mmx) {
-			return LLVMX86MMXTypeInContext(ctx);
-		}
 		return LLVMVectorType(lb_type(m, type->SimdVector.elem), cast(unsigned)type->SimdVector.count);
 
 	case Type_RelativePointer:
@@ -1899,9 +1929,6 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
 		break;
 
 	case Type_SimdVector:
-		if (type->SimdVector.is_x86_mmx) {
-			return LLVMDIBuilderCreateVectorType(m->debug_builder, 2, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, t_f64), nullptr, 0);
-		}
 		return LLVMDIBuilderCreateVectorType(m->debug_builder, cast(unsigned)type->SimdVector.count, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, type->SimdVector.elem), nullptr, 0);
 
 	case Type_RelativePointer: {
@@ -2523,7 +2550,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
 	p->type           = entity->type;
 	p->type_expr      = decl->type_expr;
 	p->body           = pl->body;
-	p->inlining       = ProcInlining_none;
+	p->inlining       = pl->inlining;
 	p->is_foreign     = entity->Procedure.is_foreign;
 	p->is_export      = entity->Procedure.is_export;
 	p->is_entry_point = false;
@@ -2558,9 +2585,6 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
 		LLVMSetFunctionCallConv(p->value, cc_kind);
 	}
 
-	if (entity->flags & EntityFlag_Cold) {
-		lb_add_attribute_to_proc(m, p->value, "cold");
-	}
 
 	if (pt->Proc.diverging) {
 		lb_add_attribute_to_proc(m, p->value, "noreturn");
@@ -2575,6 +2599,28 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
 		break;
 	}
 
+	if (entity->flags & EntityFlag_Cold) {
+		lb_add_attribute_to_proc(m, p->value, "cold");
+	}
+
+	switch (entity->Procedure.optimization_mode) {
+	case ProcedureOptimizationMode_None:
+		lb_add_attribute_to_proc(m, p->value, "optnone");
+		break;
+	case ProcedureOptimizationMode_Minimal:
+		lb_add_attribute_to_proc(m, p->value, "optnone");
+		break;
+	case ProcedureOptimizationMode_Size:
+		lb_add_attribute_to_proc(m, p->value, "optsize");
+		break;
+	case ProcedureOptimizationMode_Speed:
+		// TODO(bill): handle this correctly
+		lb_add_attribute_to_proc(m, p->value, "optsize");
+		break;
+	}
+
+
+
 	// lbCallingConventionKind cc_kind = lbCallingConvention_C;
 	// // TODO(bill): Clean up this logic
 	// if (build_context.metrics.os != TargetOs_js)  {
@@ -2624,26 +2670,16 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
 		for (isize i = 0; i < pt->Proc.param_count; i++) {
 			Entity *e = params->variables[i];
 			Type *original_type = e->type;
-			Type *abi_type = pt->Proc.abi_compat_params[i];
 			if (e->kind != Entity_Variable) continue;
 
 			if (i+1 == params->variables.count && pt->Proc.c_vararg) {
 				continue;
 			}
-			if (is_type_tuple(abi_type)) {
-				for_array(j, abi_type->Tuple.variables) {
-					Type *tft = abi_type->Tuple.variables[j]->type;
-					if (e->flags&EntityFlag_NoAlias) {
-						lb_add_proc_attribute_at_index(p, offset+parameter_index+j, "noalias");
-					}
-				}
-				parameter_index += abi_type->Tuple.variables.count;
-			} else {
-				if (e->flags&EntityFlag_NoAlias) {
-					lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias");
-				}
-				parameter_index += 1;
+
+			if (e->flags&EntityFlag_NoAlias) {
+				lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias");
 			}
+			parameter_index += 1;
 		}
 	}
 
@@ -7567,6 +7603,7 @@ lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p
 	lbContextData *cd = array_add_and_get(&p->context_stack);
 	cd->ctx = ctx_addr;
 	cd->scope_index = -1;
+	cd->uses = +1; // make sure it has been used already
 	return cd;
 }
 
@@ -9031,6 +9068,145 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 		}
 		return {};
 
+
+	case BuiltinProc_debug_trap:
+	case BuiltinProc_trap:
+		{
+			char const *name = nullptr;
+			switch (id) {
+			case BuiltinProc_debug_trap: name = "llvm.debugtrap"; break;
+			case BuiltinProc_trap:       name = "llvm.trap";      break;
+			}
+
+			unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+			GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
+			LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0);
+
+			LLVMBuildCall(p->builder, ip, nullptr, 0, "");
+			if (id == BuiltinProc_trap) {
+				LLVMBuildUnreachable(p->builder);
+			}
+			return {};
+		}
+
+	case BuiltinProc_read_cycle_counter:
+		{
+			char const *name = "llvm.readcyclecounter";
+			unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+			GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
+			LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0);
+
+			lbValue res = {};
+			res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, "");
+			res.type = tv.type;
+			return res;
+		}
+
+	case BuiltinProc_trailing_zeros:
+		{
+			lbValue x = lb_build_expr(p, ce->args[0]);
+			x = lb_emit_conv(p, x, tv.type);
+
+			char const *name = "llvm.cttz";
+			LLVMTypeRef types[1] = {lb_type(p->module, tv.type)};
+			unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+			GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
+			LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+			LLVMValueRef args[2] = {};
+			args[0] = x.value;
+			args[1] = LLVMConstNull(LLVMInt1TypeInContext(p->module->ctx));
+
+			lbValue res = {};
+			res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+			res.type = tv.type;
+			return res;
+		}
+
+	case BuiltinProc_count_ones:
+	case BuiltinProc_reverse_bits:
+		{
+			lbValue x = lb_build_expr(p, ce->args[0]);
+			x = lb_emit_conv(p, x, tv.type);
+
+			char const *name = nullptr;
+			switch (id) {
+			case BuiltinProc_count_ones:     name = "llvm.ctpop";      break;
+			case BuiltinProc_reverse_bits:   name = "llvm.bitreverse"; break;
+			}
+			LLVMTypeRef types[1] = {lb_type(p->module, tv.type)};
+			unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+			GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
+			LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+			LLVMValueRef args[1] = {};
+			args[0] = x.value;
+
+			lbValue res = {};
+			res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+			res.type = tv.type;
+			return res;
+		}
+
+	case BuiltinProc_byte_swap:
+		{
+			lbValue x = lb_build_expr(p, ce->args[0]);
+			x = lb_emit_conv(p, x, tv.type);
+			return lb_emit_byte_swap(p, x, tv.type);
+		}
+
+	case BuiltinProc_overflow_add:
+	case BuiltinProc_overflow_sub:
+	case BuiltinProc_overflow_mul:
+		{
+			Type *tuple = tv.type;
+			GB_ASSERT(is_type_tuple(tuple));
+			Type *type = tuple->Tuple.variables[0]->type;
+
+			lbValue x = lb_build_expr(p, ce->args[0]);
+			lbValue y = lb_build_expr(p, ce->args[1]);
+			x = lb_emit_conv(p, x, type);
+			y = lb_emit_conv(p, y, type);
+
+			char const *name = nullptr;
+			if (is_type_unsigned(type)) {
+				switch (id) {
+				case BuiltinProc_overflow_add: name = "llvm.uadd.with.overflow"; break;
+				case BuiltinProc_overflow_sub: name = "llvm.usub.with.overflow"; break;
+				case BuiltinProc_overflow_mul: name = "llvm.umul.with.overflow"; break;
+				}
+			} else {
+				switch (id) {
+				case BuiltinProc_overflow_add: name = "llvm.sadd.with.overflow"; break;
+				case BuiltinProc_overflow_sub: name = "llvm.ssub.with.overflow"; break;
+				case BuiltinProc_overflow_mul: name = "llvm.smul.with.overflow"; break;
+				}
+			}
+			LLVMTypeRef types[1] = {lb_type(p->module, type)};
+			unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+			GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
+			LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+			LLVMValueRef args[2] = {};
+			args[0] = x.value;
+			args[1] = y.value;
+
+			Type *res_type = nullptr;
+			{
+				gbAllocator a = permanent_allocator();
+				res_type = alloc_type_tuple();
+				array_init(&res_type->Tuple.variables, a, 2);
+				res_type->Tuple.variables[0] = alloc_entity_field(nullptr, blank_token, type,        false, 0);
+				res_type->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, t_llvm_bool, false, 1);
+			}
+
+			lbValue res = {};
+			res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+			res.type = res_type;
+			return res;
+		}
+
+
 	case BuiltinProc_atomic_fence:
 		LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, "");
 		return {};
@@ -9304,6 +9480,30 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 			res.type = platform_type;
 			return lb_emit_conv(p, res, tv.type);
 		}
+
+	case BuiltinProc_expect:
+		{
+			Type *t = default_type(tv.type);
+			lbValue x = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t);
+			lbValue y = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t);
+
+			char const *name = "llvm.expect";
+
+			LLVMTypeRef types[1] = {lb_type(p->module, t)};
+			unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+			GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
+			LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+			lbValue res = {};
+
+			LLVMValueRef args[2] = {};
+			args[0] = x.value;
+			args[1] = y.value;
+
+			res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+			res.type = t;
+			return lb_emit_conv(p, res, t);
+		}
 	}
 
 	GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));
@@ -9692,6 +9892,7 @@ bool lb_is_const_nil(lbValue value) {
 
 String lb_get_const_string(lbModule *m, lbValue value) {
 	GB_ASSERT(lb_is_const(value));
+	GB_ASSERT(LLVMIsConstant(value.value));
 
 	Type *t = base_type(value.type);
 	GB_ASSERT(are_types_identical(t, t_string));
@@ -9743,43 +9944,43 @@ LLVMValueRef lb_lookup_runtime_procedure(lbModule *m, String const &name) {
 	return found->value;
 }
 
-lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *platform_type) {
-	Type *vt = core_type(value.type);
-	GB_ASSERT(type_size_of(vt) == type_size_of(platform_type));
-
-	// TODO(bill): lb_emit_byte_swap
-	lbValue res = {};
-	res.type = platform_type;
-	res.value = value.value;
+lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type) {
+	GB_ASSERT(type_size_of(value.type) == type_size_of(end_type));
 
-	int sz = cast(int)type_size_of(vt);
-	if (sz > 1) {
-		if (is_type_float(platform_type)) {
-			String name = {};
-			switch (sz) {
-			case 2:  name = str_lit("bswap_f16");  break;
-			case 4:  name = str_lit("bswap_f32");  break;
-			case 8:  name = str_lit("bswap_f64");  break;
-			default: GB_PANIC("unhandled byteswap size"); break;
-			}
-			LLVMValueRef fn = lb_lookup_runtime_procedure(p->module, name);
-			res.value = LLVMBuildCall(p->builder, fn, &value.value, 1, "");
-		} else {
-			GB_ASSERT(is_type_integer(platform_type));
-			String name = {};
-			switch (sz) {
-			case 2:  name = str_lit("bswap_16");  break;
-			case 4:  name = str_lit("bswap_32");  break;
-			case 8:  name = str_lit("bswap_64");  break;
-			case 16: name = str_lit("bswap_128"); break;
-			default: GB_PANIC("unhandled byteswap size"); break;
-			}
-			LLVMValueRef fn = lb_lookup_runtime_procedure(p->module, name);
+	if (type_size_of(value.type) < 2) {
+		return value;
+	}
 
-			res.value = LLVMBuildCall(p->builder, fn, &value.value, 1, "");
+	Type *original_type = value.type;
+	if (is_type_float(original_type)) {
+		i64 sz = type_size_of(original_type);
+		Type *integer_type = nullptr;
+		switch (sz) {
+		case 2: integer_type = t_u16; break;
+		case 4: integer_type = t_u32; break;
+		case 8: integer_type = t_u64; break;
 		}
+		GB_ASSERT(integer_type != nullptr);
+		value = lb_emit_transmute(p, value, integer_type);
 	}
 
+	char const *name = "llvm.bswap";
+	LLVMTypeRef types[1] = {lb_type(p->module, value.type)};
+	unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+	GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
+	LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+	LLVMValueRef args[1] = {};
+	args[0] = value.value;
+
+	lbValue res = {};
+	res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+	res.type = value.type;
+
+	if (is_type_float(original_type)) {
+		res = lb_emit_transmute(p, res, original_type);
+	}
+	res.type = end_type;
 	return res;
 }
 
@@ -10963,7 +11164,8 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
 	case_end;
 
 	case_ast_node(ac, AutoCast, expr);
-		return lb_build_expr(p, ac->expr);
+		lbValue value = lb_build_expr(p, ac->expr);
+		return lb_emit_conv(p, value, tv.type);
 	case_end;
 
 	case_ast_node(ue, UnaryExpr, expr);
@@ -13355,15 +13557,11 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
 			{
 				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_simd_vector_ptr);
 
-				LLVMValueRef vals[4] = {};
+				LLVMValueRef vals[3] = {};
 
-				if (t->SimdVector.is_x86_mmx) {
-					vals[3] = lb_const_bool(m, t_bool, true).value;
-				} else {
-					vals[0] = lb_get_type_info_ptr(m, t->SimdVector.elem).value;
-					vals[1] = lb_const_int(m, t_int, type_size_of(t->SimdVector.elem)).value;
-					vals[2] = lb_const_int(m, t_int, t->SimdVector.count).value;
-				}
+				vals[0] = lb_get_type_info_ptr(m, t->SimdVector.elem).value;
+				vals[1] = lb_const_int(m, t_int, type_size_of(t->SimdVector.elem)).value;
+				vals[2] = lb_const_int(m, t_int, t->SimdVector.count).value;
 
 				lbValue res = {};
 				res.type = type_deref(tag.type);
@@ -13844,11 +14042,28 @@ void lb_generate_code(lbGenerator *gen) {
 	LLVMPassRegistryRef pass_registry = LLVMGetGlobalPassRegistry();
 
 	LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(mod);
+	LLVMPassManagerRef function_pass_manager_minimal = LLVMCreateFunctionPassManagerForModule(mod);
+	LLVMPassManagerRef function_pass_manager_size = LLVMCreateFunctionPassManagerForModule(mod);
+	LLVMPassManagerRef function_pass_manager_speed = LLVMCreateFunctionPassManagerForModule(mod);
 	defer (LLVMDisposePassManager(default_function_pass_manager));
+	defer (LLVMDisposePassManager(function_pass_manager_minimal));
+	defer (LLVMDisposePassManager(function_pass_manager_size));
+	defer (LLVMDisposePassManager(function_pass_manager_speed));
 
 	LLVMInitializeFunctionPassManager(default_function_pass_manager);
+	LLVMInitializeFunctionPassManager(function_pass_manager_minimal);
+	LLVMInitializeFunctionPassManager(function_pass_manager_size);
+	LLVMInitializeFunctionPassManager(function_pass_manager_speed);
+
 	lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level);
+	lb_populate_function_pass_manager_specific(function_pass_manager_minimal, 0);
+	lb_populate_function_pass_manager_specific(function_pass_manager_size,    1);
+	lb_populate_function_pass_manager_specific(function_pass_manager_speed,   2);
+
 	LLVMFinalizeFunctionPassManager(default_function_pass_manager);
+	LLVMFinalizeFunctionPassManager(function_pass_manager_minimal);
+	LLVMFinalizeFunctionPassManager(function_pass_manager_size);
+	LLVMFinalizeFunctionPassManager(function_pass_manager_speed);
 
 
 	LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(mod);
@@ -13988,6 +14203,47 @@ void lb_generate_code(lbGenerator *gen) {
 		}*/
 	}
 
+	String filepath_ll = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".ll"));
+
+	TIME_SECTION("LLVM Procedure Generation");
+	for_array(i, m->procedures_to_generate) {
+		lbProcedure *p = m->procedures_to_generate[i];
+		if (p->is_done) {
+			continue;
+		}
+		if (p->body != nullptr) { // Build Procedure
+			m->curr_procedure = p;
+			lb_begin_procedure_body(p);
+			lb_build_stmt(p, p->body);
+			lb_end_procedure_body(p);
+			p->is_done = true;
+			m->curr_procedure = nullptr;
+		}
+		lb_end_procedure(p);
+
+		// Add Flags
+		if (p->body != nullptr) {
+			if (p->name == "memcpy" || p->name == "memmove" ||
+			    p->name == "runtime.mem_copy" || p->name == "mem_copy_non_overlapping" ||
+			    string_starts_with(p->name, str_lit("llvm.memcpy")) ||
+			    string_starts_with(p->name, str_lit("llvm.memmove"))) {
+				p->flags |= lbProcedureFlag_WithoutMemcpyPass;
+			}
+		}
+
+		if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
+			gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name));
+			LLVMDumpValue(p->value);
+			gb_printf_err("\n\n\n\n");
+			if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) {
+				gb_printf_err("LLVM Error: %s\n", llvm_error);
+			}
+			LLVMVerifyFunction(p->value, LLVMPrintMessageAction);
+			gb_exit(1);
+		}
+	}
+
+
 	if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) {
 		TIME_SECTION("LLVM DLL main");
 
@@ -14075,7 +14331,7 @@ void lb_generate_code(lbGenerator *gen) {
 		} else {
 			lbValue *found = map_get(&m->values, hash_entity(entry_point));
 			GB_ASSERT(found != nullptr);
-			LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, found->type)), found->value, nullptr, 0, "");
+			lb_emit_call(p, *found, {});
 		}
 
 		LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false));
@@ -14093,47 +14349,6 @@ void lb_generate_code(lbGenerator *gen) {
 	}
 
 
-	String filepath_ll = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".ll"));
-
-	TIME_SECTION("LLVM Procedure Generation");
-	for_array(i, m->procedures_to_generate) {
-		lbProcedure *p = m->procedures_to_generate[i];
-		if (p->is_done) {
-			continue;
-		}
-		if (p->body != nullptr) { // Build Procedure
-			m->curr_procedure = p;
-			lb_begin_procedure_body(p);
-			lb_build_stmt(p, p->body);
-			lb_end_procedure_body(p);
-			p->is_done = true;
-			m->curr_procedure = nullptr;
-		}
-		lb_end_procedure(p);
-
-		// Add Flags
-		if (p->body != nullptr) {
-			if (p->name == "memcpy" || p->name == "memmove" ||
-			    p->name == "runtime.mem_copy" || p->name == "mem_copy_non_overlapping" ||
-			    string_starts_with(p->name, str_lit("llvm.memcpy")) ||
-			    string_starts_with(p->name, str_lit("llvm.memmove"))) {
-				p->flags |= lbProcedureFlag_WithoutMemcpyPass;
-			}
-		}
-
-		if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
-			gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name));
-			LLVMDumpValue(p->value);
-			gb_printf_err("\n\n\n\n");
-			if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) {
-				gb_printf_err("LLVM Error: %s\n", llvm_error);
-			}
-			LLVMVerifyFunction(p->value, LLVMPrintMessageAction);
-			gb_exit(1);
-		}
-	}
-
-
 	if (m->debug_builder != nullptr) {
 		TIME_SECTION("LLVM Debug Info Complete Types");
 		lb_debug_complete_types(m);
@@ -14158,7 +14373,25 @@ void lb_generate_code(lbGenerator *gen) {
 				if (p->flags & lbProcedureFlag_WithoutMemcpyPass) {
 					LLVMRunFunctionPassManager(default_function_pass_manager_without_memcpy, p->value);
 				} else {
-					LLVMRunFunctionPassManager(default_function_pass_manager, p->value);
+					if (p->entity && p->entity->kind == Entity_Procedure) {
+						switch (p->entity->Procedure.optimization_mode) {
+						case ProcedureOptimizationMode_None:
+						case ProcedureOptimizationMode_Minimal:
+							LLVMRunFunctionPassManager(function_pass_manager_minimal, p->value);
+							break;
+						case ProcedureOptimizationMode_Size:
+							LLVMRunFunctionPassManager(function_pass_manager_size, p->value);
+							break;
+						case ProcedureOptimizationMode_Speed:
+							LLVMRunFunctionPassManager(function_pass_manager_speed, p->value);
+							break;
+						default:
+							LLVMRunFunctionPassManager(default_function_pass_manager, p->value);
+							break;
+						}
+					} else {
+						LLVMRunFunctionPassManager(default_function_pass_manager, p->value);
+					}
 				}
 			}
 		}
@@ -14220,13 +14453,18 @@ void lb_generate_code(lbGenerator *gen) {
 		return;
 	}
 	llvm_error = nullptr;
-	if (build_context.keep_temp_files) {
+	if (build_context.keep_temp_files ||
+	    build_context.build_mode == BuildMode_LLVM_IR) {
 		TIME_SECTION("LLVM Print Module to File");
 		if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) {
 			gb_printf_err("LLVM Error: %s\n", llvm_error);
 			gb_exit(1);
 			return;
 		}
+		if (build_context.build_mode == BuildMode_LLVM_IR) {
+			gb_exit(0);
+			return;
+		}
 	}
 
 	TIME_SECTION("LLVM Object Generation");

+ 2 - 1
src/llvm_backend.hpp

@@ -157,6 +157,7 @@ struct lbBranchBlocks {
 struct lbContextData {
 	lbAddr ctx;
 	isize scope_index;
+	isize uses;
 };
 
 enum lbParamPasskind {
@@ -310,7 +311,7 @@ lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection sel);
 lbValue lb_emit_deep_field_ev(lbProcedure *p, lbValue e, Selection sel);
 
 lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type);
-lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *platform_type);
+lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type);
 void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block);
 lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t);
 lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right);

+ 64 - 5
src/llvm_backend_opt.cpp

@@ -1,11 +1,41 @@
+void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level);
+void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimization_level);
+void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPassManagerRef mpm, i32 optimization_level);
+void lb_populate_function_pass_manager_specific(LLVMPassManagerRef fpm, i32 optimization_level);
+
+void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) {
+	LLVMAddPromoteMemoryToRegisterPass(fpm);
+	LLVMAddMergedLoadStoreMotionPass(fpm);
+	LLVMAddConstantPropagationPass(fpm);
+	LLVMAddEarlyCSEPass(fpm);
+
+	LLVMAddConstantPropagationPass(fpm);
+	LLVMAddMergedLoadStoreMotionPass(fpm);
+	LLVMAddPromoteMemoryToRegisterPass(fpm);
+	LLVMAddCFGSimplificationPass(fpm);
+}
+
 void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level) {
 	// NOTE(bill): Treat -opt:3 as if it was -opt:2
 	// TODO(bill): Determine which opt definitions should exist in the first place
 	optimization_level = gb_clamp(optimization_level, 0, 2);
 
-	if (!ignore_memcpy_pass) {
+	if (ignore_memcpy_pass) {
+		lb_basic_populate_function_pass_manager(fpm);
+		return;
+	} else if (optimization_level == 0) {
 		LLVMAddMemCpyOptPass(fpm);
+		lb_basic_populate_function_pass_manager(fpm);
+		return;
 	}
+
+#if 0
+	LLVMPassManagerBuilderRef pmb = LLVMPassManagerBuilderCreate();
+	LLVMPassManagerBuilderSetOptLevel(pmb, optimization_level);
+	LLVMPassManagerBuilderSetSizeLevel(pmb, optimization_level);
+	LLVMPassManagerBuilderPopulateFunctionPassManager(pmb, fpm);
+#else
+	LLVMAddMemCpyOptPass(fpm);
 	LLVMAddPromoteMemoryToRegisterPass(fpm);
 	LLVMAddMergedLoadStoreMotionPass(fpm);
 	LLVMAddConstantPropagationPass(fpm);
@@ -16,16 +46,45 @@ void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcp
 	LLVMAddPromoteMemoryToRegisterPass(fpm);
 	LLVMAddCFGSimplificationPass(fpm);
 
-	// LLVMAddSLPVectorizePass(fpm);
-	// LLVMAddLoopVectorizePass(fpm);
+	LLVMAddSCCPPass(fpm);
+
+	LLVMAddPromoteMemoryToRegisterPass(fpm);
+	LLVMAddUnifyFunctionExitNodesPass(fpm);
+
+	LLVMAddCFGSimplificationPass(fpm);
+	LLVMAddEarlyCSEPass(fpm);
+	LLVMAddLowerExpectIntrinsicPass(fpm);
+#endif
+}
+
+void lb_populate_function_pass_manager_specific(LLVMPassManagerRef fpm, i32 optimization_level) {
+	// NOTE(bill): Treat -opt:3 as if it was -opt:2
+	// TODO(bill): Determine which opt definitions should exist in the first place
+	optimization_level = gb_clamp(optimization_level, 0, 2);
 
-	// LLVMAddScalarizerPass(fpm);
-	// LLVMAddLoopIdiomPass(fpm);
 	if (optimization_level == 0) {
+		LLVMAddMemCpyOptPass(fpm);
+		lb_basic_populate_function_pass_manager(fpm);
 		return;
 	}
 
 #if 1
+	LLVMPassManagerBuilderRef pmb = LLVMPassManagerBuilderCreate();
+	LLVMPassManagerBuilderSetOptLevel(pmb, optimization_level);
+	LLVMPassManagerBuilderSetSizeLevel(pmb, optimization_level);
+	LLVMPassManagerBuilderPopulateFunctionPassManager(pmb, fpm);
+#else
+	LLVMAddMemCpyOptPass(fpm);
+	LLVMAddPromoteMemoryToRegisterPass(fpm);
+	LLVMAddMergedLoadStoreMotionPass(fpm);
+	LLVMAddConstantPropagationPass(fpm);
+	LLVMAddEarlyCSEPass(fpm);
+
+	LLVMAddConstantPropagationPass(fpm);
+	LLVMAddMergedLoadStoreMotionPass(fpm);
+	LLVMAddPromoteMemoryToRegisterPass(fpm);
+	LLVMAddCFGSimplificationPass(fpm);
+
 	LLVMAddSCCPPass(fpm);
 
 	LLVMAddPromoteMemoryToRegisterPass(fpm);

+ 50 - 1
src/main.cpp

@@ -571,6 +571,7 @@ enum BuildFlagKind {
 
 	BuildFlag_OutFile,
 	BuildFlag_OptimizationLevel,
+	BuildFlag_OptimizationMode,
 	BuildFlag_ShowTimings,
 	BuildFlag_ShowUnused,
 	BuildFlag_ShowUnusedWithLocation,
@@ -687,6 +688,8 @@ bool parse_build_flags(Array<String> args) {
 	add_flag(&build_flags, BuildFlag_Help,              str_lit("help"),                BuildFlagParam_None, Command_all);
 	add_flag(&build_flags, BuildFlag_OutFile,           str_lit("out"),                 BuildFlagParam_String, Command__does_build &~ Command_test);
 	add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"),                 BuildFlagParam_Integer, Command__does_build);
+	add_flag(&build_flags, BuildFlag_OptimizationMode,  str_lit("o"),                   BuildFlagParam_String, Command__does_build);
+	add_flag(&build_flags, BuildFlag_OptimizationMode,  str_lit("O"),                   BuildFlagParam_String, Command__does_build);
 	add_flag(&build_flags, BuildFlag_ShowTimings,       str_lit("show-timings"),        BuildFlagParam_None, Command__does_check);
 	add_flag(&build_flags, BuildFlag_ShowMoreTimings,   str_lit("show-more-timings"),   BuildFlagParam_None, Command__does_check);
 	add_flag(&build_flags, BuildFlag_ShowUnused,        str_lit("show-unused"),         BuildFlagParam_None, Command_check);
@@ -885,8 +888,36 @@ bool parse_build_flags(Array<String> args) {
 						}
 						case BuildFlag_OptimizationLevel:
 							GB_ASSERT(value.kind == ExactValue_Integer);
+							if (set_flags[BuildFlag_OptimizationMode]) {
+								gb_printf_err("Mixture of -opt and -o is not allowed\n");
+								bad_flags = true;
+								break;
+							}
 							build_context.optimization_level = cast(i32)big_int_to_i64(&value.value_integer);
 							break;
+						case BuildFlag_OptimizationMode:
+							GB_ASSERT(value.kind == ExactValue_String);
+							if (set_flags[BuildFlag_OptimizationLevel]) {
+								gb_printf_err("Mixture of -opt and -o is not allowed\n");
+								bad_flags = true;
+								break;
+							}
+
+							if (value.value_string == "minimal") {
+								build_context.optimization_level = 0;
+							} else if (value.value_string == "size") {
+								build_context.optimization_level = 1;
+							} else if (value.value_string == "speed") {
+								build_context.optimization_level = 2;
+							} else {
+								gb_printf_err("Invalid optimization mode for -o:<string>, got %.*s\n", LIT(value.value_string));
+								gb_printf_err("Valid optimization modes:\n");
+								gb_printf_err("\tminimal\n");
+								gb_printf_err("\tsize\n");
+								gb_printf_err("\tspeed\n");
+								bad_flags = true;
+							}
+							break;
 						case BuildFlag_ShowTimings:
 							GB_ASSERT(value.kind == ExactValue_Invalid);
 							build_context.show_timings = true;
@@ -1109,8 +1140,16 @@ bool parse_build_flags(Array<String> args) {
 								build_context.build_mode = BuildMode_Executable;
 							} else if (str == "asm" || str == "assembly" || str == "assembler") {
 								build_context.build_mode = BuildMode_Assembly;
+							} else if (str == "llvm" || str == "llvm-ir") {
+								build_context.build_mode = BuildMode_LLVM_IR;
 							} else {
 								gb_printf_err("Unknown build mode '%.*s'\n", LIT(str));
+								gb_printf_err("Valid build modes:\n");
+								gb_printf_err("\tdll, shared\n");
+								gb_printf_err("\tobj, object\n");
+								gb_printf_err("\texe\n");
+								gb_printf_err("\tasm, assembly, assembler\n");
+								gb_printf_err("\tllvm, llvm-ir\n");
 								bad_flags = true;
 								break;
 							}
@@ -1612,6 +1651,10 @@ void print_show_help(String const arg0, String const &command) {
 		print_usage_line(1, "-all-packages");
 		print_usage_line(2, "Generates documentation for all packages used in the current project");
 		print_usage_line(0, "");
+
+		print_usage_line(1, "-doc-format");
+		print_usage_line(2, "Generates documentation as the .odin-doc format (useful for external tooling)");
+		print_usage_line(0, "");
 	}
 
 	if (run_or_build) {
@@ -1621,10 +1664,16 @@ void print_show_help(String const arg0, String const &command) {
 		print_usage_line(0, "");
 
 		print_usage_line(1, "-opt:<integer>");
-		print_usage_line(2, "Set the optimization level for complication");
+		print_usage_line(2, "Set the optimization level for compilation");
 		print_usage_line(2, "Accepted values: 0, 1, 2, 3");
 		print_usage_line(2, "Example: -opt:2");
 		print_usage_line(0, "");
+
+		print_usage_line(1, "-o:<string>");
+		print_usage_line(2, "Set the optimization mode for compilation");
+		print_usage_line(2, "Accepted values: minimal, size, speed");
+		print_usage_line(2, "Example: -o:speed");
+		print_usage_line(0, "");
 	}
 
 	if (check) {

+ 1 - 0
src/parser.cpp

@@ -1813,6 +1813,7 @@ void parse_proc_tags(AstFile *f, u64 *tags) {
 
 		if (false) {}
 		ELSE_IF_ADD_TAG(optional_ok)
+		ELSE_IF_ADD_TAG(optional_second)
 		ELSE_IF_ADD_TAG(require_results)
 		ELSE_IF_ADD_TAG(bounds_check)
 		ELSE_IF_ADD_TAG(no_bounds_check)

+ 2 - 0
src/parser.hpp

@@ -203,6 +203,7 @@ enum ProcTag {
 
 	ProcTag_require_results = 1<<4,
 	ProcTag_optional_ok     = 1<<5,
+	ProcTag_optional_second = 1<<6,
 };
 
 enum ProcCallingConvention {
@@ -338,6 +339,7 @@ AST_KIND(_ExprBegin,  "",  bool) \
 		Token        ellipsis; \
 		ProcInlining inlining; \
 		bool         optional_ok_one; \
+		i32          builtin_id; \
 	}) \
 	AST_KIND(FieldValue,      "field value",              struct { Token eq; Ast *field, *value; }) \
 	AST_KIND(TernaryExpr,     "ternary expression",       struct { Ast *cond, *x, *y; }) \

+ 21 - 24
src/types.cpp

@@ -283,7 +283,6 @@ struct TypeProc {
 	TYPE_KIND(SimdVector, struct {                        \
 		i64   count;                                      \
 		Type *elem;                                       \
-		bool is_x86_mmx;                                  \
 	})                                                    \
 	TYPE_KIND(RelativePointer, struct {                   \
 		Type *pointer_type;                               \
@@ -460,8 +459,8 @@ gb_global Type basic_types[] = {
 	{Type_Basic, {Basic_i64,               BasicFlag_Integer,                          8, STR_LIT("i64")}},
 	{Type_Basic, {Basic_u64,               BasicFlag_Integer | BasicFlag_Unsigned,     8, STR_LIT("u64")}},
 
-	{Type_Basic, {Basic_i128,               BasicFlag_Integer,                        16, STR_LIT("i128")}},
-	{Type_Basic, {Basic_u128,               BasicFlag_Integer | BasicFlag_Unsigned,   16, STR_LIT("u128")}},
+	{Type_Basic, {Basic_i128,              BasicFlag_Integer,                         16, STR_LIT("i128")}},
+	{Type_Basic, {Basic_u128,              BasicFlag_Integer | BasicFlag_Unsigned,    16, STR_LIT("u128")}},
 
 	{Type_Basic, {Basic_rune,              BasicFlag_Integer | BasicFlag_Rune,         4, STR_LIT("rune")}},
 
@@ -679,8 +678,6 @@ gb_global Type *t_source_code_location_ptr       = nullptr;
 gb_global Type *t_map_hash                       = nullptr;
 gb_global Type *t_map_header                     = nullptr;
 
-gb_global Type *t_vector_x86_mmx                 = nullptr;
-
 
 gb_global Type *t_equal_proc  = nullptr;
 gb_global Type *t_hasher_proc = nullptr;
@@ -1012,6 +1009,20 @@ bool is_type_integer(Type *t) {
 	}
 	return false;
 }
+bool is_type_integer_like(Type *t) {
+	t = core_type(t);
+	if (t->kind == Type_Basic) {
+		return (t->Basic.flags & (BasicFlag_Integer|BasicFlag_Boolean)) != 0;
+	}
+	if (t->kind == Type_BitSet) {
+		if (t->BitSet.underlying) {
+			return is_type_integer_like(t->BitSet.underlying);
+		}
+		return true;
+	}
+	return false;
+}
+
 bool is_type_unsigned(Type *t) {
 	t = base_type(t);
 	// t = core_type(t);
@@ -1468,7 +1479,7 @@ Type *integer_endian_type_to_platform_type(Type *t) {
 	if (t->kind == Type_BitSet) {
 		t = bit_set_to_int(t);
 	}
-	GB_ASSERT(t->kind == Type_Basic);
+	GB_ASSERT_MSG(t->kind == Type_Basic, "%s", type_to_string(t));
 
 	switch (t->Basic.kind) {
 	// Endian Specific Types
@@ -2148,12 +2159,8 @@ bool are_types_identical(Type *x, Type *y) {
 
 	case Type_SimdVector:
 		if (y->kind == Type_SimdVector) {
-			if (x->SimdVector.is_x86_mmx == y->SimdVector.is_x86_mmx) {
-				if (x->SimdVector.is_x86_mmx) {
-					return true;
-				} else if (x->SimdVector.count == y->SimdVector.count) {
-					return are_types_identical(x->SimdVector.elem, y->SimdVector.elem);
-				}
+			if (x->SimdVector.count == y->SimdVector.count) {
+				return are_types_identical(x->SimdVector.elem, y->SimdVector.elem);
 			}
 		}
 		break;
@@ -2953,9 +2960,6 @@ i64 type_align_of_internal(Type *t, TypePath *path) {
 	}
 
 	case Type_SimdVector: {
-		if (t->SimdVector.is_x86_mmx) {
-			return 8;
-		}
 		// align of
 		i64 count = t->SimdVector.count;
 		Type *elem = t->SimdVector.elem;
@@ -3219,9 +3223,6 @@ i64 type_size_of_internal(Type *t, TypePath *path) {
 	}
 
 	case Type_SimdVector: {
-		if (t->SimdVector.is_x86_mmx) {
-			return 8;
-		}
 		i64 count = t->SimdVector.count;
 		Type *elem = t->SimdVector.elem;
 		return count * type_size_of_internal(elem, path);
@@ -3656,12 +3657,8 @@ gbString write_type_to_string(gbString str, Type *type) {
 		break;
 
 	case Type_SimdVector:
-		if (type->SimdVector.is_x86_mmx) {
-			return gb_string_appendc(str, "intrinsics.x86_mmx");
-		} else {
-			str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count);
-			str = write_type_to_string(str, type->SimdVector.elem);
-		}
+		str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count);
+		str = write_type_to_string(str, type->SimdVector.elem);
 		break;
 
 	case Type_RelativePointer: