浏览代码

Merge branch 'master' into windows-llvm-11.1.0

gingerBill 3 年之前
父节点
当前提交
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.
 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
 ## 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.procedure = default_temp_allocator_proc
 	c.temp_allocator.data = &global_default_temp_allocator_data
 	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.procedure = default_logger_proc
 	c.logger.data = nil
 	c.logger.data = nil

+ 2 - 13
core/runtime/default_allocators_general.odin

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

+ 27 - 6
core/runtime/default_allocators_nil.odin

@@ -1,17 +1,38 @@
-//+build freestanding
 package runtime
 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,
                                size, alignment: int,
                                old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
                                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
 	return nil, .None
 }
 }
 
 
-default_allocator :: proc() -> Allocator {
+nil_allocator :: proc() -> Allocator {
 	return Allocator{
 	return Allocator{
-		procedure = default_allocator_proc,
+		procedure = nil_allocator_proc,
 		data = nil,
 		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
 //+build wasi
 package runtime
 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
 //+build windows
 package runtime
 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)
 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 {
 } else {
 	Default_Temp_Allocator :: struct {
 	Default_Temp_Allocator :: struct {
 		data:               []byte,
 		data:               []byte,

+ 19 - 16
core/runtime/internal.odin

@@ -2,6 +2,9 @@ package runtime
 
 
 import "core:intrinsics"
 import "core:intrinsics"
 
 
+@(private)
+RUNTIME_LINKAGE :: "strong" when ODIN_USE_SEPARATE_MODULES else "internal"
+
 @(private)
 @(private)
 byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte #no_bounds_check {
 byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte #no_bounds_check {
 	return ([^]byte)(data)[:max(len, 0)]
 	return ([^]byte)(data)[:max(len, 0)]
@@ -646,7 +649,7 @@ quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
 	return quaternion(t0, t1, t2, t3)
 	return quaternion(t0, t1, t2, t3)
 }
 }
 
 
-@(link_name="__truncsfhf2")
+@(link_name="__truncsfhf2", linkage=RUNTIME_LINKAGE, require)
 truncsfhf2 :: proc "c" (value: f32) -> u16 {
 truncsfhf2 :: proc "c" (value: f32) -> u16 {
 	v: struct #raw_union { i: u32, f: f32 }
 	v: struct #raw_union { i: u32, f: f32 }
 	i, s, e, m: i32
 	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 {
 truncdfhf2 :: proc "c" (value: f64) -> u16 {
 	return truncsfhf2(f32(value))
 	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 {
 gnu_h2f_ieee :: proc "c" (value: u16) -> f32 {
 	fp32 :: struct #raw_union { u: u32, f: 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 {
 gnu_f2h_ieee :: proc "c" (value: f32) -> u16 {
 	return truncsfhf2(value)
 	return truncsfhf2(value)
 }
 }
 
 
-@(link_name="__extendhfsf2")
+@(link_name="__extendhfsf2", linkage=RUNTIME_LINKAGE, require)
 extendhfsf2 :: proc "c" (value: u16) -> f32 {
 extendhfsf2 :: proc "c" (value: u16) -> f32 {
 	return gnu_h2f_ieee(value)
 	return gnu_h2f_ieee(value)
 }
 }
 
 
 
 
 
 
-@(link_name="__floattidf")
+@(link_name="__floattidf", linkage=RUNTIME_LINKAGE, require)
 floattidf :: proc "c" (a: i128) -> f64 {
 floattidf :: proc "c" (a: i128) -> f64 {
 	DBL_MANT_DIG :: 53
 	DBL_MANT_DIG :: 53
 	if a == 0 {
 	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 {
 floattidf_unsigned :: proc "c" (a: u128) -> f64 {
 	DBL_MANT_DIG :: 53
 	DBL_MANT_DIG :: 53
 	if a == 0 {
 	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 {
 fixunsdfti :: #force_no_inline proc "c" (a: f64) -> u128 {
 	// TODO(bill): implement `fixunsdfti` correctly
 	// TODO(bill): implement `fixunsdfti` correctly
 	x := u64(a)
 	x := u64(a)
 	return u128(x)
 	return u128(x)
 }
 }
 
 
-@(link_name="__fixunsdfdi")
+@(link_name="__fixunsdfdi", linkage=RUNTIME_LINKAGE, require)
 fixunsdfdi :: #force_no_inline proc "c" (a: f64) -> i128 {
 fixunsdfdi :: #force_no_inline proc "c" (a: f64) -> i128 {
 	// TODO(bill): implement `fixunsdfdi` correctly
 	// TODO(bill): implement `fixunsdfdi` correctly
 	x := i64(a)
 	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 {
 umodti3 :: proc "c" (a, b: u128) -> u128 {
 	r: u128 = ---
 	r: u128 = ---
 	_ = udivmod128(a, b, &r)
 	_ = 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 {
 udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
 	return udivmod128(a, b, rem)
 	return udivmod128(a, b, rem)
 }
 }
 
 
-@(link_name="__udivti3")
+@(link_name="__udivti3", linkage=RUNTIME_LINKAGE, require)
 udivti3 :: proc "c" (a, b: u128) -> u128 {
 udivti3 :: proc "c" (a, b: u128) -> u128 {
 	return udivmodti4(a, b, nil)
 	return udivmodti4(a, b, nil)
 }
 }
 
 
 
 
-@(link_name="__modti3")
+@(link_name="__modti3", linkage=RUNTIME_LINKAGE, require)
 modti3 :: proc "c" (a, b: i128) -> i128 {
 modti3 :: proc "c" (a, b: i128) -> i128 {
 	s_a := a >> (128 - 1)
 	s_a := a >> (128 - 1)
 	s_b := b >> (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 {
 divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
 	u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem)
 	u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem)
 	return transmute(i128)u
 	return transmute(i128)u
 }
 }
 
 
-@(link_name="__divti3")
+@(link_name="__divti3", linkage=RUNTIME_LINKAGE, require)
 divti3 :: proc "c" (a, b: i128) -> i128 {
 divti3 :: proc "c" (a, b: i128) -> i128 {
 	u := udivmodti4(transmute(u128)a, transmute(u128)b, nil)
 	u := udivmodti4(transmute(u128)a, transmute(u128)b, nil)
 	return transmute(i128)u
 	return transmute(i128)u
 }
 }
 
 
 
 
-@(link_name="__fixdfti")
+@(link_name="__fixdfti", linkage=RUNTIME_LINKAGE, require)
 fixdfti :: proc(a: u64) -> i128 {
 fixdfti :: proc(a: u64) -> i128 {
 	significandBits :: 52
 	significandBits :: 52
 	typeWidth       :: (size_of(u64)*8)
 	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
 package runtime
 
 
 when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" {
 when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" {
-	@(link_name="memset")
+	@(link_name="memset", require)
 	memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
 	memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
 		if ptr != nil && len != 0 {
 		if ptr != nil && len != 0 {
 			b := byte(val)
 			b := byte(val)
@@ -13,7 +13,7 @@ when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" {
 		return ptr
 		return ptr
 	}
 	}
 	
 	
-	@(link_name="memmove")
+	@(link_name="memmove", require)
 	memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
 	memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
 		if dst != src {
 		if dst != src {
 			d, s := ([^]byte)(dst), ([^]byte)(src)
 			d, s := ([^]byte)(dst), ([^]byte)(src)
@@ -24,6 +24,43 @@ when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" {
 		}
 		}
 		return dst
 		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 {
 } else {
 	memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
 	memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {

+ 2 - 1
core/runtime/procs_wasm32.odin

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

+ 3 - 3
core/runtime/procs_windows_386.odin

@@ -22,7 +22,7 @@ windows_trap_type_assertion :: proc "contextless" () -> ! {
 	windows_trap_array_bounds()
 	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()
 	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)
 		m4 := mat4(m2)
 		assert(m4[2, 2] == 1)
 		assert(m4[2, 2] == 1)
 		assert(m4[3, 3] == 1)
 		assert(m4[3, 3] == 1)
-		fmt.println("m2", m2)
+		fmt.printf("m2 %#v\n", m2)
 		fmt.println("m4", m4)
 		fmt.println("m4", m4)
 		fmt.println("mat2(m4)", mat2(m4))
 		fmt.println("mat2(m4)", mat2(m4))
 		assert(mat2(m4) == m2)
 		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
 	{ // 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) {
 	if (ac.set_cold) {
 		e->flags |= EntityFlag_Cold;
 		e->flags |= EntityFlag_Cold;
 	}
 	}
-
 	e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode;
 	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_foreign = e->Procedure.is_foreign;
 	bool is_export  = e->Procedure.is_export;
 	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 (e->pkg != nullptr && e->token.string == "main") {
 		if (pt->param_count != 0 ||
 		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) {
 	if (ac.require_declaration) {
+		e->flags |= EntityFlag_Require;
 		mpmc_enqueue(&ctx->info->required_global_variable_queue, e);
 		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_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_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_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);
 	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;
 					return false;
 				} else if (name == "init") {
 				} else if (name == "init") {
 					return false;
 					return false;
-				}
+				} else if (name == "linkage") {
+					return false;
+				} 
 			}
 			}
 		}
 		}
 	}
 	}
@@ -2657,6 +2661,32 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 			return false;
 			return false;
 		}
 		}
 		return true;
 		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") {
 	} else if (name == "init") {
 		if (value != nullptr) {
 		if (value != nullptr) {
 			error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name));
 			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 (name == "require") {
 		if (value != nullptr) {
 		if (value != nullptr) {
-			error(elem, "'static' does not have any parameters");
+			error(elem, "'require' does not have any parameters");
 		}
 		}
 		ac->require_declaration = true;
 		ac->require_declaration = true;
 		return true;
 		return true;
@@ -2911,6 +2941,26 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
 			error(elem, "An exported variable cannot be thread local");
 			error(elem, "An exported variable cannot be thread local");
 		}
 		}
 		return true;
 		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") {
 	} else if (name == "link_name") {
 		if (ev.kind == ExactValue_String) {
 		if (ev.kind == ExactValue_String) {
 			ac->link_name = ev.value_string;
 			ac->link_name = ev.value_string;
@@ -3958,6 +4008,8 @@ DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) {
 	if (name == "force" || name == "require") {
 	if (name == "force" || name == "require") {
 		if (value != nullptr) {
 		if (value != nullptr) {
 			error(elem, "Expected no parameter for '%.*s'", LIT(name));
 			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;
 		ac->require_declaration = true;
 		return true;
 		return true;

+ 1 - 0
src/checker.hpp

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

+ 6 - 0
src/entity.cpp

@@ -76,6 +76,12 @@ enum EntityFlag : u64 {
 	EntityFlag_Init          = 1ull<<31,
 	EntityFlag_Init          = 1ull<<31,
 	
 	
 	EntityFlag_CustomLinkName = 1ull<<40,
 	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,
 	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[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);
 		params->Tuple.variables[2] = alloc_entity_param(nullptr, make_token_ident("lpReserved"), t_rawptr, false, true);
 		call_cleanup = false;
 		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");
 		name = str_lit("mainCRTStartup");
 	} else if (is_arch_wasm()) {
 	} else if (is_arch_wasm()) {
 		name = str_lit("_start");
 		name = str_lit("_start");
@@ -1498,6 +1498,8 @@ void lb_generate_code(lbGenerator *gen) {
 				LLVMSetLinkage(g.value, LLVMInternalLinkage);
 				LLVMSetLinkage(g.value, LLVMInternalLinkage);
 			}
 			}
 		}
 		}
+		lb_set_linkage_from_entity_flags(m, g.value, e->flags);
+		
 		if (e->Variable.link_section.len > 0) {
 		if (e->Variable.link_section.len > 0) {
 			LLVMSetSection(g.value, alloc_cstring(permanent_allocator(), e->Variable.link_section));
 			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) {
 	for_array(i, gen->modules.entries) {
 		lbModule *m = gen->modules.entries[i].value;
 		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);
 		auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
 		wd->m = m;
 		wd->m = m;

+ 3 - 1
src/llvm_backend.hpp

@@ -605,4 +605,6 @@ char const *llvm_linkage_strings[] = {
 	"common linkage",
 	"common linkage",
 	"linker private linkage",
 	"linker private linkage",
 	"linker private weak 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);
 	lb_try_update_alignment(res.addr, cast(unsigned)min_alignment);
 	return res;
 	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 removal_count = 0;
 	isize pass_count = 0;
 	isize pass_count = 0;
 	isize const max_pass_count = 10;
 	isize const max_pass_count = 10;
 	// Custom remove dead function pass
 	// Custom remove dead function pass
 	for (; pass_count < max_pass_count; pass_count++) {
 	for (; pass_count < max_pass_count; pass_count++) {
 		bool was_dead_function = false;	
 		bool was_dead_function = false;	
-		for (LLVMValueRef func = LLVMGetFirstFunction(mod);
+		for (LLVMValueRef func = LLVMGetFirstFunction(m->mod);
 		     func != nullptr;
 		     func != nullptr;
 		     /**/
 		     /**/
 		     ) {
 		     ) {
@@ -382,30 +382,20 @@ void lb_run_remove_unused_function_pass(LLVMModuleRef mod) {
 				// Ignore for the time being
 				// Ignore for the time being
 				continue;
 				continue;
 			}
 			}
-			
-			if (name == "memset" ||
-			    name == "memmove" ||
-			    name == "memcpy") {
+			LLVMLinkage linkage = LLVMGetLinkage(curr_func);
+			if (linkage != LLVMInternalLinkage) {
 				continue;
 				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;
 					continue;
 				}
 				}
 			}
 			}
-			
-			LLVMLinkage linkage = LLVMGetLinkage(curr_func);
-						
-			switch (linkage) {
-			case LLVMExternalLinkage:
-			case LLVMDLLImportLinkage:
-			case LLVMDLLExportLinkage:
-			default:
-				continue;
-			case LLVMInternalLinkage:
-				break;
-			}
+
 			LLVMDeleteFunction(curr_func);
 			LLVMDeleteFunction(curr_func);
 			was_dead_function = true;
 			was_dead_function = true;
 			removal_count += 1;
 			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) {
 	if (p->is_foreign) {
 		lb_set_wasm_import_attributes(p->value, entity, p->name);
 		lb_set_wasm_import_attributes(p->value, entity, p->name);