Sfoglia il codice sorgente

Merge branch 'master' into windows-llvm-11.1.0

gingerBill 3 anni fa
parent
commit
4acf2838e0

+ 2 - 2
.github/ISSUE_TEMPLATE/bug_report.md

@@ -11,8 +11,8 @@ assignees: ''
 
 Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.
 
-* Operating System:
-* Please paste `odin version` output:
+* Operating System & Odin Version:
+* Please paste `odin report` output:
 
 ## Expected Behavior
 

+ 4 - 2
core/runtime/core.odin

@@ -506,8 +506,10 @@ __init_context :: proc "contextless" (c: ^Context) {
 
 	c.temp_allocator.procedure = default_temp_allocator_proc
 	c.temp_allocator.data = &global_default_temp_allocator_data
-
-	c.assertion_failure_proc = default_assertion_failure_proc
+	
+	when !ODIN_DISABLE_ASSERT {
+		c.assertion_failure_proc = default_assertion_failure_proc
+	}
 
 	c.logger.procedure = default_logger_proc
 	c.logger.data = nil

+ 2 - 13
core/runtime/default_allocators_general.odin

@@ -5,19 +5,8 @@ package runtime
 
 when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
 	// mem.nil_allocator reimplementation
-
-	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
-	}
-
-	default_allocator :: proc() -> Allocator {
-		return Allocator{
-			procedure = default_allocator_proc,
-			data = nil,
-		}
-	}
+	default_allocator_proc :: nil_allocator_proc
+	default_allocator :: nil_allocator
 } else {
 	// TODO(bill): reimplement these procedures in the os_specific stuff
 	import "core:os"

+ 27 - 6
core/runtime/default_allocators_nil.odin

@@ -1,17 +1,38 @@
-//+build freestanding
 package runtime
 
-// mem.nil_allocator reimplementation
-
-default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
+nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
                                size, alignment: int,
                                old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
+	switch mode {
+	case .Alloc:
+		return nil, .Out_Of_Memory
+	case .Free:
+		return nil, .None
+	case .Free_All:
+		return nil, .Mode_Not_Implemented
+	case .Resize:
+		if size == 0 {
+			return nil, .None
+		}
+		return nil, .Out_Of_Memory
+	case .Query_Features:
+		return nil, .Mode_Not_Implemented
+	case .Query_Info:
+		return nil, .Mode_Not_Implemented
+	}
 	return nil, .None
 }
 
-default_allocator :: proc() -> Allocator {
+nil_allocator :: proc() -> Allocator {
 	return Allocator{
-		procedure = default_allocator_proc,
+		procedure = nil_allocator_proc,
 		data = nil,
 	}
 }
+
+
+
+when ODIN_OS == "freestanding" {
+	default_allocator_proc :: nil_allocator_proc
+	default_allocator :: nil_allocator
+} 

+ 2 - 29
core/runtime/default_allocators_wasi.odin

@@ -1,32 +1,5 @@
 //+build wasi
 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) {
-	switch mode {
-	case .Alloc:
-		return nil, .Out_Of_Memory
-	case .Free:
-		return nil, .None
-	case .Free_All:
-		return nil, .Mode_Not_Implemented
-	case .Resize:
-		if size == 0 {
-			return nil, .None
-		}
-		return nil, .Out_Of_Memory
-	case .Query_Features:
-		return nil, .Mode_Not_Implemented
-	case .Query_Info:
-		return nil, .Mode_Not_Implemented
-	}
-	return nil, .None
-}
-
-default_allocator :: proc() -> Allocator {
-	return Allocator{
-		procedure = default_allocator_proc,
-		data = nil,
-	}
-}
+default_allocator_proc :: nil_allocator_proc
+default_allocator :: nil_allocator

+ 36 - 30
core/runtime/default_allocators_windows.odin

@@ -1,38 +1,44 @@
 //+build windows
 package runtime
 
