Browse Source

Merge branch 'master' into new-matrix-type

gingerBill 3 years ago
parent
commit
b4df51e483

+ 9 - 2
.github/workflows/nightly.yml

@@ -93,7 +93,7 @@ jobs:
       - uses: actions/checkout@v1
       - uses: actions/setup-python@v2
         with:
-          python-version: '3.x'
+          python-version: '3.8.x'
 
       - name: Install B2 CLI
         shell: bash
@@ -127,16 +127,23 @@ jobs:
           BUCKET: ${{ secrets.B2_BUCKET }}
           DAYS_TO_KEEP: ${{ secrets.B2_DAYS_TO_KEEP }}
         run: |
+          echo Authorizing B2 account
           b2 authorize-account "$APPID" "$APPKEY"
-
+          
+          echo Uploading artifcates to B2
           chmod +x ./ci/upload_create_nightly.sh
           ./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/
           ./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/
           ./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/
 
+          echo Deleting old artifacts in B2
           python3 ci/delete_old_binaries.py "$BUCKET" "$DAYS_TO_KEEP"
 
+          echo Creating nightly.json
           python3 ci/create_nightly_json.py "$BUCKET" > nightly.json
+          
+          echo Uploading nightly.json
           b2 upload-file "$BUCKET" nightly.json nightly.json
 
+          echo Clear B2 account info
           b2 clear-account

+ 2 - 0
core/log/multi_logger.odin

@@ -13,6 +13,8 @@ create_multi_logger :: proc(logs: ..Logger) -> Logger {
 }
 
 destroy_multi_logger :: proc(log : ^Logger) {
+	data := (^Multi_Logger_Data)(log.data)
+	delete(data.loggers)
 	free(log.data)
 	log^ = nil_logger()
 }

+ 2 - 2
core/runtime/default_allocators_nil.odin

@@ -6,12 +6,12 @@ package runtime
 default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
                                size, alignment: int,
                                old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
-	return nil, .None;
+	return nil, .None
 }
 
 default_allocator :: proc() -> Allocator {
 	return Allocator{
 		procedure = default_allocator_proc,
 		data = nil,
-	};
+	}
 }

+ 71 - 11
core/runtime/internal.odin

@@ -177,8 +177,70 @@ mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment
 	new_ptr = raw_data(new_data)
 	return
 }
