From 8ffcdf172aa05159cde19d0a660e117d18f3ab12 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 5 May 2026 15:07:42 +0100 Subject: [PATCH] `Odin_Calling_Convention` defined in compiler; Allow for `main :: proc "contextless" () {}` with `-bedrock`; `intrinsics.type_proc_calling_convention` --- base/intrinsics/intrinsics.odin | 4 +++ base/runtime/core.odin | 6 +++- base/runtime/core_builtin.odin | 24 ++++++++------- base/runtime/default_temporary_allocator.odin | 4 +-- src/check_builtin.cpp | 22 ++++++++++++++ src/check_decl.cpp | 22 ++++++++++++-- src/checker.cpp | 30 +++++++++++++++++-- src/checker_builtin_procs.hpp | 4 +++ src/main.cpp | 2 +- src/types.cpp | 3 ++ 10 files changed, 101 insertions(+), 20 deletions(-) diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index dd3b1d38e..cf9b66573 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -214,6 +214,8 @@ type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) --- type_proc_parameter_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) --- type_proc_return_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) --- +type_proc_calling_convention :: proc($T: typeid) -> Odin_Calling_Convention where type_is_proc(T) --- + type_struct_field_count :: proc($T: typeid) -> int where type_is_struct(T) --- type_struct_has_implicit_padding :: proc($T: typeid) -> bool where type_is_struct(T) --- @@ -248,6 +250,8 @@ type_integer_to_signed :: proc($T: typeid) -> type where type_is_integer(T), t type_has_shared_fields :: proc($U, $V: typeid) -> bool where type_is_struct(U), type_is_struct(V) --- + + // Returns the canonicalized name of the type, of which is used to produce the pseudo-unique 'typeid' type_canonical_name :: proc($T: typeid) -> string --- diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 4003850da..09a780d02 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -24,7 +24,9 @@ package runtime import "base:intrinsics" // NOTE(bill): This must match the compiler's -Calling_Convention :: enum u8 { + +/* +enum u8 { Invalid = 0, Odin = 1, Contextless = 2, @@ -44,6 +46,8 @@ Calling_Convention :: enum u8 { Preserve_Most = 12, Preserve_All = 13, } +*/ +Calling_Convention :: type_of(ODIN_DEFAULT_CALLING_CONVENTION) Type_Info_Enum_Value :: distinct i64 diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index 4026ab765..b0b8aa73f 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -2,6 +2,8 @@ package runtime import "base:intrinsics" +MAP_ENABLED :: !ODIN_BEDROCK + @builtin Maybe :: union($T: typeid) {T} @@ -65,7 +67,7 @@ when !NO_DEFAULT_TEMP_ALLOCATOR { // Initializes the global temporary allocator used as the default `context.temp_allocator`. // This is ignored when `NO_DEFAULT_TEMP_ALLOCATOR` is true. @(builtin, disabled=NO_DEFAULT_TEMP_ALLOCATOR) -init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) { +init_global_temporary_allocator :: proc "odin" (size: int, backup_allocator := context.allocator) { when !NO_DEFAULT_TEMP_ALLOCATOR { default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator) } @@ -387,7 +389,7 @@ pop_front_safe :: proc { @builtin clear :: proc{ clear_dynamic_array, - clear_map where !ODIN_BEDROCK, + clear_map where MAP_ENABLED, clear_fixed_capacity_dynamic_array, clear_soa_dynamic_array, @@ -397,7 +399,7 @@ clear :: proc{ @builtin reserve :: proc{ reserve_dynamic_array, - reserve_map where !ODIN_BEDROCK, + reserve_map where MAP_ENABLED, reserve_soa, } @@ -430,7 +432,7 @@ non_zero_resize :: proc{ @builtin shrink :: proc{ shrink_dynamic_array, - shrink_map where !ODIN_BEDROCK, + shrink_map where MAP_ENABLED, } // `free` will try to free the passed pointer, with the given `allocator` if the allocator supports this operation. @@ -481,7 +483,7 @@ delete_cstring16 :: proc(str: cstring16, allocator := context.allocator, loc := return mem_free((^u16)(str), allocator, loc) } -when !ODIN_BEDROCK { +when MAP_ENABLED { // `delete_map` will try to free the underlying data of the passed map, with the given `allocator` if the allocator supports this operation. // // Note: Prefer the procedure group `delete`. @@ -500,7 +502,7 @@ delete :: proc{ delete_cstring, delete_dynamic_array, delete_slice, - delete_map where !ODIN_BEDROCK, + delete_map where MAP_ENABLED, delete_soa_slice, delete_soa_dynamic_array, delete_string16, @@ -599,7 +601,7 @@ _make_dynamic_array_len_cap :: proc(array: ^Raw_Dynamic_Array, size_of_elem, ali return } -when !ODIN_BEDROCK { +when MAP_ENABLED { // `make_map` initializes a map with an allocator. Like `new`, the first argument is a type, not a value. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. // @@ -654,8 +656,8 @@ make :: proc{ make_dynamic_array, make_dynamic_array_len, make_dynamic_array_len_cap, - make_map where !ODIN_BEDROCK, - make_map_cap where !ODIN_BEDROCK, + make_map where MAP_ENABLED, + make_map_cap where MAP_ENABLED, make_multi_pointer, make_soa_slice, @@ -664,7 +666,7 @@ make :: proc{ make_soa_dynamic_array_len_cap, } -when !ODIN_BEDROCK { +when MAP_ENABLED { // `clear_map` will set the length of a passed map to `0` // @@ -1476,7 +1478,7 @@ _shrink_dynamic_array :: proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem return true, nil } -when !ODIN_BEDROCK { +when MAP_ENABLED { @builtin map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) { diff --git a/base/runtime/default_temporary_allocator.odin b/base/runtime/default_temporary_allocator.odin index 2017570bb..857463912 100644 --- a/base/runtime/default_temporary_allocator.odin +++ b/base/runtime/default_temporary_allocator.odin @@ -7,7 +7,7 @@ when NO_DEFAULT_TEMP_ALLOCATOR { // `Default_Temp_Allocator` is a `nil_allocator` when `NO_DEFAULT_TEMP_ALLOCATOR` is `true`. Default_Temp_Allocator :: struct {} - default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) {} + default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator: Allocator) {} default_temp_allocator_destroy :: proc "contextless" (s: ^Default_Temp_Allocator) {} @@ -30,7 +30,7 @@ when NO_DEFAULT_TEMP_ALLOCATOR { arena: Arena, } - default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) { + default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator: Allocator) { _ = arena_init(&s.arena, uint(size), backing_allocator) } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 91aecdc41..674f8214a 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -7699,6 +7699,28 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; + case BuiltinProc_type_proc_calling_convention: + if (operand->mode != Addressing_Type || !is_type_proc(operand->type)) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + return false; + } else { + if (is_type_polymorphic(operand->type)) { + error(operand->expr, "Expected a non-polymorphic procedure type for '%.*s'", LIT(builtin_name)); + return false; + } + + Type *pt = base_type(operand->type); + GB_ASSERT(pt->kind == Type_Proc); + ProcCallingConvention cc = pt->Proc.calling_convention; + + operand->mode = Addressing_Constant; + operand->type = t_odin_calling_convention; + operand->value = exact_value_i64(cc); + } + + break; + + case BuiltinProc_type_polymorphic_record_parameter_count: operand->value = exact_value_i64(0); if (operand->mode != Addressing_Type) { diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 731d52649..65d8a1491 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1527,10 +1527,26 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { error(e->token, "Procedure type of 'main' was expected to be 'proc()', got %s", str); gb_string_free(str); } - if (pt->calling_convention != default_calling_convention()) { - error(e->token, "Procedure 'main' cannot have a custom calling convention"); + if (build_context.bedrock) { + switch (pt->calling_convention) { + case ProcCC_Odin: + case ProcCC_Contextless: + // Okay + break; + default: + error(e->token, "Procedure 'main' cannot have a custom calling convention beyond \"odin\" and \"contextless\" with '-bedrock'"); + pt->calling_convention = ProcCC_Odin; + break; + } + + } else { + if (pt->calling_convention != default_calling_convention()) { + error(e->token, "Procedure 'main' cannot have a custom calling convention"); + } + pt->calling_convention = default_calling_convention(); + } - pt->calling_convention = default_calling_convention(); + if (e->pkg->kind == Package_Init) { if (ctx->info->entry_point != nullptr) { error(e->token, "Redeclaration of the entry pointer procedure 'main'"); diff --git a/src/checker.cpp b/src/checker.cpp index 9193bffaf..3b7fb9f06 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1042,14 +1042,14 @@ struct GlobalEnumValue { i64 value; }; -gb_internal Slice add_global_enum_type(String const &type_name, GlobalEnumValue *values, isize value_count, Type **enum_type_ = nullptr) { +gb_internal Slice add_global_enum_type(String const &type_name, GlobalEnumValue *values, isize value_count, Type **enum_type_ = nullptr, Type *base_type = t_int) { Scope *scope = create_scope(nullptr, builtin_pkg->scope); Entity *entity = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved); Type *enum_type = alloc_type_enum(); Type *named_type = alloc_type_named(type_name, enum_type, entity); set_base_type(named_type, enum_type); - enum_type->Enum.base_type = t_int; + enum_type->Enum.base_type = base_type; enum_type->Enum.scope = scope; entity->type = named_type; @@ -1276,6 +1276,32 @@ gb_internal void init_universal(void) { scope_insert(intrinsics_pkg->scope, t_atomic_memory_order->Named.type_name); } + { + GlobalEnumValue values[ProcCC_MAX] = { + {"Invalid", ProcCC_Invalid}, + {"Odin", ProcCC_Odin}, + {"Contextless", ProcCC_Contextless}, + {"CDecl", ProcCC_CDecl}, + {"Std_Call", ProcCC_StdCall}, + {"Fast_Call", ProcCC_FastCall}, + + {"None", ProcCC_None}, + {"Naked", ProcCC_Naked}, + + {"_", ProcCC_InlineAsm}, + + {"Win64", ProcCC_Win64}, + {"SysV", ProcCC_SysV}, + + {"PreserveNone", ProcCC_PreserveNone}, + {"PreserveMost", ProcCC_PreserveMost}, + {"PreserveAll", ProcCC_PreserveAll}, + }; + + auto fields = add_global_enum_type(str_lit("Odin_Calling_Convention"), values, gb_count_of(values), &t_odin_calling_convention, t_u8); + add_global_enum_constant(fields, "ODIN_DEFAULT_CALLING_CONVENTION", default_calling_convention()); + } + { int minimum_os_version = 0; if (build_context.minimum_os_version_string != "") { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 8baa92e58..5bc23e1f3 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -350,6 +350,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc_type_proc_parameter_type, BuiltinProc_type_proc_return_type, + BuiltinProc_type_proc_calling_convention, + BuiltinProc_type_polymorphic_record_parameter_count, BuiltinProc_type_polymorphic_record_parameter_value, @@ -748,6 +750,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_proc_parameter_type"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_proc_return_type"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_proc_calling_convention"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_polymorphic_record_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_polymorphic_record_parameter_value"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/main.cpp b/src/main.cpp index 2482d6791..9dfdd4021 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -632,7 +632,7 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_BuildDiagnostics, str_lit("build-diagnostics"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_Bedrock, str_lit("bedrock"), BuildFlagParam_None, Command__does_check); - add_flag(&build_flags, BuildFlag_DisableNonConstantGlobals, str_lit("disable-non-constant-globals"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_DisableNonConstantGlobals, str_lit("disable-non-constant-globals"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_DisableInitFini, str_lit("disable-init-fini"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_InternalFastISel, str_lit("internal-fast-isel"), BuildFlagParam_None, Command_all); diff --git a/src/types.cpp b/src/types.cpp index f7975838d..20db1b383 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -781,6 +781,9 @@ gb_global Type *t_c_va_list = nullptr; gb_global Type *t_c_va_list_ptr = nullptr; +gb_global Type *t_odin_calling_convention = nullptr; + + enum OdinAtomicMemoryOrder : i32 { OdinAtomicMemoryOrder_relaxed = 0, // unordered OdinAtomicMemoryOrder_consume = 1, // monotonic