-default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
-                                size, alignment: int,
-                                old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
-	switch mode {
-	case .Alloc:
-		data, err = _windows_default_alloc(size, alignment)
-
-	case .Free:
-		_windows_default_free(old_memory)
-
-	case .Free_All:
-		// NOTE(tetra): Do nothing.
-
-	case .Resize:
-		data, err = _windows_default_resize(old_memory, old_size, size, alignment)
-
-	case .Query_Features:
-		set := (^Allocator_Mode_Set)(old_memory)
-		if set != nil {
-			set^ = {.Alloc, .Free, .Resize, .Query_Features}
+when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
+	// mem.nil_allocator reimplementation
+	default_allocator_proc :: nil_allocator_proc
+	default_allocator :: nil_allocator
+} else {
+	default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
+	                                size, alignment: int,
+	                                old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
+		switch mode {
+		case .Alloc:
+			data, err = _windows_default_alloc(size, alignment)
+
+		case .Free:
+			_windows_default_free(old_memory)
+
+		case .Free_All:
+			// NOTE(tetra): Do nothing.
+
+		case .Resize:
+			data, err = _windows_default_resize(old_memory, old_size, size, alignment)
+
+		case .Query_Features:
+			set := (^Allocator_Mode_Set)(old_memory)
+			if set != nil {
+				set^ = {.Alloc, .Free, .Resize, .Query_Features}
+			}
+
+		case .Query_Info:
+			// Do nothing
 		}
 
-	case .Query_Info:
-		// Do nothing
+		return
 	}
 
-	return
-}
-
-default_allocator :: proc() -> Allocator {
-	return Allocator{
-		procedure = default_allocator_proc,
-		data = nil,
+	default_allocator :: proc() -> Allocator {
+		return Allocator{
+			procedure = default_allocator_proc,
+			data = nil,
+		}
 	}
-}
+}

+ 5 - 12
core/runtime/default_temporary_allocator.odin

@@ -3,21 +3,14 @@ package runtime
 DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 1<<22)
 
 