-memory_equal :: proc "contextless" (a, b: rawptr, n: int) -> bool {
-	return memory_compare(a, b, n) == 0
+memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
+	switch {
+	case n == 0: return true
+	case x == y: return true
+	}
+	
+	a, b := ([^]byte)(x), ([^]byte)(y)
+	length := uint(n)
+	
+	when size_of(uint) == 8 {
+		if word_length := length >> 3; word_length != 0 {
+			for i in 0..<word_length {
+				if intrinsics.unaligned_load((^u64)(a)) != intrinsics.unaligned_load((^u64)(b)) {
+					return false
+				}
+				a = a[size_of(u64):]
+				b = b[size_of(u64):]
+			}
+		}
+		
+		if length & 4 != 0 {
+			if intrinsics.unaligned_load((^u32)(a)) != intrinsics.unaligned_load((^u32)(b)) {
+				return false
+			}
+			a = a[size_of(u32):]
+			b = b[size_of(u32):]
+		}
+		
+		if length & 2 != 0 {
+			if intrinsics.unaligned_load((^u16)(a)) != intrinsics.unaligned_load((^u16)(b)) {
+				return false
+			}
+			a = a[size_of(u16):]
+			b = b[size_of(u16):]
+		}
+		
+		if length & 1 != 0 && a[0] != b[0] {
+			return false	
+		}
+		return true
+	} else {
+		if word_length := length >> 2; word_length != 0 {
+			for i in 0..<word_length {
+				if intrinsics.unaligned_load((^u32)(a)) != intrinsics.unaligned_load((^u32)(b)) {
+					return false
+				}
+				a = a[size_of(u32):]
+				b = b[size_of(u32):]
+			}
+		}
+		
+		length &= 3
+		
+		if length != 0 {
+			for i in 0..<length {
+				if a[i] != b[i] {
+					return false
+				}
+			}
+		}
+
+		return true
+	}
+
 }
 memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check {
 	switch {
@@ -258,15 +320,13 @@ memory_compare_zero :: proc "contextless" (a: rawptr, n: int) -> int #no_bounds_
 	return 0
 }
 
-string_eq :: proc "contextless" (a, b: string) -> bool {
-	x := transmute(Raw_String)a
-	y := transmute(Raw_String)b
-	switch {
-	case x.len != y.len: return false
-	case x.len == 0:      return true
-	case x.data == y.data:   return true
+string_eq :: proc "contextless" (lhs, rhs: string) -> bool {
+	x := transmute(Raw_String)lhs
+	y := transmute(Raw_String)rhs
+	if x.len != y.len {
+		return false
 	}
-	return string_cmp(a, b) == 0
+	return #force_inline memory_equal(x.data, y.data, x.len)
 }
 
 string_cmp :: proc "contextless" (a, b: string) -> int {
@@ -708,7 +768,7 @@ floattidf :: proc "c" (a: i128) -> f64 {
 		a += 1
 		a >>= 2
 
-		if a & (1 << DBL_MANT_DIG) != 0 {
+		if a & (i128(1) << DBL_MANT_DIG) != 0 {
 			a >>= 1
 			e += 1
 		}

+ 1 - 1
core/runtime/os_specific_freestanding.odin

@@ -3,5 +3,5 @@ package runtime
 
 // TODO(bill): reimplement `os.write`
 _os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
-	return 0, -1;
+	return 0, -1
 }

+ 4 - 0
src/check_builtin.cpp

@@ -2749,6 +2749,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 	case BuiltinProc_volatile_store:
 		/*fallthrough*/
+	case BuiltinProc_unaligned_store:
+		/*fallthrough*/
 	case BuiltinProc_atomic_store:
 	case BuiltinProc_atomic_store_rel:
 	case BuiltinProc_atomic_store_relaxed:
@@ -2770,6 +2772,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 	case BuiltinProc_volatile_load:
 		/*fallthrough*/
+	case BuiltinProc_unaligned_load:
+		/*fallthrough*/
 	case BuiltinProc_atomic_load:
 	case BuiltinProc_atomic_load_acq:
 	case BuiltinProc_atomic_load_relaxed:

+ 21 - 9
src/check_expr.cpp

@@ -837,7 +837,7 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co
 			operand->mode = Addressing_Invalid;
 		}
 
-
+		convert_to_typed(c, operand, type);
 		return;
 	}
 
@@ -3208,6 +3208,10 @@ void update_untyped_expr_type(CheckerContext *c, Ast *e, Type *type, bool final)
 		if (type != nullptr && type != t_invalid) {
 			if (e->tav.type == nullptr || e->tav.type == t_invalid) {
 				add_type_and_value(c->info, e, e->tav.mode, type ? type : e->tav.type, e->tav.value);
+				if (e->kind == Ast_TernaryIfExpr) {
+					update_untyped_expr_type(c, e->TernaryIfExpr.x, type, final);
+					update_untyped_expr_type(c, e->TernaryIfExpr.y, type, final);
+				}
 			}
 		}
 		return;
@@ -3414,9 +3418,9 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
 
 			case Basic_UntypedNil:
 				if (is_type_any(target_type)) {
-					target_type = t_untyped_nil;
+					// target_type = t_untyped_nil;
 				} else if (is_type_cstring(target_type)) {
-					target_type = t_untyped_nil;
+					// target_type = t_untyped_nil;
 				} else if (!type_has_nil(target_type)) {
 					operand->mode = Addressing_Invalid;
 					convert_untyped_error(c, operand, target_type);
@@ -3587,6 +3591,14 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
 		}
 		break;
 	}
+	
+	if (is_type_any(target_type) && is_type_untyped(operand->type)) {
+		if (is_type_untyped_nil(operand->type) && is_type_untyped_undef(operand->type)) {
+				
+		} else {
+			target_type = default_type(operand->type);
+		}
+	}
 
 	update_untyped_expr_type(c, operand->expr, target_type, true);
 	operand->type = target_type;
@@ -7030,18 +7042,18 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 			return kind;
 		}
 
-		Type *type = x.type;
-		if (is_type_untyped_nil(type) || is_type_untyped_undef(type)) {
-			type = y.type;
+		o->type = x.type;
+		if (is_type_untyped_nil(o->type) || is_type_untyped_undef(o->type)) {
+			o->type = y.type;
 		}
 
-		o->type = type;
 		o->mode = Addressing_Value;
-		if (type_hint != nullptr && is_type_untyped(type)) {
+		o->expr = node;
+		if (type_hint != nullptr && is_type_untyped(o->type)) {
 			if (check_cast_internal(c, &x, type_hint) &&
 			    check_cast_internal(c, &y, type_hint)) {
+				convert_to_typed(c, o, type_hint);
 				update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint));
-				o->type = type_hint;
 			}
 		}
 	case_end;

+ 11 - 2
src/checker.cpp

@@ -1111,9 +1111,12 @@ void check_set_expr_info(CheckerContext *c, Ast *expr, AddressingMode mode, Type
 void check_remove_expr_info(CheckerContext *c, Ast *e) {
 	if (c->untyped != nullptr) {
 		map_remove(c->untyped, hash_pointer(e));
+		GB_ASSERT(map_get(c->untyped, hash_pointer(e)) == nullptr);
 	} else {
+		auto *untyped = &c->info->global_untyped;
 		mutex_lock(&c->info->global_untyped_mutex);
-		map_remove(&c->info->global_untyped, hash_pointer(e));
+		map_remove(untyped, hash_pointer(e));
+		GB_ASSERT(map_get(untyped, hash_pointer(e)) == nullptr);
 		mutex_unlock(&c->info->global_untyped_mutex);
 	}
 }
@@ -1190,7 +1193,13 @@ void add_type_and_value(CheckerInfo *i, Ast *expr, AddressingMode mode, Type *ty
 	while (prev_expr != expr) {
 		prev_expr = expr;
 		expr->tav.mode = mode;
-		expr->tav.type = type;
+		if (type != nullptr && expr->tav.type != nullptr && 
+		    is_type_any(type) && is_type_untyped(expr->tav.type)) {
+			// ignore
+		} else {
+			expr->tav.type = type;
+		}
+
 		if (mode == Addressing_Constant || mode == Addressing_Invalid) {
 			expr->tav.value = value;
 		} else if (mode == Addressing_Value && is_type_typeid(type)) {

+ 6 - 0
src/checker_builtin_procs.hpp

@@ -77,6 +77,9 @@ enum BuiltinProcId {
 	BuiltinProc_volatile_store,
 	BuiltinProc_volatile_load,
 	
+	BuiltinProc_unaligned_store,
+	BuiltinProc_unaligned_load,
+	
 	BuiltinProc_prefetch_read_instruction,
 	BuiltinProc_prefetch_read_data,
 	BuiltinProc_prefetch_write_instruction,
@@ -326,6 +329,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("volatile_store"),  2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
 	{STR_LIT("volatile_load"),   1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	
+	{STR_LIT("unaligned_store"),  2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+	{STR_LIT("unaligned_load"),   1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	
 	{STR_LIT("prefetch_read_instruction"),  2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
 	{STR_LIT("prefetch_read_data"),         2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
 	{STR_LIT("prefetch_write_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},

+ 1 - 1
src/llvm_backend_const.cpp

@@ -329,7 +329,7 @@ LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, BigInt const *
 		debug_print_big_int(a);
 		gb_printf_err("%s -> %tu\n", type_to_string(original_type), sz);;
 	}
-	GB_ASSERT_MSG(sz >= max_count, "max_count: %tu, sz: %tu, written: %tu", max_count, sz, written);
+	GB_ASSERT_MSG(sz >= max_count, "max_count: %tu, sz: %tu, written: %tu, type %s", max_count, sz, written, type_to_string(original_type));
 	GB_ASSERT(gb_size_of(rop64) >= sz);
 
 	mp_err err = mp_pack(rop, sz, &written,

+ 13 - 13
src/llvm_backend_expr.cpp

@@ -2806,17 +2806,18 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
 
 	TokenPos expr_pos = ast_token(expr).pos;
 	TypeAndValue tv = type_and_value_of_expr(expr);
+	Type *type = type_of_expr(expr);
 	GB_ASSERT_MSG(tv.mode != Addressing_Invalid, "invalid expression '%s' (tv.mode = %d, tv.type = %s) @ %s\n Current Proc: %.*s : %s", expr_to_string(expr), tv.mode, type_to_string(tv.type), token_pos_to_string(expr_pos), LIT(p->name), type_to_string(p->type));
 
 	if (tv.value.kind != ExactValue_Invalid) {
 		// NOTE(bill): The commented out code below is just for debug purposes only
-		// GB_ASSERT_MSG(!is_type_untyped(tv.type), "%s @ %s\n%s", type_to_string(tv.type), token_pos_to_string(expr_pos), expr_to_string(expr));
-		// if (is_type_untyped(tv.type)) {
-		// 	gb_printf_err("%s %s\n", token_pos_to_string(expr_pos), expr_to_string(expr));
+		// if (is_type_untyped(type)) {
+		// 	gb_printf_err("%s %s : %s @ %p\n", token_pos_to_string(expr_pos), expr_to_string(expr), type_to_string(expr->tav.type), expr);
+		// 	GB_PANIC("%s\n", type_to_string(tv.type));
 		// }
 
 		// NOTE(bill): Short on constant values
-		return lb_const_value(p->module, tv.type, tv.value);
+		return lb_const_value(p->module, type, tv.value);
 	}
 
 	#if 0
@@ -2847,12 +2848,12 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
 
 	case_ast_node(u, Undef, expr)
 		lbValue res = {};
-		if (is_type_untyped(tv.type)) {
+		if (is_type_untyped(type)) {
 			res.value = nullptr;
 			res.type  = t_untyped_undef;
 		} else {
-			res.value = LLVMGetUndef(lb_type(m, tv.type));
-			res.type  = tv.type;
+			res.value = LLVMGetUndef(lb_type(m, type));
+			res.type  = type;
 		}
 		return res;
 	case_end;
@@ -2893,7 +2894,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
 		TypeAndValue tav = type_and_value_of_expr(expr);
 		GB_ASSERT(tav.mode == Addressing_Constant);
 
-		return lb_const_value(p->module, tv.type, tv.value);
+		return lb_const_value(p->module, type, tv.value);
 	case_end;
 
 	case_ast_node(se, SelectorCallExpr, expr);
@@ -2966,7 +2967,6 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
 
 	case_ast_node(ta, TypeAssertion, expr);
 		TokenPos pos = ast_token(expr).pos;
-		Type *type = tv.type;
 		lbValue e = lb_build_expr(p, ta->expr);
 		Type *t = type_deref(e.type);
 		if (is_type_union(t)) {
@@ -2986,16 +2986,16 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
 		lbValue e = lb_build_expr(p, tc->expr);
 		switch (tc->token.kind) {
 		case Token_cast:
-			return lb_emit_conv(p, e, tv.type);
+			return lb_emit_conv(p, e, type);
 		case Token_transmute:
-			return lb_emit_transmute(p, e, tv.type);
+			return lb_emit_transmute(p, e, type);
 		}
 		GB_PANIC("Invalid AST TypeCast");
 	case_end;
 
 	case_ast_node(ac, AutoCast, expr);
 		lbValue value = lb_build_expr(p, ac->expr);
-		return lb_emit_conv(p, value, tv.type);
+		return lb_emit_conv(p, value, type);
 	case_end;
 
 	case_ast_node(ue, UnaryExpr, expr);
@@ -3005,7 +3005,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
 		default:
 			{
 				lbValue v = lb_build_expr(p, ue->expr);
-				return lb_emit_unary_arith(p, ue->op.kind, v, tv.type);
+				return lb_emit_unary_arith(p, ue->op.kind, v, type);
 			}
 		}
 	case_end;

+ 88 - 50
src/llvm_backend_proc.cpp

@@ -1,3 +1,62 @@
+void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false) {
+	dst = lb_emit_conv(p, dst, t_rawptr);
+	src = lb_emit_conv(p, src, t_rawptr);
+	len = lb_emit_conv(p, len, t_int);
+	
+	char const *name = "llvm.memmove";
+	if (LLVMIsConstant(len.value)) {
+		i64 const_len = cast(i64)LLVMConstIntGetSExtValue(len.value);
+		if (const_len <= 4*build_context.word_size) {
+			name = "llvm.memmove.inline";
+		}
+	}
+
+	LLVMTypeRef types[3] = {
+		lb_type(p->module, t_rawptr),
+		lb_type(p->module, t_rawptr),
+		lb_type(p->module, t_int)
+	};
+	unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+	GB_ASSERT_MSG(id != 0, "Unable to find %s.%s.%s.%s", name, LLVMPrintTypeToString(types[0]), LLVMPrintTypeToString(types[1]), LLVMPrintTypeToString(types[2]));
+	LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+	LLVMValueRef args[4] = {};
+	args[0] = dst.value;
+	args[1] = src.value;
+	args[2] = len.value;
+	args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, is_volatile);
+	LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+}
+void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false) {
+	dst = lb_emit_conv(p, dst, t_rawptr);
+	src = lb_emit_conv(p, src, t_rawptr);
+	len = lb_emit_conv(p, len, t_int);
+	
+	char const *name = "llvm.memcpy";
+	if (LLVMIsConstant(len.value)) {
+		i64 const_len = cast(i64)LLVMConstIntGetSExtValue(len.value);
+		if (const_len <= 4*build_context.word_size) {
+			name = "llvm.memcpy.inline";
+		}
+	}
+
+	LLVMTypeRef types[3] = {
+		lb_type(p->module, t_rawptr),
+		lb_type(p->module, t_rawptr),
+		lb_type(p->module, t_int)
+	};
+	unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+	GB_ASSERT_MSG(id != 0, "Unable to find %s.%s.%s.%s", name, LLVMPrintTypeToString(types[0]), LLVMPrintTypeToString(types[1]), LLVMPrintTypeToString(types[2]));
+	LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+	LLVMValueRef args[4] = {};
+	args[0] = dst.value;
+	args[1] = src.value;
+	args[2] = len.value;
+	args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, is_volatile);
+	LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+}
+
 
 lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) {
 	GB_ASSERT(entity != nullptr);
@@ -1487,61 +1546,21 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 		}
 
 	case BuiltinProc_mem_copy:
+		{
+			lbValue dst = lb_build_expr(p, ce->args[0]);
+			lbValue src = lb_build_expr(p, ce->args[1]);
+			lbValue len = lb_build_expr(p, ce->args[2]);
+			
+			lb_mem_copy_overlapping(p, dst, src, len, false);
+			return {};
+		}
 	case BuiltinProc_mem_copy_non_overlapping:
 		{
 			lbValue dst = lb_build_expr(p, ce->args[0]);
 			lbValue src = lb_build_expr(p, ce->args[1]);
 			lbValue len = lb_build_expr(p, ce->args[2]);
-			dst = lb_emit_conv(p, dst, t_rawptr);
-			src = lb_emit_conv(p, src, t_rawptr);
-			len = lb_emit_conv(p, len, t_int);
-
-			bool is_inlinable = false;
-
-			if (ce->args[2]->tav.mode == Addressing_Constant) {
-				ExactValue ev = exact_value_to_integer(ce->args[2]->tav.value);
-				i64 const_len = exact_value_to_i64(ev);
-				// TODO(bill): Determine when it is better to do the `*.inline` versions
-				if (const_len <= 4*build_context.word_size) {
-					is_inlinable = true;
-				}
-			}
-
-			char const *name = nullptr;
-			switch (id) {
-			case BuiltinProc_mem_copy:
-				if (is_inlinable) {
-					name = "llvm.memmove.inline";
-				} else {
-					name = "llvm.memmove";
-				}
-				break;
-			case BuiltinProc_mem_copy_non_overlapping:
-				if (is_inlinable) {
-					name = "llvm.memcpy.line";
-				} else {
-					name = "llvm.memcpy";
-				}
-				break;
-			}
-
-			LLVMTypeRef types[3] = {
-				lb_type(p->module, t_rawptr),
-				lb_type(p->module, t_rawptr),
-				lb_type(p->module, t_int)
-			};
-			unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
-			GB_ASSERT_MSG(id != 0, "Unable to find %s.%s.%s.%s", name, LLVMPrintTypeToString(types[0]), LLVMPrintTypeToString(types[1]), LLVMPrintTypeToString(types[2]));
-			LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
-
-			LLVMValueRef args[4] = {};
-			args[0] = dst.value;
-			args[1] = src.value;
-			args[2] = len.value;
-			args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, false); // is_volatile parameter
-
-			LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
-
+			
+			lb_mem_copy_non_overlapping(p, dst, src, len, false);
 			return {};
 		}
 
@@ -1647,6 +1666,25 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 		res.type = type_deref(dst.type);
 		return res;
 	}
+	
+	case BuiltinProc_unaligned_store:
+		{
+			lbValue dst = lb_build_expr(p, ce->args[0]);
+			lbValue src = lb_build_expr(p, ce->args[1]);
+			src = lb_address_from_load_or_generate_local(p, src);
+			Type *t = type_deref(dst.type);
+			lb_mem_copy_non_overlapping(p, dst, src, lb_const_int(p->module, t_int, type_size_of(t)), false);
+			return {};
+		}
+	
+	case BuiltinProc_unaligned_load:
+		{
+			lbValue src = lb_build_expr(p, ce->args[0]);
+			Type *t = type_deref(src.type);
+			lbAddr dst = lb_add_local_generated(p, t, false);
+			lb_mem_copy_non_overlapping(p, dst.addr, src, lb_const_int(p->module, t_int, type_size_of(t)), false);
+			return lb_addr_load(p, dst);
+		}
 
 	case BuiltinProc_atomic_add:
 	case BuiltinProc_atomic_add_acq:

+ 6 - 3
src/main.cpp

@@ -632,7 +632,9 @@ enum BuildFlagKind {
 	BuildFlag_IgnoreWarnings,
 	BuildFlag_WarningsAsErrors,
 	BuildFlag_VerboseErrors,
-	BuildFlag_IgnoreLazy, // internal use only
+	
+	// internal use only
+	BuildFlag_InternalIgnoreLazy, 
 
 #if defined(GB_SYSTEM_WINDOWS)
 	BuildFlag_IgnoreVsSearch,
@@ -779,7 +781,8 @@ bool parse_build_flags(Array<String> args) {
 	add_flag(&build_flags, BuildFlag_IgnoreWarnings,   str_lit("ignore-warnings"),    BuildFlagParam_None, Command_all);
 	add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all);
 	add_flag(&build_flags, BuildFlag_VerboseErrors,    str_lit("verbose-errors"),     BuildFlagParam_None, Command_all);
-	add_flag(&build_flags, BuildFlag_IgnoreLazy,       str_lit("ignore-lazy"),        BuildFlagParam_None, Command_all);
+	
+	add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all);
 
 #if defined(GB_SYSTEM_WINDOWS)
 	add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"),  BuildFlagParam_None, Command__does_build);
@@ -1393,7 +1396,7 @@ bool parse_build_flags(Array<String> args) {
 							build_context.show_error_line = true;
 							break;
 
-						case BuildFlag_IgnoreLazy:
+						case BuildFlag_InternalIgnoreLazy:
 							build_context.ignore_lazy = true;
 							break;
 

+ 22 - 18
src/map.cpp

@@ -114,16 +114,17 @@ gb_internal isize map__add_entry(Map<T> *h, HashKey const &key) {
 template <typename T>
 gb_internal MapFindResult map__find(Map<T> *h, HashKey const &key) {
 	MapFindResult fr = {-1, -1, -1};
-	if (h->hashes.count > 0) {
-		fr.hash_index = key.key & (h->hashes.count-1);
-		fr.entry_index = h->hashes.data[fr.hash_index];
-		while (fr.entry_index >= 0) {
-			if (hash_key_equal(h->entries.data[fr.entry_index].key, key)) {
-				return fr;
-			}
-			fr.entry_prev = fr.entry_index;
-			fr.entry_index = h->entries.data[fr.entry_index].next;
+	if (h->hashes.count == 0) {
+		return fr;
+	}
+	fr.hash_index = key.key & (h->hashes.count-1);
+	fr.entry_index = h->hashes.data[fr.hash_index];
+	while (fr.entry_index >= 0) {
+		if (hash_key_equal(h->entries.data[fr.entry_index].key, key)) {
+			return fr;
 		}
+		fr.entry_prev = fr.entry_index;
+		fr.entry_index = h->entries.data[fr.entry_index].next;
 	}
 	return fr;
 }
@@ -131,16 +132,17 @@ gb_internal MapFindResult map__find(Map<T> *h, HashKey const &key) {
 template <typename T>
 gb_internal MapFindResult map__find_from_entry(Map<T> *h, MapEntry<T> *e) {
 	MapFindResult fr = {-1, -1, -1};
-	if (h->hashes.count > 0) {
-		fr.hash_index  = e->key.key & (h->hashes.count-1);
-		fr.entry_index = h->hashes.data[fr.hash_index];
-		while (fr.entry_index >= 0) {
-			if (&h->entries.data[fr.entry_index] == e) {
-				return fr;
-			}
-			fr.entry_prev = fr.entry_index;
-			fr.entry_index = h->entries.data[fr.entry_index].next;
+	if (h->hashes.count == 0) {
+		return fr;
+	}
+	fr.hash_index  = e->key.key & (h->hashes.count-1);
+	fr.entry_index = h->hashes.data[fr.hash_index];
+	while (fr.entry_index >= 0) {
+		if (&h->entries.data[fr.entry_index] == e) {
+			return fr;
 		}
+		fr.entry_prev = fr.entry_index;
+		fr.entry_index = h->entries.data[fr.entry_index].next;
 	}
 	return fr;
 }
@@ -246,6 +248,8 @@ void map__erase(Map<T> *h, MapFindResult const &fr) {
 		return;
 	}
 	h->entries.data[fr.entry_index] = h->entries.data[h->entries.count-1];
+	array_pop(&h->entries);
+	
 	last = map__find(h, h->entries.data[fr.entry_index].key);
 	if (last.entry_prev >= 0) {
 		h->entries.data[last.entry_prev].next = fr.entry_index;

+ 4 - 2
src/parser.cpp

@@ -2572,6 +2572,9 @@ bool is_literal_type(Ast *node) {
 	case Ast_MatrixType:
 	case Ast_CallExpr:
 		return true;
+	case Ast_MultiPointerType:
+		// For better error messages
+		return true;
 	}
 	return false;
 }
@@ -5426,8 +5429,7 @@ bool parse_file(Parser *p, AstFile *f) {
 							// Ignore
 						} else if (f->flags & AstFile_IsTest) {
 							// Ignore
-						} else if (build_context.command_kind == Command_doc &&
-						           f->pkg->kind == Package_Init) {
+						} else if (f->pkg->kind == Package_Init && build_context.command_kind == Command_doc) {
 							// Ignore
 						} else {
 							f->flags |= AstFile_IsLazy;

+ 1 - 1
src/parser.hpp

@@ -28,9 +28,9 @@ enum AddressingMode : u8 {
 };
 
 struct TypeAndValue {
+	Type *         type;
 	AddressingMode mode;
 	bool           is_lhs; // Debug info
-	Type *         type;
 	ExactValue     value;
 };
 

+ 5 - 5
vendor/sdl2/sdl_keyboard.odin

@@ -9,7 +9,7 @@ when ODIN_OS == "freebsd" { foreign import lib "system:SDL2" }
 
 Keysym :: struct {
 	scancode: Scancode, /**< SDL physical key code - see ::SDL_Scancode for details */
-	sym:      KeyCode,  /**< SDL virtual key code - see ::SDL_KeyCode for details */
+	sym:      Keycode,  /**< SDL virtual key code - see ::SDL_Keycode for details */
 	mod:      Keymod,   /**< current key modifiers */
 	unused:   u32,
 }
@@ -19,12 +19,12 @@ Keysym :: struct {
 foreign lib {
 	GetKeyboardFocus        :: proc() -> ^Window ---
 	GetKeyboardState        :: proc(numkeys: ^c.int) -> [^]u8 ---
-	GetKeyFromScancode      :: proc(scancode: Scancode) -> KeyCode ---
-	GetScancodeFromKey      :: proc(key: KeyCode) -> Scancode ---
+	GetKeyFromScancode      :: proc(scancode: Scancode) -> Keycode ---
+	GetScancodeFromKey      :: proc(key: Keycode) -> Scancode ---
 	GetScancodeName         :: proc(scancode: Scancode) -> cstring ---
 	GetScancodeFromName     :: proc(name: cstring) -> Scancode ---
-	GetKeyName              :: proc(key: KeyCode) -> cstring ---
-	GetKeyFromName          :: proc(name: cstring) -> KeyCode ---
+	GetKeyName              :: proc(key: Keycode) -> cstring ---
+	GetKeyFromName          :: proc(name: cstring) -> Keycode ---
 	StartTextInput          :: proc() ---
 	IsTextInputActive       :: proc() -> bool ---
 	StopTextInput           :: proc() ---

+ 3 - 3
vendor/sdl2/sdl_keycode.odin

@@ -2,11 +2,11 @@ package sdl2
 
 
 SCANCODE_MASK :: 1<<30
-SCANCODE_TO_KEYCODE :: #force_inline proc "c" (X: Scancode) -> KeyCode {
-	return KeyCode(i32(X) | SCANCODE_MASK)
+SCANCODE_TO_KEYCODE :: #force_inline proc "c" (X: Scancode) -> Keycode {
+	return Keycode(i32(X) | SCANCODE_MASK)
 }
 
-KeyCode :: enum i32 {
+Keycode :: enum i32 {
 	UNKNOWN = 0,
 
 	RETURN = '\r',