diff options
| author | gingerBill <bill@gingerbill.org> | 2024-01-28 21:05:53 +0000 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2024-01-28 21:05:53 +0000 |
| commit | 09fa1c29cd014b4560b3c79c72db68af20ef8187 (patch) | |
| tree | 45095630fb03a50df20e0249f98879cf27d94397 /base/runtime/core.odin | |
| parent | ddcaa0de5395bfb1a2b004e6a6cb5e2ba1e2eed1 (diff) | |
Move `core:runtime` to `base:runtime`; keep alias around
Diffstat (limited to 'base/runtime/core.odin')
| -rw-r--r-- | base/runtime/core.odin | 681 |
1 files changed, 681 insertions, 0 deletions
diff --git a/base/runtime/core.odin b/base/runtime/core.odin new file mode 100644 index 000000000..740482493 --- /dev/null +++ b/base/runtime/core.odin @@ -0,0 +1,681 @@ +// This is the runtime code required by the compiler +// IMPORTANT NOTE(bill): Do not change the order of any of this data +// The compiler relies upon this _exact_ order +// +// Naming Conventions: +// In general, Ada_Case for types and snake_case for values +// +// Package Name: snake_case (but prefer single word) +// Import Name: snake_case (but prefer single word) +// Types: Ada_Case +// Enum Values: Ada_Case +// Procedures: snake_case +// Local Variables: snake_case +// Constant Variables: SCREAMING_SNAKE_CASE +// +// IMPORTANT NOTE(bill): `type_info_of` cannot be used within a +// #shared_global_scope due to the internals of the compiler. +// This could change at a later date if the all these data structures are +// implemented within the compiler rather than in this "preload" file +// +//+no-instrumentation +package runtime + +import "core:intrinsics" + +// NOTE(bill): This must match the compiler's +Calling_Convention :: enum u8 { + Invalid = 0, + Odin = 1, + Contextless = 2, + CDecl = 3, + Std_Call = 4, + Fast_Call = 5, + + None = 6, + Naked = 7, + + _ = 8, // reserved + + Win64 = 9, + SysV = 10, +} + +Type_Info_Enum_Value :: distinct i64 + +Platform_Endianness :: enum u8 { + Platform = 0, + Little = 1, + Big = 2, +} + +// Procedure type to test whether two values of the same type are equal +Equal_Proc :: distinct proc "contextless" (rawptr, rawptr) -> bool +// Procedure type to hash a value, default seed value is 0 +Hasher_Proc :: distinct proc "contextless" (data: rawptr, seed: uintptr = 0) -> uintptr + +Type_Info_Struct_Soa_Kind :: enum u8 { + None = 0, + Fixed = 1, + Slice = 2, + Dynamic = 3, +} + +// Variant Types +Type_Info_Named :: struct { + name: string, + base: ^Type_Info, + pkg: string, + loc: Source_Code_Location, +} +Type_Info_Integer :: struct {signed: bool, endianness: Platform_Endianness} +Type_Info_Rune :: struct {} +Type_Info_Float :: struct {endianness: Platform_Endianness} +Type_Info_Complex :: struct {} +Type_Info_Quaternion :: struct {} +Type_Info_String :: struct {is_cstring: bool} +Type_Info_Boolean :: struct {} +Type_Info_Any :: struct {} +Type_Info_Type_Id :: struct {} +Type_Info_Pointer :: struct { + elem: ^Type_Info, // nil -> rawptr +} +Type_Info_Multi_Pointer :: struct { + elem: ^Type_Info, +} +Type_Info_Procedure :: struct { + params: ^Type_Info, // Type_Info_Parameters + results: ^Type_Info, // Type_Info_Parameters + variadic: bool, + convention: Calling_Convention, +} +Type_Info_Array :: struct { + elem: ^Type_Info, + elem_size: int, + count: int, +} +Type_Info_Enumerated_Array :: struct { + elem: ^Type_Info, + index: ^Type_Info, + elem_size: int, + count: int, + min_value: Type_Info_Enum_Value, + max_value: Type_Info_Enum_Value, + is_sparse: bool, +} +Type_Info_Dynamic_Array :: struct {elem: ^Type_Info, elem_size: int} +Type_Info_Slice :: struct {elem: ^Type_Info, elem_size: int} + +Type_Info_Parameters :: struct { // Only used for procedures parameters and results + types: []^Type_Info, + names: []string, +} +Type_Info_Tuple :: Type_Info_Parameters // Will be removed eventually + +Type_Info_Struct :: struct { + types: []^Type_Info, + names: []string, + offsets: []uintptr, + usings: []bool, + tags: []string, + is_packed: bool, + is_raw_union: bool, + is_no_copy: bool, + custom_align: bool, + + equal: Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set + + // These are only set iff this structure is an SOA structure + soa_kind: Type_Info_Struct_Soa_Kind, + soa_base_type: ^Type_Info, + soa_len: int, +} +Type_Info_Union :: struct { + variants: []^Type_Info, + tag_offset: uintptr, + tag_type: ^Type_Info, + + equal: Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set + + custom_align: bool, + no_nil: bool, + shared_nil: bool, +} +Type_Info_Enum :: struct { + base: ^Type_Info, + names: []string, + values: []Type_Info_Enum_Value, +} +Type_Info_Map :: struct { + key: ^Type_Info, + value: ^Type_Info, + map_info: ^Map_Info, +} +Type_Info_Bit_Set :: struct { + elem: ^Type_Info, + underlying: ^Type_Info, // Possibly nil + lower: i64, + upper: i64, +} +Type_Info_Simd_Vector :: struct { + elem: ^Type_Info, + elem_size: int, + count: int, +} +Type_Info_Relative_Pointer :: struct { + pointer: ^Type_Info, // ^T + base_integer: ^Type_Info, +} +Type_Info_Relative_Multi_Pointer :: struct { + pointer: ^Type_Info, // [^]T + base_integer: ^Type_Info, +} +Type_Info_Matrix :: struct { + elem: ^Type_Info, + elem_size: int, + elem_stride: int, // elem_stride >= row_count + row_count: int, + column_count: int, + // Total element count = column_count * elem_stride +} +Type_Info_Soa_Pointer :: struct { + elem: ^Type_Info, +} + +Type_Info_Flag :: enum u8 { + Comparable = 0, + Simple_Compare = 1, +} +Type_Info_Flags :: distinct bit_set[Type_Info_Flag; u32] + +Type_Info :: struct { + size: int, + align: int, + flags: Type_Info_Flags, + id: typeid, + + variant: union { + Type_Info_Named, + Type_Info_Integer, + Type_Info_Rune, + Type_Info_Float, + Type_Info_Complex, + Type_Info_Quaternion, + Type_Info_String, + Type_Info_Boolean, + Type_Info_Any, + Type_Info_Type_Id, + Type_Info_Pointer, + Type_Info_Multi_Pointer, + Type_Info_Procedure, + Type_Info_Array, + Type_Info_Enumerated_Array, + Type_Info_Dynamic_Array, + Type_Info_Slice, + Type_Info_Parameters, + Type_Info_Struct, + Type_Info_Union, + Type_Info_Enum, + Type_Info_Map, + Type_Info_Bit_Set, + Type_Info_Simd_Vector, + Type_Info_Relative_Pointer, + Type_Info_Relative_Multi_Pointer, + Type_Info_Matrix, + Type_Info_Soa_Pointer, + }, +} + +// NOTE(bill): This must match the compiler's +Typeid_Kind :: enum u8 { + Invalid, + Integer, + Rune, + Float, + Complex, + Quaternion, + String, + Boolean, + Any, + Type_Id, + Pointer, + Multi_Pointer, + Procedure, + Array, + Enumerated_Array, + Dynamic_Array, + Slice, + Tuple, + Struct, + Union, + Enum, + Map, + Bit_Set, + Simd_Vector, + Relative_Pointer, + Relative_Multi_Pointer, + Matrix, + Soa_Pointer, +} +#assert(len(Typeid_Kind) < 32) + +// Typeid_Bit_Field :: bit_field #align(align_of(uintptr)) { +// index: 8*size_of(uintptr) - 8, +// kind: 5, // Typeid_Kind +// named: 1, +// special: 1, // signed, cstring, etc +// reserved: 1, +// } +// #assert(size_of(Typeid_Bit_Field) == size_of(uintptr)); + +// NOTE(bill): only the ones that are needed (not all types) +// This will be set by the compiler +type_table: []Type_Info + +args__: []cstring + +when ODIN_OS == .Windows { + // NOTE(Jeroen): If we're a Windows DLL, fwdReason will be populated. + // This tells a DLL if it's first loaded, about to be unloaded, or a thread is joining/exiting. + + DLL_Forward_Reason :: enum u32 { + Process_Detach = 0, // About to unload DLL + Process_Attach = 1, // Entry point + Thread_Attach = 2, + Thread_Detach = 3, + } + dll_forward_reason: DLL_Forward_Reason +} + +// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it) + + +Source_Code_Location :: struct { + file_path: string, + line, column: i32, + procedure: string, +} + +Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location) -> ! + +// Allocation Stuff +Allocator_Mode :: enum byte { + Alloc, + Free, + Free_All, + Resize, + Query_Features, + Query_Info, + Alloc_Non_Zeroed, + Resize_Non_Zeroed, +} + +Allocator_Mode_Set :: distinct bit_set[Allocator_Mode] + +Allocator_Query_Info :: struct { + pointer: rawptr, + size: Maybe(int), + alignment: Maybe(int), +} + +Allocator_Error :: enum byte { + None = 0, + Out_Of_Memory = 1, + Invalid_Pointer = 2, + Invalid_Argument = 3, + Mode_Not_Implemented = 4, +} + +Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, + location: Source_Code_Location = #caller_location) -> ([]byte, Allocator_Error) +Allocator :: struct { + procedure: Allocator_Proc, + data: rawptr, +} + +Byte :: 1 +Kilobyte :: 1024 * Byte +Megabyte :: 1024 * Kilobyte +Gigabyte :: 1024 * Megabyte +Terabyte :: 1024 * Gigabyte +Petabyte :: 1024 * Terabyte +Exabyte :: 1024 * Petabyte + +// Logging stuff + +Logger_Level :: enum uint { + Debug = 0, + Info = 10, + Warning = 20, + Error = 30, + Fatal = 40, +} + +Logger_Option :: enum { + Level, + Date, + Time, + Short_File_Path, + Long_File_Path, + Line, + Procedure, + Terminal_Color, + Thread_Id, +} + +Logger_Options :: bit_set[Logger_Option] +Logger_Proc :: #type proc(data: rawptr, level: Logger_Level, text: string, options: Logger_Options, location := #caller_location) + +Logger :: struct { + procedure: Logger_Proc, + data: rawptr, + lowest_level: Logger_Level, + options: Logger_Options, +} + +Context :: struct { + allocator: Allocator, + temp_allocator: Allocator, + assertion_failure_proc: Assertion_Failure_Proc, + logger: Logger, + + user_ptr: rawptr, + user_index: int, + + // Internal use only + _internal: rawptr, +} + + +Raw_String :: struct { + data: [^]byte, + len: int, +} + +Raw_Slice :: struct { + data: rawptr, + len: int, +} + +Raw_Dynamic_Array :: struct { + data: rawptr, + len: int, + cap: int, + allocator: Allocator, +} + +// The raw, type-erased representation of a map. +// +// 32-bytes on 64-bit +// 16-bytes on 32-bit +Raw_Map :: struct { + // A single allocation spanning all keys, values, and hashes. + // { + // k: Map_Cell(K) * (capacity / ks_per_cell) + // v: Map_Cell(V) * (capacity / vs_per_cell) + // h: Map_Cell(H) * (capacity / hs_per_cell) + // } + // + // The data is allocated assuming 64-byte alignment, meaning the address is + // always a multiple of 64. This means we have 6 bits of zeros in the pointer + // to store the capacity. We can store a value as large as 2^6-1 or 63 in + // there. This conveniently is the maximum log2 capacity we can have for a map + // as Odin uses signed integers to represent capacity. + // + // Since the hashes are backed by Map_Hash, which is just a 64-bit unsigned + // integer, the cell structure for hashes is unnecessary because 64/8 is 8 and + // requires no padding, meaning it can be indexed as a regular array of + // Map_Hash directly, though for consistency sake it's written as if it were + // an array of Map_Cell(Map_Hash). + data: uintptr, // 8-bytes on 64-bits, 4-bytes on 32-bits + len: uintptr, // 8-bytes on 64-bits, 4-bytes on 32-bits + allocator: Allocator, // 16-bytes on 64-bits, 8-bytes on 32-bits +} + +Raw_Any :: struct { + data: rawptr, + id: typeid, +} + +Raw_Cstring :: struct { + data: [^]byte, +} + +Raw_Soa_Pointer :: struct { + data: rawptr, + index: int, +} + + + +/* + // Defined internally by the compiler + Odin_OS_Type :: enum int { + Unknown, + Windows, + Darwin, + Linux, + Essence, + FreeBSD, + OpenBSD, + WASI, + JS, + Freestanding, + } +*/ +Odin_OS_Type :: type_of(ODIN_OS) + +/* + // Defined internally by the compiler + Odin_Arch_Type :: enum int { + Unknown, + amd64, + i386, + arm32, + arm64, + wasm32, + wasm64p32, + } +*/ +Odin_Arch_Type :: type_of(ODIN_ARCH) + +/* + // Defined internally by the compiler + Odin_Build_Mode_Type :: enum int { + Executable, + Dynamic, + Object, + Assembly, + LLVM_IR, + } +*/ +Odin_Build_Mode_Type :: type_of(ODIN_BUILD_MODE) + +/* + // Defined internally by the compiler + Odin_Endian_Type :: enum int { + Unknown, + Little, + Big, + } +*/ +Odin_Endian_Type :: type_of(ODIN_ENDIAN) + + +/* + // Defined internally by the compiler + Odin_Platform_Subtarget_Type :: enum int { + Default, + iOS, + } +*/ +Odin_Platform_Subtarget_Type :: type_of(ODIN_PLATFORM_SUBTARGET) + +/* + // Defined internally by the compiler + Odin_Sanitizer_Flag :: enum u32 { + Address = 0, + Memory = 1, + Thread = 2, + } + Odin_Sanitizer_Flags :: distinct bitset[Odin_Sanitizer_Flag; u32] + + ODIN_SANITIZER_FLAGS // is a constant +*/ +Odin_Sanitizer_Flags :: type_of(ODIN_SANITIZER_FLAGS) + + +///////////////////////////// +// Init Startup Procedures // +///////////////////////////// + +// IMPORTANT NOTE(bill): Do not call this unless you want to explicitly set up the entry point and how it gets called +// This is probably only useful for freestanding targets +foreign { + @(link_name="__$startup_runtime") + _startup_runtime :: proc "odin" () --- + @(link_name="__$cleanup_runtime") + _cleanup_runtime :: proc "odin" () --- +} + +_cleanup_runtime_contextless :: proc "contextless" () { + context = default_context() + _cleanup_runtime() +} + + +///////////////////////////// +///////////////////////////// +///////////////////////////// + + +type_info_base :: proc "contextless" (info: ^Type_Info) -> ^Type_Info { + if info == nil { + return nil + } + + base := info + loop: for { + #partial switch i in base.variant { + case Type_Info_Named: base = i.base + case: break loop + } + } + return base +} + + +type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info { + if info == nil { + return nil + } + + base := info + loop: for { + #partial switch i in base.variant { + case Type_Info_Named: base = i.base + case Type_Info_Enum: base = i.base + case: break loop + } + } + return base +} +type_info_base_without_enum :: type_info_core + +__type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check { + MASK :: 1<<(8*size_of(typeid) - 8) - 1 + data := transmute(uintptr)id + n := int(data & MASK) + if n < 0 || n >= len(type_table) { + n = 0 + } + return &type_table[n] +} + +when !ODIN_NO_RTTI { + typeid_base :: proc "contextless" (id: typeid) -> typeid { + ti := type_info_of(id) + ti = type_info_base(ti) + return ti.id + } + typeid_core :: proc "contextless" (id: typeid) -> typeid { + ti := type_info_core(type_info_of(id)) + return ti.id + } + typeid_base_without_enum :: typeid_core +} + + + +debug_trap :: intrinsics.debug_trap +trap :: intrinsics.trap +read_cycle_counter :: intrinsics.read_cycle_counter + + + +default_logger_proc :: proc(data: rawptr, level: Logger_Level, text: string, options: Logger_Options, location := #caller_location) { + // Nothing +} + +default_logger :: proc() -> Logger { + return Logger{default_logger_proc, nil, Logger_Level.Debug, nil} +} + + +default_context :: proc "contextless" () -> Context { + c: Context + __init_context(&c) + return c +} + +@private +__init_context_from_ptr :: proc "contextless" (c: ^Context, other: ^Context) { + if c == nil { + return + } + c^ = other^ + __init_context(c) +} + +@private +__init_context :: proc "contextless" (c: ^Context) { + if c == nil { + return + } + + // NOTE(bill): Do not initialize these procedures with a call as they are not defined with the "contextless" calling convention + c.allocator.procedure = default_allocator_proc + c.allocator.data = nil + + c.temp_allocator.procedure = default_temp_allocator_proc + when !NO_DEFAULT_TEMP_ALLOCATOR { + c.temp_allocator.data = &global_default_temp_allocator_data + } + + when !ODIN_DISABLE_ASSERT { + c.assertion_failure_proc = default_assertion_failure_proc + } + + c.logger.procedure = default_logger_proc + c.logger.data = nil +} + +default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! { + when ODIN_OS == .Freestanding { + // Do nothing + } else { + when !ODIN_DISABLE_ASSERT { + print_caller_location(loc) + print_string(" ") + } + print_string(prefix) + if len(message) > 0 { + print_string(": ") + print_string(message) + } + print_byte('\n') + } + trap() +} |