-when ODIN_OS == "freestanding" {
-	Default_Temp_Allocator :: struct {
-	}
+when ODIN_OS == "freestanding" || ODIN_DEFAULT_TO_NIL_ALLOCATOR {
+	Default_Temp_Allocator :: struct {}
 	
-	default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {
-	}
+	default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {}
 	
-	default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
-	}
+	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, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
-		return nil, nil		
-	}
+	default_temp_allocator_proc :: nil_allocator_proc
 } else {
 	Default_Temp_Allocator :: struct {
 		data:               []byte,

+ 19 - 16
core/runtime/internal.odin

@@ -2,6 +2,9 @@ package runtime
 
 import "core:intrinsics"
 
+@(private)
+RUNTIME_LINKAGE :: "strong" when ODIN_USE_SEPARATE_MODULES else "internal"
+
 @(private)
 byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte #no_bounds_check {
 	return ([^]byte)(data)[:max(len, 0)]
@@ -646,7 +649,7 @@ quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
 	return quaternion(t0, t1, t2, t3)
 }
 
-@(link_name="__truncsfhf2")
+@(link_name="__truncsfhf2", linkage=RUNTIME_LINKAGE, require)
 truncsfhf2 :: proc "c" (value: f32) -> u16 {
 	v: struct #raw_union { i: u32, f: f32 }
 	i, s, e, m: i32
@@ -704,12 +707,12 @@ truncsfhf2 :: proc "c" (value: f32) -> u16 {
 }
 
 
-@(link_name="__truncdfhf2")
+@(link_name="__truncdfhf2", linkage=RUNTIME_LINKAGE, require)
 truncdfhf2 :: proc "c" (value: f64) -> u16 {
 	return truncsfhf2(f32(value))
 }
 
-@(link_name="__gnu_h2f_ieee")
+@(link_name="__gnu_h2f_ieee", linkage=RUNTIME_LINKAGE, require)
 gnu_h2f_ieee :: proc "c" (value: u16) -> f32 {
 	fp32 :: struct #raw_union { u: u32, f: f32 }
 
@@ -728,19 +731,19 @@ gnu_h2f_ieee :: proc "c" (value: u16) -> f32 {
 }
 
 
-@(link_name="__gnu_f2h_ieee")
+@(link_name="__gnu_f2h_ieee", linkage=RUNTIME_LINKAGE, require)
 gnu_f2h_ieee :: proc "c" (value: f32) -> u16 {
 	return truncsfhf2(value)
 }
 
-@(link_name="__extendhfsf2")
+@(link_name="__extendhfsf2", linkage=RUNTIME_LINKAGE, require)
 extendhfsf2 :: proc "c" (value: u16) -> f32 {
 	return gnu_h2f_ieee(value)
 }
 
 
 
-@(link_name="__floattidf")
+@(link_name="__floattidf", linkage=RUNTIME_LINKAGE, require)
 floattidf :: proc "c" (a: i128) -> f64 {
 	DBL_MANT_DIG :: 53
 	if a == 0 {
@@ -783,7 +786,7 @@ floattidf :: proc "c" (a: i128) -> f64 {
 }
 
 
-@(link_name="__floattidf_unsigned")
+@(link_name="__floattidf_unsigned", linkage=RUNTIME_LINKAGE, require)
 floattidf_unsigned :: proc "c" (a: u128) -> f64 {
 	DBL_MANT_DIG :: 53
 	if a == 0 {
@@ -825,14 +828,14 @@ floattidf_unsigned :: proc "c" (a: u128) -> f64 {
 
 
 
-@(link_name="__fixunsdfti")
+@(link_name="__fixunsdfti", linkage=RUNTIME_LINKAGE, require)
 fixunsdfti :: #force_no_inline proc "c" (a: f64) -> u128 {
 	// TODO(bill): implement `fixunsdfti` correctly
 	x := u64(a)
 	return u128(x)
 }
 
-@(link_name="__fixunsdfdi")
+@(link_name="__fixunsdfdi", linkage=RUNTIME_LINKAGE, require)
 fixunsdfdi :: #force_no_inline proc "c" (a: f64) -> i128 {
 	// TODO(bill): implement `fixunsdfdi` correctly
 	x := i64(a)
@@ -842,7 +845,7 @@ fixunsdfdi :: #force_no_inline proc "c" (a: f64) -> i128 {
 
 
 
-@(link_name="__umodti3")
+@(link_name="__umodti3", linkage=RUNTIME_LINKAGE, require)
 umodti3 :: proc "c" (a, b: u128) -> u128 {
 	r: u128 = ---
 	_ = udivmod128(a, b, &r)
@@ -850,18 +853,18 @@ umodti3 :: proc "c" (a, b: u128) -> u128 {
 }
 
 
-@(link_name="__udivmodti4")
+@(link_name="__udivmodti4", linkage=RUNTIME_LINKAGE, require)
 udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
 	return udivmod128(a, b, rem)
 }
 
-@(link_name="__udivti3")
+@(link_name="__udivti3", linkage=RUNTIME_LINKAGE, require)
 udivti3 :: proc "c" (a, b: u128) -> u128 {
 	return udivmodti4(a, b, nil)
 }
 
 
-@(link_name="__modti3")
+@(link_name="__modti3", linkage=RUNTIME_LINKAGE, require)
 modti3 :: proc "c" (a, b: i128) -> i128 {
 	s_a := a >> (128 - 1)
 	s_b := b >> (128 - 1)
@@ -874,20 +877,20 @@ modti3 :: proc "c" (a, b: i128) -> i128 {
 }
 
 
-@(link_name="__divmodti4")
+@(link_name="__divmodti4", linkage=RUNTIME_LINKAGE, require)
 divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
 	u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem)
 	return transmute(i128)u
 }
 
-@(link_name="__divti3")
+@(link_name="__divti3", linkage=RUNTIME_LINKAGE, require)
 divti3 :: proc "c" (a, b: i128) -> i128 {
 	u := udivmodti4(transmute(u128)a, transmute(u128)b, nil)
 	return transmute(i128)u
 }
 
 
-@(link_name="__fixdfti")
+@(link_name="__fixdfti", linkage=RUNTIME_LINKAGE, require)
 fixdfti :: proc(a: u64) -> i128 {
 	significandBits :: 52
 	typeWidth       :: (size_of(u64)*8)

+ 0 - 1
core/runtime/internal_linux.odin

@@ -1 +0,0 @@
-package runtime

+ 0 - 1
core/runtime/internal_windows.odin

@@ -1 +0,0 @@
-package runtime

+ 39 - 2
core/runtime/procs.odin

@@ -1,7 +1,7 @@
 package runtime
 
 when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" {
-	@(link_name="memset")
+	@(link_name="memset", require)
 	memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
 		if ptr != nil && len != 0 {
 			b := byte(val)
@@ -13,7 +13,7 @@ when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" {
 		return ptr
 	}
 	
-	@(link_name="memmove")
+	@(link_name="memmove", require)
 	memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
 		if dst != src {
 			d, s := ([^]byte)(dst), ([^]byte)(src)
@@ -24,6 +24,43 @@ when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" {
 		}
 		return dst
 		
+	}
+} else when ODIN_NO_CRT {
+	@(link_name="memset", linkage=RUNTIME_LINKAGE, require)
+	memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
+		if ptr != nil && len != 0 {
+			b := byte(val)
+			p := ([^]byte)(ptr)
+			for i in 0..<len {
+				p[i] = b
+			}
+		}
+		return ptr
+	}
+	
+	@(link_name="memmove", linkage=RUNTIME_LINKAGE, require)
+	memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
+		if dst != src {
+			d, s := ([^]byte)(dst), ([^]byte)(src)
+			d_end, s_end := d[len:], s[len:]
+			for i := len-1; i >= 0; i -= 1 {
+				d[i] = s[i]
+			}
+		}
+		return dst
+		
+	}
+	@(link_name="memcpy", linkage=RUNTIME_LINKAGE, require)
+	memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
+		if dst != src {
+			d, s := ([^]byte)(dst), ([^]byte)(src)
+			d_end, s_end := d[len:], s[len:]
+			for i := len-1; i >= 0; i -= 1 {
+				d[i] = s[i]
+			}
+		}
+		return dst
+		
 	}
 } else {
 	memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {

+ 2 - 1
core/runtime/procs_wasm32.odin

@@ -1,7 +1,8 @@
 //+build wasm32
 package runtime
 
-@(link_name="__ashlti3")
+@(link_name="__ashlti3", linkage="strong")
 __ashlti3 :: proc "c" (a: i64, b: i32) -> i64 {
+	// TODO(bill): __ashlti3 on wasm32
 	return a
 }

+ 3 - 3
core/runtime/procs_windows_386.odin

@@ -22,7 +22,7 @@ windows_trap_type_assertion :: proc "contextless" () -> ! {
 	windows_trap_array_bounds()
 }
 
-@(private, require, link_name="_fltused") _fltused: i32 = 0x9875
+@(private, export, link_name="_fltused") _fltused: i32 = 0x9875
 
-@(private, require, link_name="_tls_index") _tls_index: u32
-@(private, require, link_name="_tls_array") _tls_array: u32
+@(private, export, link_name="_tls_index") _tls_index: u32
+@(private, export, link_name="_tls_array") _tls_array: u32

+ 11 - 7
core/runtime/procs_windows_amd64.odin

@@ -19,10 +19,14 @@ windows_trap_type_assertion :: proc "contextless" () -> ! {
 	windows_trap_array_bounds()
 }
 
-// @private
-// @(link_name="_tls_index")
-// _tls_index: u32;
-
-// @private
-// @(link_name="_fltused")
-// _fltused: i32 = 0x9875;
+when ODIN_NO_CRT {
+	@(private, export, link_name="_tls_index")
+	_tls_index: u32
+
+	@(private, export, link_name="_fltused")
+	_fltused: i32 = 0x9875
+	
+	@(private, export, link_name="__chkstk")
+	__chkstk :: proc "c" (rawptr) {
+	}
+}

+ 9 - 1
examples/demo/demo.odin

@@ -2339,10 +2339,18 @@ matrix_type :: proc() {
 		m4 := mat4(m2)
 		assert(m4[2, 2] == 1)
 		assert(m4[3, 3] == 1)
-		fmt.println("m2", m2)
+		fmt.printf("m2 %#v\n", m2)
 		fmt.println("m4", m4)
 		fmt.println("mat2(m4)", mat2(m4))
 		assert(mat2(m4) == m2)
+		
+		b4 := mat4{
+			1, 2, 0, 0,
+			3, 4, 0, 0,
+			5, 0, 6, 0,
+			0, 7, 0, 8,
+		}
+		fmt.println("b4", matrix_flatten(b4))
 	}
 	
 	{ // Casting non-square matrices

+ 17 - 1
src/check_decl.cpp

@@ -730,7 +730,6 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 	if (ac.set_cold) {
 		e->flags |= EntityFlag_Cold;
 	}
-
 	e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode;
 
 
@@ -760,6 +759,22 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 
 	bool is_foreign = e->Procedure.is_foreign;
 	bool is_export  = e->Procedure.is_export;
+	
+	if (ac.linkage.len != 0) {
+		     if (ac.linkage == "internal")  { e->flags |= EntityFlag_CustomLinkage_Internal; }
+		else if (ac.linkage == "strong")    { e->flags |= EntityFlag_CustomLinkage_Strong;   }
+		else if (ac.linkage == "weak")      { e->flags |= EntityFlag_CustomLinkage_Weak;     }
+		else if (ac.linkage == "link_once") { e->flags |= EntityFlag_CustomLinkage_LinkOnce; }
+		
+		if (is_foreign && (e->flags & EntityFlag_CustomLinkage_Internal)) {
+			error(e->token, "A foreign procedure may not have an \"internal\" linkage");
+		}
+	}
+
+	if (ac.require_declaration) {
+		e->flags |= EntityFlag_Require;
+	}
+
 
 	if (e->pkg != nullptr && e->token.string == "main") {
 		if (pt->param_count != 0 ||
@@ -943,6 +958,7 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
 	}
 
 	if (ac.require_declaration) {
+		e->flags |= EntityFlag_Require;
 		mpmc_enqueue(&ctx->info->required_global_variable_queue, e);
 	}
 

+ 54 - 2
src/checker.cpp

@@ -782,6 +782,8 @@ void init_universal(void) {
 	add_global_bool_constant("ODIN_DISABLE_ASSERT",           bc->ODIN_DISABLE_ASSERT);
 	add_global_bool_constant("ODIN_DEFAULT_TO_NIL_ALLOCATOR", bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR);
 	add_global_bool_constant("ODIN_NO_DYNAMIC_LITERALS",      bc->no_dynamic_literals);
+	add_global_bool_constant("ODIN_NO_CRT",                   bc->no_crt);
+	add_global_bool_constant("ODIN_USE_SEPARATE_MODULES",     bc->use_separate_modules);
 	add_global_bool_constant("ODIN_TEST",                     bc->command_kind == Command_test);
 
 
@@ -1387,7 +1389,9 @@ bool could_entity_be_lazy(Entity *e, DeclInfo *d) {
 					return false;
 				} else if (name == "init") {
 					return false;
-				}
+				} else if (name == "linkage") {
+					return false;
+				} 
 			}
 		}
 	}
@@ -2657,6 +2661,32 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 			return false;
 		}
 		return true;
+	} else if (name == "linkage") {
+		ExactValue ev = check_decl_attribute_value(c, value);
+		if (ev.kind != ExactValue_String) {
+			error(value, "Expected either a string 'linkage'");
+			return false;
+		}
+		String linkage = ev.value_string;
+		if (linkage == "internal" ||
+		    linkage == "strong" ||
+		    linkage == "weak" ||
+		    linkage == "link_once") {
+			ac->linkage = linkage;
+		} else {
+			error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage));
+			error_line("\tinternal\n");
+			error_line("\tstrong\n");
+			error_line("\tweak\n");
+			error_line("\tlink_once\n");
+		}
+		return true;
+	} else if (name == "require") {
+		if (value != nullptr) {
+			error(elem, "'require' does not have any parameters");
+		}
+		ac->require_declaration = true;
+		return true;
 	} else if (name == "init") {
 		if (value != nullptr) {
 			error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name));
