From 9ab71ca0da6a68806ff9825000799e8ddfbfc341 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 4 Nov 2021 00:50:28 +0000 Subject: [PATCH 1/6] Add `ODIN_NO_CRT` global constant --- core/runtime/procs.odin | 37 +++++++++++++ core/runtime/procs_windows_amd64.odin | 14 ++--- examples/demo/demo.odin | 76 +++++++++++++++------------ src/checker.cpp | 1 + src/llvm_backend.cpp | 2 +- 5 files changed, 89 insertions(+), 41 deletions(-) diff --git a/core/runtime/procs.odin b/core/runtime/procs.odin index 09ad25379..7ea6145b9 100644 --- a/core/runtime/procs.odin +++ b/core/runtime/procs.odin @@ -24,6 +24,43 @@ when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" { } return dst + } +} else when ODIN_NO_CRT { + @(link_name="memset") + 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.. 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") + 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 { diff --git a/core/runtime/procs_windows_amd64.odin b/core/runtime/procs_windows_amd64.odin index e5f24e53b..8faed87e5 100644 --- a/core/runtime/procs_windows_amd64.odin +++ b/core/runtime/procs_windows_amd64.odin @@ -19,10 +19,12 @@ windows_trap_type_assertion :: proc "contextless" () -> ! { windows_trap_array_bounds() } -// @private -// @(link_name="_tls_index") -// _tls_index: u32; +when ODIN_NO_CRT { + @private + @(link_name="_tls_index") + _tls_index: u32 -// @private -// @(link_name="_fltused") -// _fltused: i32 = 0x9875; + @private + @(link_name="_fltused") + _fltused: i32 = 0x9875 +} \ No newline at end of file diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index c287e4054..d12df2776 100644 --- a/examples/demo/demo.odin +++ b/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 @@ -2414,39 +2422,39 @@ matrix_type :: proc() { main :: proc() { when true { - the_basics() - control_flow() - named_proc_return_parameters() - explicit_procedure_overloading() - struct_type() - union_type() - using_statement() - implicit_context_system() - parametric_polymorphism() - array_programming() - map_type() - implicit_selector_expression() - partial_switch() - cstring_example() - bit_set_type() - deferred_procedure_associations() - reflection() - quaternions() - unroll_for_statement() - where_clauses() - foreign_system() - ranged_fields_for_array_compound_literals() - deprecated_attribute() - range_statements_with_multiple_return_values() - threading_example() - soa_struct_layout() - constant_literal_expressions() - union_maybe() - explicit_context_definition() - relative_data_types() - or_else_operator() - or_return_operator() - arbitrary_precision_maths() + // the_basics() + // control_flow() + // named_proc_return_parameters() + // explicit_procedure_overloading() + // struct_type() + // union_type() + // using_statement() + // implicit_context_system() + // parametric_polymorphism() + // array_programming() + // map_type() + // implicit_selector_expression() + // partial_switch() + // cstring_example() + // bit_set_type() + // deferred_procedure_associations() + // reflection() + // quaternions() + // unroll_for_statement() + // where_clauses() + // foreign_system() + // ranged_fields_for_array_compound_literals() + // deprecated_attribute() + // range_statements_with_multiple_return_values() + // threading_example() + // soa_struct_layout() + // constant_literal_expressions() + // union_maybe() + // explicit_context_definition() + // relative_data_types() + // or_else_operator() + // or_return_operator() + // arbitrary_precision_maths() matrix_type() } } diff --git a/src/checker.cpp b/src/checker.cpp index f0f463816..92e38a643 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -782,6 +782,7 @@ 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_TEST", bc->command_kind == Command_test); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 730b9c1ba..c0a7b60df 100644 --- a/src/llvm_backend.cpp +++ b/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"); From 1980f32bd6636edf7f8a1ba0d0010f23b5292488 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 4 Nov 2021 00:50:48 +0000 Subject: [PATCH 2/6] Correct demo.odin --- examples/demo/demo.odin | 66 ++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index d12df2776..e43ac2548 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -2422,39 +2422,39 @@ matrix_type :: proc() { main :: proc() { when true { - // the_basics() - // control_flow() - // named_proc_return_parameters() - // explicit_procedure_overloading() - // struct_type() - // union_type() - // using_statement() - // implicit_context_system() - // parametric_polymorphism() - // array_programming() - // map_type() - // implicit_selector_expression() - // partial_switch() - // cstring_example() - // bit_set_type() - // deferred_procedure_associations() - // reflection() - // quaternions() - // unroll_for_statement() - // where_clauses() - // foreign_system() - // ranged_fields_for_array_compound_literals() - // deprecated_attribute() - // range_statements_with_multiple_return_values() - // threading_example() - // soa_struct_layout() - // constant_literal_expressions() - // union_maybe() - // explicit_context_definition() - // relative_data_types() - // or_else_operator() - // or_return_operator() - // arbitrary_precision_maths() + the_basics() + control_flow() + named_proc_return_parameters() + explicit_procedure_overloading() + struct_type() + union_type() + using_statement() + implicit_context_system() + parametric_polymorphism() + array_programming() + map_type() + implicit_selector_expression() + partial_switch() + cstring_example() + bit_set_type() + deferred_procedure_associations() + reflection() + quaternions() + unroll_for_statement() + where_clauses() + foreign_system() + ranged_fields_for_array_compound_literals() + deprecated_attribute() + range_statements_with_multiple_return_values() + threading_example() + soa_struct_layout() + constant_literal_expressions() + union_maybe() + explicit_context_definition() + relative_data_types() + or_else_operator() + or_return_operator() + arbitrary_precision_maths() matrix_type() } } From 3fa7dabaa87e99386e469bd5e4badab23f89aaef Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 4 Nov 2021 11:03:21 +0000 Subject: [PATCH 3/6] Correctly support `-default-to-nil-allocator` for all platforms --- core/runtime/core.odin | 6 +- core/runtime/default_allocators_general.odin | 15 +---- core/runtime/default_allocators_nil.odin | 33 +++++++++-- core/runtime/default_allocators_wasi.odin | 31 +--------- core/runtime/default_allocators_windows.odin | 58 ++++++++++--------- core/runtime/default_temporary_allocator.odin | 17 ++---- core/runtime/procs.odin | 3 + core/runtime/procs_windows_amd64.odin | 10 ++-- 8 files changed, 81 insertions(+), 92 deletions(-) diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 22e23d79e..20e808fb7 100644 --- a/core/runtime/core.odin +++ b/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 diff --git a/core/runtime/default_allocators_general.odin b/core/runtime/default_allocators_general.odin index 6bcfb68ae..5ccd5ceb4 100644 --- a/core/runtime/default_allocators_general.odin +++ b/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" diff --git a/core/runtime/default_allocators_nil.odin b/core/runtime/default_allocators_nil.odin index 5100bc517..ccb4a3381 100644 --- a/core/runtime/default_allocators_nil.odin +++ b/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 +} \ No newline at end of file diff --git a/core/runtime/default_allocators_wasi.odin b/core/runtime/default_allocators_wasi.odin index e2bb7516e..2e475e055 100644 --- a/core/runtime/default_allocators_wasi.odin +++ b/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 diff --git a/core/runtime/default_allocators_windows.odin b/core/runtime/default_allocators_windows.odin index f57f4c86c..9cabbcce8 100644 --- a/core/runtime/default_allocators_windows.odin +++ b/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) +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: + _windows_default_free(old_memory) - case .Free_All: - // NOTE(tetra): Do nothing. + case .Free_All: + // NOTE(tetra): Do nothing. - case .Resize: - data, err = _windows_default_resize(old_memory, old_size, size, alignment) + 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_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, + } } -} +} \ No newline at end of file diff --git a/core/runtime/default_temporary_allocator.odin b/core/runtime/default_temporary_allocator.odin index afe3ee922..3da0a9f21 100644 --- a/core/runtime/default_temporary_allocator.odin +++ b/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, diff --git a/core/runtime/procs.odin b/core/runtime/procs.odin index 7ea6145b9..17703efdf 100644 --- a/core/runtime/procs.odin +++ b/core/runtime/procs.odin @@ -26,6 +26,7 @@ when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" { } } else when ODIN_NO_CRT { + @(export) @(link_name="memset") memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr { if ptr != nil && len != 0 { @@ -38,6 +39,7 @@ when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" { return ptr } + @(export) @(link_name="memmove") memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr { if dst != src { @@ -50,6 +52,7 @@ when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" { return dst } + @(export) @(link_name="memcpy") memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr { if dst != src { diff --git a/core/runtime/procs_windows_amd64.odin b/core/runtime/procs_windows_amd64.odin index 8faed87e5..394df14e6 100644 --- a/core/runtime/procs_windows_amd64.odin +++ b/core/runtime/procs_windows_amd64.odin @@ -20,11 +20,13 @@ windows_trap_type_assertion :: proc "contextless" () -> ! { } when ODIN_NO_CRT { - @private - @(link_name="_tls_index") + @(private, export, link_name="_tls_index") _tls_index: u32 - @private - @(link_name="_fltused") + @(private, export, link_name="_fltused") _fltused: i32 = 0x9875 + + @(private, export, link_name="__chkstk") + __chkstk :: proc "c" (rawptr) { + } } \ No newline at end of file From db2d7a4fdb7c4a18699f7e6365bb1d6a99e69dcc Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 4 Nov 2021 12:36:48 +0100 Subject: [PATCH 4/6] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 96746c20e..10ff54071 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -11,7 +11,7 @@ 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: +* Operating System & Odin Version: * Please paste `odin version` output: ## Expected Behavior From 0d1bc05419602d774b91738b587d9819903f1cfe Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 4 Nov 2021 12:37:24 +0100 Subject: [PATCH 5/6] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 10ff54071..3eac46aca 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -12,7 +12,7 @@ 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 & Odin Version: -* Please paste `odin version` output: +* Please paste `odin report` output: ## Expected Behavior From 6ded538546cca4f1e50a011a64932f7f3c784cc2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 4 Nov 2021 12:40:50 +0000 Subject: [PATCH 6/6] `@(linkage=)` for procedures and variables; `@(require)` for procedures; `package runtime` linkage improvements; Subsequence improvements to `lb_run_remove_unused_function_pass` --- core/runtime/internal.odin | 35 +++++++++--------- core/runtime/internal_linux.odin | 1 - core/runtime/internal_windows.odin | 1 - core/runtime/procs.odin | 13 +++---- core/runtime/procs_wasm32.odin | 3 +- core/runtime/procs_windows_386.odin | 6 ++-- src/check_decl.cpp | 18 +++++++++- src/checker.cpp | 55 +++++++++++++++++++++++++++-- src/checker.hpp | 1 + src/entity.cpp | 6 ++++ src/llvm_backend.cpp | 4 ++- src/llvm_backend.hpp | 4 ++- src/llvm_backend_general.cpp | 13 +++++++ src/llvm_backend_opt.cpp | 32 ++++++----------- src/llvm_backend_proc.cpp | 2 ++ 15 files changed, 138 insertions(+), 56 deletions(-) delete mode 100644 core/runtime/internal_linux.odin delete mode 100644 core/runtime/internal_windows.odin diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index d681e581b..2b8dcd36a 100644 --- a/core/runtime/internal.odin +++ b/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) diff --git a/core/runtime/internal_linux.odin b/core/runtime/internal_linux.odin deleted file mode 100644 index 7ccdf5f69..000000000 --- a/core/runtime/internal_linux.odin +++ /dev/null @@ -1 +0,0 @@ -package runtime diff --git a/core/runtime/internal_windows.odin b/core/runtime/internal_windows.odin deleted file mode 100644 index 7ccdf5f69..000000000 --- a/core/runtime/internal_windows.odin +++ /dev/null @@ -1 +0,0 @@ -package runtime diff --git a/core/runtime/procs.odin b/core/runtime/procs.odin index 17703efdf..d3233c31b 100644 --- a/core/runtime/procs.odin +++ b/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) @@ -26,8 +26,7 @@ when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" { } } else when ODIN_NO_CRT { - @(export) - @(link_name="memset") + @(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) @@ -39,8 +38,7 @@ when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" { return ptr } - @(export) - @(link_name="memmove") + @(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) @@ -52,8 +50,7 @@ when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" { return dst } - @(export) - @(link_name="memcpy") + @(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) diff --git a/core/runtime/procs_wasm32.odin b/core/runtime/procs_wasm32.odin index dbc0dfcb7..cc9715183 100644 --- a/core/runtime/procs_wasm32.odin +++ b/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 } \ No newline at end of file diff --git a/core/runtime/procs_windows_386.odin b/core/runtime/procs_windows_386.odin index 37441ab64..f810197f1 100644 --- a/core/runtime/procs_windows_386.odin +++ b/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 diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 33e85edf7..134dbb35b 100644 --- a/src/check_decl.cpp +++ b/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); } diff --git a/src/checker.cpp b/src/checker.cpp index 92e38a643..591377726 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -783,6 +783,7 @@ void init_universal(void) { 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); @@ -1388,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; + } } } } @@ -2658,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)); @@ -2894,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; @@ -2912,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; @@ -3959,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; diff --git a/src/checker.hpp b/src/checker.hpp index 97631c547..c65d766ba 100644 --- a/src/checker.hpp +++ b/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; diff --git a/src/entity.cpp b/src/entity.cpp index d95c74f22..ef4f7e0fa 100644 --- a/src/entity.cpp +++ b/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, }; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index c0a7b60df..928efbb54 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -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; diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 5fc5dfebf..16c9f752e 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -605,4 +605,6 @@ char const *llvm_linkage_strings[] = { "common linkage", "linker private linkage", "linker private weak linkage" -}; \ No newline at end of file +}; + +#define ODIN_METADATA_REQUIRE "odin-metadata-require", 21 diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index b1c1f924b..5c664f0e0 100644 --- a/src/llvm_backend_general.cpp +++ b/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); + } +} diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 75a377e5b..2648863e2 100644 --- a/src/llvm_backend_opt.cpp +++ b/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; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index ed9aa4b8b..ee5b94cec 100644 --- a/src/llvm_backend_proc.cpp +++ b/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);