@@ -2893,7 +2923,7 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
 
 	if (name == "require") {
 		if (value != nullptr) {
-			error(elem, "'static' does not have any parameters");
+			error(elem, "'require' does not have any parameters");
 		}
 		ac->require_declaration = true;
 		return true;
@@ -2911,6 +2941,26 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
 			error(elem, "An exported variable cannot be thread local");
 		}
 		return true;
+	} else if (name == "linkage") {
+		ExactValue ev = check_decl_attribute_value(c, value);
+		if (ev.kind != ExactValue_String) {
+			error(value, "Expected either a string 'linkage'");
+			return false;
+		}
+		String linkage = ev.value_string;
+		if (linkage == "internal" ||
+		    linkage == "strong" ||
+		    linkage == "weak" ||
+		    linkage == "link_once") {
+			ac->linkage = linkage;
+		} else {
+			error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage));
+			error_line("\tinternal\n");
+			error_line("\tstrong\n");
+			error_line("\tweak\n");
+			error_line("\tlink_once\n");
+		}
+		return true;
 	} else if (name == "link_name") {
 		if (ev.kind == ExactValue_String) {
 			ac->link_name = ev.value_string;
@@ -3958,6 +4008,8 @@ DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) {
 	if (name == "force" || name == "require") {
 		if (value != nullptr) {
 			error(elem, "Expected no parameter for '%.*s'", LIT(name));
+		} else if (name == "force") {
+			warning(elem, "'force' is deprecated and is identical to 'require'");
 		}
 		ac->require_declaration = true;
 		return true;

+ 1 - 0
src/checker.hpp

@@ -102,6 +102,7 @@ struct AttributeContext {
 	String  link_name;
 	String  link_prefix;
 	String  link_section;
+	String  linkage;
 	isize   init_expr_list_count;
 	String  thread_local_model;
 	String  deprecated_message;

+ 6 - 0
src/entity.cpp

@@ -76,6 +76,12 @@ enum EntityFlag : u64 {
 	EntityFlag_Init          = 1ull<<31,
 	
 	EntityFlag_CustomLinkName = 1ull<<40,
+	EntityFlag_CustomLinkage_Internal = 1ull<<41,
+	EntityFlag_CustomLinkage_Strong   = 1ull<<42,
+	EntityFlag_CustomLinkage_Weak     = 1ull<<43,
+	EntityFlag_CustomLinkage_LinkOnce = 1ull<<44,
+	
+	EntityFlag_Require = 1ull<<50,
 
 	EntityFlag_Overridden    = 1ull<<63,
 };

+ 4 - 2
src/llvm_backend.cpp

@@ -785,7 +785,7 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
 		params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("fdwReason"),  t_u32,    false, true);
 		params->Tuple.variables[2] = alloc_entity_param(nullptr, make_token_ident("lpReserved"), t_rawptr, false, true);
 		call_cleanup = false;
-	} else if (build_context.metrics.os == TargetOs_windows && build_context.metrics.arch == TargetArch_386) {
+	} else if (build_context.metrics.os == TargetOs_windows && (build_context.metrics.arch == TargetArch_386 || build_context.no_crt)) {
 		name = str_lit("mainCRTStartup");
 	} else if (is_arch_wasm()) {
 		name = str_lit("_start");
@@ -1498,6 +1498,8 @@ void lb_generate_code(lbGenerator *gen) {
 				LLVMSetLinkage(g.value, LLVMInternalLinkage);
 			}
 		}
+		lb_set_linkage_from_entity_flags(m, g.value, e->flags);
+		
 		if (e->Variable.link_section.len > 0) {
 			LLVMSetSection(g.value, alloc_cstring(permanent_allocator(), e->Variable.link_section));
 		}
@@ -1675,7 +1677,7 @@ void lb_generate_code(lbGenerator *gen) {
 	for_array(i, gen->modules.entries) {
 		lbModule *m = gen->modules.entries[i].value;
 		
-		lb_run_remove_unused_function_pass(m->mod);
+		lb_run_remove_unused_function_pass(m);
 
 		auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
 		wd->m = m;

+ 3 - 1
src/llvm_backend.hpp

@@ -605,4 +605,6 @@ char const *llvm_linkage_strings[] = {
 	"common linkage",
 	"linker private linkage",
 	"linker private weak linkage"
-};
+};
+
+#define ODIN_METADATA_REQUIRE "odin-metadata-require", 21

+ 13 - 0
src/llvm_backend_general.cpp

@@ -2692,3 +2692,16 @@ lbAddr lb_add_local_generated_temp(lbProcedure *p, Type *type, i64 min_alignment
 	lb_try_update_alignment(res.addr, cast(unsigned)min_alignment);
 	return res;
 }
+
+
+void lb_set_linkage_from_entity_flags(lbModule *m, LLVMValueRef value, u64 flags) {
+	if (flags & EntityFlag_CustomLinkage_Internal) {
+		LLVMSetLinkage(value, LLVMInternalLinkage);
+	} else if (flags & EntityFlag_CustomLinkage_Strong) {
+		LLVMSetLinkage(value, LLVMExternalLinkage);
+	} else if (flags & EntityFlag_CustomLinkage_Weak) {
+		LLVMSetLinkage(value, LLVMExternalWeakLinkage);
+	} else if (flags & EntityFlag_CustomLinkage_LinkOnce) {
+		LLVMSetLinkage(value, LLVMLinkOnceAnyLinkage);
+	}
+}

+ 11 - 21
src/llvm_backend_opt.cpp

@@ -357,14 +357,14 @@ void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p) {
 }
 
 
-void lb_run_remove_unused_function_pass(LLVMModuleRef mod) {
+void lb_run_remove_unused_function_pass(lbModule *m) {
 	isize removal_count = 0;
 	isize pass_count = 0;
 	isize const max_pass_count = 10;
 	// Custom remove dead function pass
 	for (; pass_count < max_pass_count; pass_count++) {
 		bool was_dead_function = false;	
-		for (LLVMValueRef func = LLVMGetFirstFunction(mod);
+		for (LLVMValueRef func = LLVMGetFirstFunction(m->mod);
 		     func != nullptr;
 		     /**/
 		     ) {
@@ -382,30 +382,20 @@ void lb_run_remove_unused_function_pass(LLVMModuleRef mod) {
 				// Ignore for the time being
 				continue;
 			}
-			
-			if (name == "memset" ||
-			    name == "memmove" ||
-			    name == "memcpy") {
+			LLVMLinkage linkage = LLVMGetLinkage(curr_func);
+			if (linkage != LLVMInternalLinkage) {
 				continue;
 			}
-			if (is_arch_wasm()) {
-				if (name == "__ashlti3") {
-					LLVMSetLinkage(curr_func, LLVMExternalLinkage);
+			
+			Entity **found = map_get(&m->procedure_values, hash_pointer(curr_func));
+			if (found && *found) {
+				Entity *e = *found;
+				bool is_required = (e->flags & EntityFlag_Require) == EntityFlag_Require;
+				if (is_required) {
 					continue;
 				}
 			}
-			
-			LLVMLinkage linkage = LLVMGetLinkage(curr_func);
-						
-			switch (linkage) {
-			case LLVMExternalLinkage:
-			case LLVMDLLImportLinkage:
-			case LLVMDLLExportLinkage:
-			default:
-				continue;
-			case LLVMInternalLinkage:
-				break;
-			}
+
 			LLVMDeleteFunction(curr_func);
 			was_dead_function = true;
 			removal_count += 1;

+ 2 - 0
src/llvm_backend_proc.cpp

@@ -203,6 +203,8 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
 			}
 		}
 	}
+	lb_set_linkage_from_entity_flags(p->module, p->value, entity->flags);
+	
 	
 	if (p->is_foreign) {
 		lb_set_wasm_import_attributes(p->value, entity, p->name);