diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 3c4185830..f84f14f76 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -50,6 +50,7 @@ jobs: run: | mkdir dist cp odin dist + cp libLLVM*.so dist cp -r shared dist cp -r core dist cp -r vendor dist diff --git a/.gitignore b/.gitignore index 4c69a3f43..deccccbbd 100644 --- a/.gitignore +++ b/.gitignore @@ -271,6 +271,7 @@ odin odin.dSYM *.bin demo.bin +libLLVM*.so # shared collection shared/ @@ -283,3 +284,4 @@ shared/ *.sublime-workspace examples/bug/ build.sh +!core/debug/ \ No newline at end of file diff --git a/build_odin.sh b/build_odin.sh index 62d8a0f59..b7462106b 100755 --- a/build_odin.sh +++ b/build_odin.sh @@ -99,7 +99,8 @@ config_linux() { LDFLAGS="$LDFLAGS -ldl" CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" - LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)" + LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs --libfiles) -Wl,-rpath=\$ORIGIN" + cp $($LLVM_CONFIG --libfiles) ./ } build_odin() { diff --git a/core/c/libc/complex.odin b/core/c/libc/complex.odin index 22c422cce..7f2ca37ae 100644 --- a/core/c/libc/complex.odin +++ b/core/c/libc/complex.odin @@ -49,8 +49,8 @@ foreign libc { // 7.3.8 Power and absolute-value functions cabs :: proc(z: complex_double) -> complex_double --- cabsf :: proc(z: complex_float) -> complex_float --- - cpow :: proc(z: complex_double) -> complex_double --- - cpowf :: proc(z: complex_float) -> complex_float --- + cpow :: proc(x, y: complex_double) -> complex_double --- + cpowf :: proc(x, y: complex_float) -> complex_float --- csqrt :: proc(z: complex_double) -> complex_double --- csqrtf :: proc(z: complex_float) -> complex_float --- diff --git a/core/c/libc/stdlib.odin b/core/c/libc/stdlib.odin index a278db0ef..d797b8746 100644 --- a/core/c/libc/stdlib.odin +++ b/core/c/libc/stdlib.odin @@ -88,7 +88,6 @@ foreign libc { srand :: proc(seed: uint) --- // 7.22.3 Memory management functions - aligned_alloc :: proc(aligment, size: size_t) -> rawptr --- calloc :: proc(nmemb, size: size_t) -> rawptr --- free :: proc(ptr: rawptr) --- malloc :: proc(size: size_t) -> rawptr --- @@ -125,3 +124,30 @@ foreign libc { mbstowcs :: proc(pwcs: ^wchar_t, s: cstring, n: size_t) -> size_t --- wcstombs :: proc(s: [^]char, pwcs: ^wchar_t, n: size_t) -> size_t --- } + + +aligned_alloc :: #force_inline proc "c" (alignment, size: size_t) -> rawptr { + when ODIN_OS == .Windows { + foreign libc { + _aligned_malloc :: proc(size, alignment: size_t) -> rawptr --- + } + return _aligned_malloc(size=size, alignment=alignment) + } else { + foreign libc { + aligned_alloc :: proc(alignment, size: size_t) -> rawptr --- + } + return aligned_alloc(alignment=alignment, size=size) + } +} + + +aligned_free :: #force_inline proc "c" (ptr: rawptr) { + when ODIN_OS == .Windows { + foreign libc { + _aligned_free :: proc(ptr: rawptr) --- + } + _aligned_free(ptr) + } else { + free(ptr) + } +} \ No newline at end of file diff --git a/core/debug/pe/pe.odin b/core/debug/pe/pe.odin new file mode 100644 index 000000000..587c01e8e --- /dev/null +++ b/core/debug/pe/pe.odin @@ -0,0 +1,221 @@ +package debug_pe + +PE_SIGNATURE_OFFSET_INDEX_POS :: 0x3c +PE_SIGNATURE :: u32le(0x0000_4550) // "PE\x00\x00" +PE_SIGNATURE_STRING :: "PE\x00\x00" + +OPTIONAL_HEADER_MAGIC :: enum u16le { + PE32 = 0x010b, + PE32_PLUS = 0x020b, +} + +Optional_Header_Base :: struct #packed { + magic: OPTIONAL_HEADER_MAGIC, + major_linker_version: u8, + minor_linker_version: u8, + size_of_code: u32le, + size_of_initialized_data: u32le, + size_of_uninitialized_data: u32le, + address_of_entry_point: u32le, + base_of_code: u32le, +} + +File_Header :: struct #packed { + machine: IMAGE_FILE_MACHINE, + number_of_sections: u16le, + time_date_stamp: u32le, + pointer_to_symbol_table: u32le, + number_of_symbols: u32le, + size_of_optional_header: u16le, + characteristics: IMAGE_FILE_CHARACTERISTICS, +} + +Data_Directory :: struct #packed { + virtual_address: u32le, + size: u32le, +} + +Optional_Header32 :: struct #packed { + using base: Optional_Header_Base, + base_of_data: u32le, + image_base: u32le, + section_alignment: u32le, + file_alignment: u32le, + major_operating_system_version: u16le, + minor_operating_system_version: u16le, + major_image_version: u16le, + minor_image_version: u16le, + major_subsystem_version: u16le, + minor_subsystem_version: u16le, + win32_version_value: u32le, + size_of_image: u32le, + size_of_headers: u32le, + check_sum: u32le, + subsystem: IMAGE_SUBSYSTEM, + dll_characteristics: IMAGE_DLLCHARACTERISTICS, + size_of_stack_reserve: u32le, + size_of_stack_commit: u32le, + size_of_heap_reserve: u32le, + size_of_heap_commit: u32le, + loader_flags: u32le, + number_of_rva_and_sizes: u32le, + data_directory: [16]Data_Directory, +} + +Optional_Header64 :: struct #packed { + using base: Optional_Header_Base, + image_base: u64le, + section_alignment: u32le, + file_alignment: u32le, + major_operating_system_version: u16le, + minor_operating_system_version: u16le, + major_image_version: u16le, + minor_image_version: u16le, + major_subsystem_version: u16le, + minor_subsystem_version: u16le, + win32_version_value: u32le, + size_of_image: u32le, + size_of_headers: u32le, + check_sum: u32le, + subsystem: IMAGE_SUBSYSTEM, + dll_characteristics: IMAGE_DLLCHARACTERISTICS, + size_of_stack_reserve: u64le, + size_of_stack_commit: u64le, + size_of_heap_reserve: u64le, + size_of_heap_commit: u64le, + loader_flags: u32le, + number_of_rva_and_sizes: u32le, + data_directory: [16]Data_Directory, +} + +// .debug section +Debug_Directory_Entry :: struct { + characteristics: u32le, + time_date_stamp: u32le, + major_version: u16le, + minor_version: u16le, + type: IMAGE_DEBUG_TYPE, + size_of_data: u32le, + address_of_raw_data: u32le, + pointer_to_raw_data: u32le, +} + + +IMAGE_FILE_MACHINE :: enum u16le { + UNKNOWN = 0x0, + AM33 = 0x1d3, + AMD64 = 0x8664, + ARM = 0x1c0, + ARMNT = 0x1c4, + ARM64 = 0xaa64, + EBC = 0xebc, + I386 = 0x14c, + IA64 = 0x200, + LOONGARCH32 = 0x6232, + LOONGARCH64 = 0x6264, + M32R = 0x9041, + MIPS16 = 0x266, + MIPSFPU = 0x366, + MIPSFPU16 = 0x466, + POWERPC = 0x1f0, + POWERPCFP = 0x1f1, + R4000 = 0x166, + SH3 = 0x1a2, + SH3DSP = 0x1a3, + SH4 = 0x1a6, + SH5 = 0x1a8, + THUMB = 0x1c2, + WCEMIPSV2 = 0x169, +} + +// IMAGE_DIRECTORY_ENTRY constants +IMAGE_DIRECTORY_ENTRY :: enum u8 { + EXPORT = 0, + IMPORT = 1, + RESOURCE = 2, + EXCEPTION = 3, + SECURITY = 4, + BASERELOC = 5, + DEBUG = 6, + ARCHITECTURE = 7, // reserved + GLOBALPTR = 8, + TLS = 9, + LOAD_CONFIG = 10, + BOUND_IMPORT = 11, + IAT = 12, + DELAY_IMPORT = 13, + COM_DESCRIPTOR = 14, // DLR Runtime headers + _RESERVED = 15, +} +#assert(len(IMAGE_DIRECTORY_ENTRY) == 16) + + +IMAGE_FILE_CHARACTERISTICS :: distinct bit_set[IMAGE_FILE_CHARACTERISTIC; u16le] +IMAGE_FILE_CHARACTERISTIC :: enum u16le { + RELOCS_STRIPPED = 0, + EXECUTABLE_IMAGE = 1, + LINE_NUMS_STRIPPED = 2, + LOCAL_SYMS_STRIPPED = 3, + AGGRESIVE_WS_TRIM = 4, + LARGE_ADDRESS_AWARE = 5, + + BYTES_REVERSED_LO = 7, + MACHINE_32BIT = 8, // IMAGE_FILE_32BIT_MACHINE originally + DEBUG_STRIPPED = 9, + REMOVABLE_RUN_FROM_SWAP = 10, + NET_RUN_FROM_SWAP = 11, + SYSTEM = 12, + DLL = 13, + UP_SYSTEM_ONLY = 14, + BYTES_REVERSED_HI = 15, +} + +IMAGE_SUBSYSTEM :: enum u16le { + UNKNOWN = 0, + NATIVE = 1, + WINDOWS_GUI = 2, + WINDOWS_CUI = 3, + OS2_CUI = 5, + POSIX_CUI = 7, + NATIVE_WINDOWS = 8, + WINDOWS_CE_GUI = 9, + EFI_APPLICATION = 10, + EFI_BOOT_SERVICE_DRIVER = 11, + EFI_RUNTIME_DRIVER = 12, + EFI_ROM = 13, + XBOX = 14, + WINDOWS_BOOT_APPLICATION = 16, +} + +IMAGE_DLLCHARACTERISTICS :: distinct bit_set[IMAGE_DLLCHARACTERISTIC; u16le] +IMAGE_DLLCHARACTERISTIC :: enum u16le { + HIGH_ENTROPY_VA = 5, + DYNAMIC_BASE = 6, + FORCE_INTEGRITY = 7, + NX_COMPAT = 8, + NO_ISOLATION = 9, + NO_SEH = 10, + NO_BIND = 11, + APPCONTAINER = 12, + WDM_DRIVER = 13, + GUARD_CF = 14, + TERMINAL_SERVER_AWARE = 15, +} + +IMAGE_DEBUG_TYPE :: enum u32le { + UNKNOWN = 0, // An unknown value that is ignored by all tools. + COFF = 1, // The COFF debug information (line numbers, symbol table, and string table). This type of debug information is also pointed to by fields in the file headers. + CODEVIEW = 2, // The Visual C++ debug information. + FPO = 3, // The frame pointer omission (FPO) information. This information tells the debugger how to interpret nonstandard stack frames, which use the EBP register for a purpose other than as a frame pointer. + MISC = 4, // The location of DBG file. + EXCEPTION = 5, // A copy of .pdata section. + FIXUP = 6, // Reserved. + OMAP_TO_SRC = 7, // The mapping from an RVA in image to an RVA in source image. + OMAP_FROM_SRC = 8, // The mapping from an RVA in source image to an RVA in image. + BORLAND = 9, // Reserved for Borland. + RESERVED10 = 10, // Reserved. + CLSID = 11, // Reserved. + REPRO = 16, // PE determinism or reproducibility. + EX_DLLCHARACTERISTICS = 20, // Extended DLL characteristics bits. +} + diff --git a/core/debug/pe/section.odin b/core/debug/pe/section.odin new file mode 100644 index 000000000..809da8bb4 --- /dev/null +++ b/core/debug/pe/section.odin @@ -0,0 +1,131 @@ +package debug_pe + +import "core:runtime" +import "core:io" + +Section_Header32 :: struct { + name: [8]u8, + virtual_size: u32le, + virtual_address: u32le, + size_of_raw_data: u32le, + pointer_to_raw_data: u32le, + pointer_to_relocations: u32le, + pointer_to_line_numbers: u32le, + number_of_relocations: u16le, + number_of_line_numbers: u16le, + characteristics: IMAGE_SCN_CHARACTERISTICS, +} + +Reloc :: struct { + virtual_address: u32le, + symbol_table_index: u32le, + type: IMAGE_REL, +} + +IMAGE_SCN_CHARACTERISTICS :: enum u32le { + TYPE_NO_PAD = 0x00000008, // The section should not be padded to the next boundary. This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. This is valid only for object files. = 0x00000010, // Reserved for future use. + CNT_CODE = 0x00000020, // The section contains executable code. + CNT_INITIALIZED_DATA = 0x00000040, // The section contains initialized data. + CNT_UNINITIALIZED_DATA = 0x00000080, // The section contains uninitialized data. + LNK_OTHER = 0x00000100, // Reserved for future use. + LNK_INFO = 0x00000200, // The section contains comments or other information. The .drectve section has this type. This is valid for object files only. = 0x00000400, // Reserved for future use. + LNK_REMOVE = 0x00000800, // The section will not become part of the image. This is valid only for object files. + LNK_COMDAT = 0x00001000, // The section contains COMDAT data. For more information, see COMDAT Sections (Object Only). This is valid only for object files. + GPREL = 0x00008000, // The section contains data referenced through the global pointer (GP). + MEM_PURGEABLE = 0x00020000, // Reserved for future use. + MEM_16BIT = 0x00020000, // Reserved for future use. + MEM_LOCKED = 0x00040000, // Reserved for future use. + MEM_PRELOAD = 0x00080000, // Reserved for future use. + ALIGN_1BYTES = 0x00100000, // Align data on a 1-byte boundary. Valid only for object files. + ALIGN_2BYTES = 0x00200000, // Align data on a 2-byte boundary. Valid only for object files. + ALIGN_4BYTES = 0x00300000, // Align data on a 4-byte boundary. Valid only for object files. + ALIGN_8BYTES = 0x00400000, // Align data on an 8-byte boundary. Valid only for object files. + ALIGN_16BYTES = 0x00500000, // Align data on a 16-byte boundary. Valid only for object files. + ALIGN_32BYTES = 0x00600000, // Align data on a 32-byte boundary. Valid only for object files. + ALIGN_64BYTES = 0x00700000, // Align data on a 64-byte boundary. Valid only for object files. + ALIGN_128BYTES = 0x00800000, // Align data on a 128-byte boundary. Valid only for object files. + ALIGN_256BYTES = 0x00900000, // Align data on a 256-byte boundary. Valid only for object files. + ALIGN_512BYTES = 0x00A00000, // Align data on a 512-byte boundary. Valid only for object files. + ALIGN_1024BYTES = 0x00B00000, // Align data on a 1024-byte boundary. Valid only for object files. + ALIGN_2048BYTES = 0x00C00000, // Align data on a 2048-byte boundary. Valid only for object files. + ALIGN_4096BYTES = 0x00D00000, // Align data on a 4096-byte boundary. Valid only for object files. + ALIGN_8192BYTES = 0x00E00000, // Align data on an 8192-byte boundary. Valid only for object files. + LNK_NRELOC_OVFL = 0x01000000, // The section contains extended relocations. + MEM_DISCARDABLE = 0x02000000, // The section can be discarded as needed. + MEM_NOT_CACHED = 0x04000000, // The section cannot be cached. + MEM_NOT_PAGED = 0x08000000, // The section is not pageable. + MEM_SHARED = 0x10000000, // The section can be shared in memory. + MEM_EXECUTE = 0x20000000, // The section can be executed as code. + MEM_READ = 0x40000000, // The section can be read. + MEM_WRITE = 0x80000000, // The section can be written to. +} + + +IMAGE_REL :: enum u16le { + I386_ABSOLUTE = 0x0000, + I386_DIR16 = 0x0001, + I386_REL16 = 0x0002, + I386_DIR32 = 0x0006, + I386_DIR32NB = 0x0007, + I386_SEG12 = 0x0009, + I386_SECTION = 0x000A, + I386_SECREL = 0x000B, + I386_TOKEN = 0x000C, + I386_SECREL7 = 0x000D, + I386_REL32 = 0x0014, + + AMD64_ABSOLUTE = 0x0000, + AMD64_ADDR64 = 0x0001, + AMD64_ADDR32 = 0x0002, + AMD64_ADDR32NB = 0x0003, + AMD64_REL32 = 0x0004, + AMD64_REL32_1 = 0x0005, + AMD64_REL32_2 = 0x0006, + AMD64_REL32_3 = 0x0007, + AMD64_REL32_4 = 0x0008, + AMD64_REL32_5 = 0x0009, + AMD64_SECTION = 0x000A, + AMD64_SECREL = 0x000B, + AMD64_SECREL7 = 0x000C, + AMD64_TOKEN = 0x000D, + AMD64_SREL32 = 0x000E, + AMD64_PAIR = 0x000F, + AMD64_SSPAN32 = 0x0010, + + ARM_ABSOLUTE = 0x0000, + ARM_ADDR32 = 0x0001, + ARM_ADDR32NB = 0x0002, + ARM_BRANCH24 = 0x0003, + ARM_BRANCH11 = 0x0004, + ARM_SECTION = 0x000E, + ARM_SECREL = 0x000F, + ARM_MOV32 = 0x0010, + + THUMB_MOV32 = 0x0011, + THUMB_BRANCH20 = 0x0012, + THUMB_BRANCH24 = 0x0014, + THUMB_BLX23 = 0x0015, + + ARM_PAIR = 0x0016, + + ARM64_ABSOLUTE = 0x0000, + ARM64_ADDR32 = 0x0001, + ARM64_ADDR32NB = 0x0002, + ARM64_BRANCH26 = 0x0003, + ARM64_PAGEBASE_REL21 = 0x0004, + ARM64_REL21 = 0x0005, + ARM64_PAGEOFFSET_12A = 0x0006, + ARM64_PAGEOFFSET_12L = 0x0007, + ARM64_SECREL = 0x0008, + ARM64_SECREL_LOW12A = 0x0009, + ARM64_SECREL_HIGH12A = 0x000A, + ARM64_SECREL_LOW12L = 0x000B, + ARM64_TOKEN = 0x000C, + ARM64_SECTION = 0x000D, + ARM64_ADDR64 = 0x000E, + ARM64_BRANCH19 = 0x000F, + ARM64_BRANCH14 = 0x0010, + ARM64_REL32 = 0x0011, +} + +PE_CODE_VIEW_SIGNATURE_RSDS :: u32le(0x5344_5352) \ No newline at end of file diff --git a/core/debug/pe/symbol.odin b/core/debug/pe/symbol.odin new file mode 100644 index 000000000..288c6bd58 --- /dev/null +++ b/core/debug/pe/symbol.odin @@ -0,0 +1,108 @@ +package debug_pe + +COFF_SYMBOL_SIZE :: 18 + +COFF_Symbol :: struct { + name: [8]u8, + value: u32le, + section_number: i16le, + type: IMAGE_SYM_TYPE, + storage_class: IMAGE_SYM_CLASS, + number_of_aux_symbols: u8, +} + +// COFF_Symbol_Aux_Format5 describes the expected form of an aux symbol +// attached to a section definition symbol. The PE format defines a +// number of different aux symbol formats: format 1 for function +// definitions, format 2 for .be and .ef symbols, and so on. Format 5 +// holds extra info associated with a section definition, including +// number of relocations + line numbers, as well as COMDAT info. See +// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-5-section-definitions +// for more on what's going on here. +COFF_Symbol_Aux_Format5 :: struct { + size: u32le, + num_relocs: u16le, + num_line_numbers: u16le, + checksum: u32le, + sec_num: u16le, + selection: IMAGE_COMDAT_SELECT, + _: [3]u8, // padding +} + +IMAGE_COMDAT_SELECT :: enum u8 { + NODUPLICATES = 1, + ANY = 2, + SAME_SIZE = 3, + EXACT_MATCH = 4, + ASSOCIATIVE = 5, + LARGEST = 6, +} + + +// The symbol record is not yet assigned a section. A value of zero indicates +// that a reference to an external symbol is defined elsewhere. A value of +// non-zero is a common symbol with a size that is specified by the value. +IMAGE_SYM_UNDEFINED :: 0 +// The symbol has an absolute (non-relocatable) value and is not an address. +IMAGE_SYM_ABSOLUTE :: -1 +// The symbol provides general type or debugging information but does not +// correspond to a section. Microsoft tools use this setting along +// with .file records (storage class FILE). +IMAGE_SYM_DEBUG :: -2 + +IMAGE_SYM_TYPE :: enum u16le { + NULL = 0, + VOID = 1, + CHAR = 2, + SHORT = 3, + INT = 4, + LONG = 5, + FLOAT = 6, + DOUBLE = 7, + STRUCT = 8, + UNION = 9, + ENUM = 10, + MOE = 11, + BYTE = 12, + WORD = 13, + UINT = 14, + DWORD = 15, + PCODE = 32768, + + DTYPE_NULL = 0, + DTYPE_POINTER = 0x10, + DTYPE_FUNCTION = 0x20, + DTYPE_ARRAY = 0x30, +} + +IMAGE_SYM_CLASS :: enum u8 { + NULL = 0, + AUTOMATIC = 1, + EXTERNAL = 2, + STATIC = 3, + REGISTER = 4, + EXTERNAL_DEF = 5, + LABEL = 6, + UNDEFINED_LABEL = 7, + MEMBER_OF_STRUCT = 8, + ARGUMENT = 9, + STRUCT_TAG = 10, + MEMBER_OF_UNION = 11, + UNION_TAG = 12, + TYPE_DEFINITION = 13, + UNDEFINED_STATIC = 14, + ENUM_TAG = 15, + MEMBER_OF_ENUM = 16, + REGISTER_PARAM = 17, + BIT_FIELD = 18, + FAR_EXTERNAL = 68, // Not in PECOFF v8 spec + BLOCK = 100, + FUNCTION = 101, + END_OF_STRUCT = 102, + FILE = 103, + SECTION = 104, + WEAK_EXTERNAL = 105, + CLR_TOKEN = 107, + + END_OF_FUNCTION = 255, +} diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index 792b1edc6..062649b58 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -405,7 +405,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm raw_map.entries.allocator = p.allocator } - header := runtime.__get_map_header_runtime(raw_map, t) + header := runtime.__get_map_header_table_runtime(t) elem_backing := bytes_make(t.value.size, t.value.align, p.allocator) or_return defer delete(elem_backing, p.allocator) @@ -422,19 +422,17 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm delete(key, p.allocator) return err } - - hash := runtime.Map_Hash { - hash = runtime.default_hasher_string(&key, 0), - key_ptr = &key, - } - + + key_hash := runtime.default_hasher_string(&key, 0) + key_ptr := rawptr(&key) + key_cstr: cstring if reflect.is_cstring(t.key) { key_cstr = cstring(raw_data(key)) - hash.key_ptr = &key_cstr + key_ptr = &key_cstr } - set_ptr := runtime.__dynamic_map_set(header, hash, map_backing_value.data) + set_ptr := runtime.__dynamic_map_set(raw_map, header, key_hash, key_ptr, map_backing_value.data) if set_ptr == nil { delete(key, p.allocator) } diff --git a/core/math/linalg/extended.odin b/core/math/linalg/extended.odin index 24b7a90fc..f6da366ba 100644 --- a/core/math/linalg/extended.odin +++ b/core/math/linalg/extended.odin @@ -81,7 +81,7 @@ max_single :: proc(a: $T) -> (out: ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T)) } else when N == 2 { out = builtin.max(a[0], a[1]) } else when N == 3 { - out = builtin.max(a[0], a[1], a[3]) + out = builtin.max(a[0], a[1], a[2]) }else { out = builtin.max(a[0], a[1]) for i in 2.. (deleted_key: K, deleted_value: if m != nil { key := key h := __get_map_header(m) - hash := __get_map_hash(&key) - fr := __dynamic_map_find(h, hash) - if fr.entry_index >= 0 { + fr := __map_find(h, &key) + if fr.entry_index != MAP_SENTINEL { entry := __dynamic_map_get_entry(h, fr.entry_index) deleted_key = (^K)(uintptr(entry)+h.key_offset)^ deleted_value = (^V)(uintptr(entry)+h.value_offset)^ @@ -335,7 +335,6 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: __dynamic_map_erase(h, fr) } } - return } @@ -673,11 +672,10 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call @builtin map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) { key, value := key, value - h := __get_map_header(m) - hash := __get_map_hash(&key) - - data := uintptr(__dynamic_map_set(h, hash, &value, loc)) - return (^V)(data + h.value_offset) + h := __get_map_header_table(T) + + e := __dynamic_map_set(m, h, __get_map_key_hash(&key), &key, &value, loc) + return (^V)(uintptr(e) + h.value_offset) } diff --git a/core/runtime/dynamic_array_internal.odin b/core/runtime/dynamic_array_internal.odin index b6a685fcf..267ee0785 100644 --- a/core/runtime/dynamic_array_internal.odin +++ b/core/runtime/dynamic_array_internal.odin @@ -59,6 +59,8 @@ __dynamic_array_shrink :: proc(array_: rawptr, elem_size, elem_align: int, new_c return } + new_cap := new_cap + new_cap = max(new_cap, 0) old_size := array.cap * elem_size new_size := new_cap * elem_size allocator := array.allocator diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 35b42d488..abe58fc5a 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -11,38 +11,34 @@ Map_Hash :: struct { key_ptr: rawptr, // address of Map_Entry_Header.key } -__get_map_hash :: proc "contextless" (k: ^$K) -> (map_hash: Map_Hash) { +__get_map_key_hash :: #force_inline proc "contextless" (k: ^$K) -> uintptr { hasher := intrinsics.type_hasher_proc(K) - map_hash.key_ptr = k - map_hash.hash = hasher(k, 0) - return + return hasher(k, 0) } -__get_map_hash_from_entry :: proc "contextless" (h: Map_Header, entry: ^Map_Entry_Header) -> (hash: Map_Hash) { - hash.hash = entry.hash - hash.key_ptr = rawptr(uintptr(entry) + h.key_offset) - return +__get_map_entry_key_ptr :: #force_inline proc "contextless" (h: Map_Header_Table, entry: ^Map_Entry_Header) -> rawptr { + return rawptr(uintptr(entry) + h.key_offset) } - +Map_Index :: distinct uint +MAP_SENTINEL :: ~Map_Index(0) Map_Find_Result :: struct { - hash_index: int, - entry_prev: int, - entry_index: int, + hash_index: Map_Index, + entry_prev: Map_Index, + entry_index: Map_Index, } Map_Entry_Header :: struct { hash: uintptr, - next: int, + next: Map_Index, /* key: Key_Value, value: Value_Type, */ } -Map_Header :: struct { - m: ^Raw_Map, +Map_Header_Table :: struct { equal: Equal_Proc, entry_size: int, @@ -55,6 +51,102 @@ Map_Header :: struct { value_size: int, } +Map_Header :: struct { + m: ^Raw_Map, + using table: Map_Header_Table, +} + +// USED INTERNALLY BY THE COMPILER +__dynamic_map_get :: proc "contextless" (m: rawptr, table: Map_Header_Table, key_hash: uintptr, key_ptr: rawptr) -> rawptr { + if m != nil { + h := Map_Header{(^Raw_Map)(m), table} + index := __dynamic_map_find(h, key_hash, key_ptr).entry_index + if index != MAP_SENTINEL { + data := uintptr(__dynamic_map_get_entry(h, index)) + return rawptr(data + h.value_offset) + } + } + return nil +} + +// USED INTERNALLY BY THE COMPILER +__dynamic_map_set :: proc "odin" (m: rawptr, table: Map_Header_Table, key_hash: uintptr, key_ptr: rawptr, value: rawptr, loc := #caller_location) -> ^Map_Entry_Header #no_bounds_check { + add_entry :: proc "odin" (h: Map_Header, key_hash: uintptr, key_ptr: rawptr, loc := #caller_location) -> Map_Index { + prev := Map_Index(h.m.entries.len) + c := Map_Index(__dynamic_array_append_nothing(&h.m.entries, h.entry_size, h.entry_align, loc)) + if c != prev { + end := __dynamic_map_get_entry(h, c-1) + end.hash = key_hash + mem_copy(rawptr(uintptr(end) + h.key_offset), key_ptr, h.key_size) + end.next = MAP_SENTINEL + } + return prev + } + + h := Map_Header{(^Raw_Map)(m), table} + + index := MAP_SENTINEL + + if len(h.m.hashes) == 0 { + __dynamic_map_reserve(m, table, INITIAL_MAP_CAP, loc) + __dynamic_map_grow(h, loc) + } + + fr := __dynamic_map_find(h, key_hash, key_ptr) + if fr.entry_index != MAP_SENTINEL { + index = fr.entry_index + } else { + index = add_entry(h, key_hash, key_ptr, loc) + if fr.entry_prev != MAP_SENTINEL { + entry := __dynamic_map_get_entry(h, fr.entry_prev) + entry.next = index + } else if fr.hash_index != MAP_SENTINEL { + h.m.hashes[fr.hash_index] = index + } else { + return nil + } + } + + e := __dynamic_map_get_entry(h, index) + e.hash = key_hash + + key := rawptr(uintptr(e) + h.key_offset) + val := rawptr(uintptr(e) + h.value_offset) + + mem_copy(key, key_ptr, h.key_size) + mem_copy(val, value, h.value_size) + + if __dynamic_map_full(h) { + __dynamic_map_grow(h, loc) + } + + return __dynamic_map_get_entry(h, index) +} + +// USED INTERNALLY BY THE COMPILER +__dynamic_map_reserve :: proc "odin" (m: rawptr, table: Map_Header_Table, cap: uint, loc := #caller_location) { + h := Map_Header{(^Raw_Map)(m), table} + + c := context + if h.m.entries.allocator.procedure != nil { + c.allocator = h.m.entries.allocator + } + context = c + + cap := cap + cap = ceil_to_pow2(cap) + + __dynamic_array_reserve(&h.m.entries, h.entry_size, h.entry_align, int(cap), loc) + + if h.m.entries.len*2 < len(h.m.hashes) { + return + } + if __slice_resize(&h.m.hashes, int(cap*2), h.m.entries.allocator, loc) { + __dynamic_map_reset_entries(h, loc) + } +} + + INITIAL_HASH_SEED :: 0xcbf29ce484222325 _fnv64a :: proc "contextless" (data: []byte, seed: u64 = INITIAL_HASH_SEED) -> u64 { @@ -138,11 +230,22 @@ default_hasher_cstring :: proc "contextless" (data: rawptr, seed: uintptr) -> ui } -__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { - header := Map_Header{m = (^Raw_Map)(m)} +__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> (header: Map_Header) { + header.m = (^Raw_Map)(m) + header.table = #force_inline __get_map_header_table(T) + return +} + +__get_map_header_runtime :: proc "contextless" (m: ^Raw_Map, ti: Type_Info_Map) -> (header: Map_Header) { + header.m = m + header.table = #force_inline __get_map_header_table_runtime(ti) + return +} + +__get_map_header_table :: proc "contextless" ($T: typeid/map[$K]$V) -> (header: Map_Header_Table) { Entry :: struct { hash: uintptr, - next: int, + next: Map_Index, key: K, value: V, } @@ -158,18 +261,16 @@ __get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { header.value_offset = offset_of(Entry, value) header.value_size = size_of(V) - return header + return } -__get_map_header_runtime :: proc "contextless" (m: ^Raw_Map, ti: Type_Info_Map) -> Map_Header { - header := Map_Header{m = m} - +__get_map_header_table_runtime :: proc "contextless" (ti: Type_Info_Map) -> (header: Map_Header) { header.equal = ti.key_equal - + entries := ti.generated_struct.variant.(Type_Info_Struct).types[1] entry := entries.variant.(Type_Info_Dynamic_Array).elem e := entry.variant.(Type_Info_Struct) - + header.entry_size = entry.size header.entry_align = entry.align @@ -179,11 +280,12 @@ __get_map_header_runtime :: proc "contextless" (m: ^Raw_Map, ti: Type_Info_Map) header.value_offset = e.offsets[3] header.value_size = e.types[3].size - return header + return } -__slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool { + +__slice_resize :: proc "odin" (array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool { array := (^Raw_Slice)(array_) if new_count < array.len { @@ -205,136 +307,82 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l return false } -__dynamic_map_reset_entries :: proc(using header: Map_Header, loc := #caller_location) { - for i in 0.. (did_shrink: bool) { - c := context - if m.entries.allocator.procedure != nil { - c.allocator = m.entries.allocator - } - context = c - - return __dynamic_array_shrink(&m.entries, entry_size, entry_align, cap, loc) -} - -__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) { - #force_inline __dynamic_map_reserve(header, new_count, loc) -} - -__dynamic_map_get :: proc(h: Map_Header, hash: Map_Hash) -> rawptr { - index := __dynamic_map_find(h, hash).entry_index - if index >= 0 { - data := uintptr(__dynamic_map_get_entry(h, index)) - return rawptr(data + h.value_offset) - } - return nil -} - -__dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := #caller_location) -> ^Map_Entry_Header #no_bounds_check { - index: int - // assert(value != nil) - - if len(h.m.hashes) == 0 { - __dynamic_map_reserve(h, INITIAL_MAP_CAP, loc) - __dynamic_map_grow(h, loc) - } - - fr := __dynamic_map_find(h, hash) - if fr.entry_index >= 0 { - index = fr.entry_index - } else { - index = __dynamic_map_add_entry(h, hash, loc) - if fr.entry_prev >= 0 { - entry := __dynamic_map_get_entry(h, fr.entry_prev) - entry.next = index - } else if fr.hash_index >= 0 { - h.m.hashes[fr.hash_index] = index } else { - return nil + h.m.hashes[fr.hash_index] = i } } +} - e := __dynamic_map_get_entry(h, index) - e.hash = hash.hash - - key := rawptr(uintptr(e) + h.key_offset) - mem_copy(key, hash.key_ptr, h.key_size) - - val := rawptr(uintptr(e) + h.value_offset) - mem_copy(val, value, h.value_size) - - if __dynamic_map_full(h) { - __dynamic_map_grow(h, loc) - // index = __dynamic_map_find(h, hash).entry_index - // assert(index >= 0) +__dynamic_map_shrink :: proc "odin" (h: Map_Header, cap: int, loc := #caller_location) -> (did_shrink: bool) { + c := context + if h.m.entries.allocator.procedure != nil { + c.allocator = h.m.entries.allocator } - - return __dynamic_map_get_entry(h, index) + context = c + + return __dynamic_array_shrink(&h.m.entries, h.entry_size, h.entry_align, cap, loc) } -__dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) { - // TODO(bill): Determine an efficient growing rate - new_count := max(4*m.entries.cap + 7, INITIAL_MAP_CAP) - __dynamic_map_rehash(h, new_count, loc) +@(private="file") +ceil_to_pow2 :: proc "contextless" (n: uint) -> uint { + if n <= 2 { + return n + } + n := n + n -= 1 + n |= n >> 1 + n |= n >> 2 + n |= n >> 4 + n |= n >> 8 + n |= n >> 16 + when size_of(int) == 8 { + n |= n >> 32 + } + n += 1 + return n } -__dynamic_map_full :: #force_inline proc "contextless" (using h: Map_Header) -> bool { - return int(0.75 * f64(len(m.hashes))) <= m.entries.len +__dynamic_map_grow :: proc "odin" (h: Map_Header, loc := #caller_location) { + new_count := max(uint(h.m.entries.cap) * 2, INITIAL_MAP_CAP) + // Rehash through Reserve + __dynamic_map_reserve(h.m, h.table, new_count, loc) } - -__dynamic_map_hash_equal :: proc "contextless" (h: Map_Header, a, b: Map_Hash) -> bool { - return a.hash == b.hash && h.equal(a.key_ptr, b.key_ptr) +__dynamic_map_full :: #force_inline proc "contextless" (h: Map_Header) -> bool { + return int(0.75 * f64(len(h.m.hashes))) <= h.m.entries.len } -__dynamic_map_find :: proc(using h: Map_Header, hash: Map_Hash) -> Map_Find_Result #no_bounds_check { - fr := Map_Find_Result{-1, -1, -1} - if n := uintptr(len(m.hashes)); n > 0 { - fr.hash_index = int(hash.hash % n) - fr.entry_index = m.hashes[fr.hash_index] - for fr.entry_index >= 0 { +__dynamic_map_find_from_entry :: proc "contextless" (h: Map_Header, e: ^Map_Entry_Header) -> Map_Find_Result #no_bounds_check { + key_ptr := __get_map_entry_key_ptr(h, e) + return __dynamic_map_find(h, e.hash, key_ptr) + +} + +__dynamic_map_find :: proc "contextless" (h: Map_Header, key_hash: uintptr, key_ptr: rawptr) -> Map_Find_Result #no_bounds_check { + fr := Map_Find_Result{MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL} + if n := uintptr(len(h.m.hashes)); n != 0 { + fr.hash_index = Map_Index(key_hash & (n-1)) + fr.entry_index = h.m.hashes[fr.hash_index] + for fr.entry_index != MAP_SENTINEL { entry := __dynamic_map_get_entry(h, fr.entry_index) - entry_hash := __get_map_hash_from_entry(h, entry) - if __dynamic_map_hash_equal(h, entry_hash, hash) { + entry_key_ptr := __get_map_entry_key_ptr(h, entry) + if entry.hash == key_hash && h.equal(entry_key_ptr, key_ptr) { return fr } - // assert(entry.next < m.entries.len) fr.entry_prev = fr.entry_index fr.entry_index = entry.next @@ -343,58 +391,38 @@ __dynamic_map_find :: proc(using h: Map_Header, hash: Map_Hash) -> Map_Find_Resu return fr } -__dynamic_map_add_entry :: proc(using h: Map_Header, hash: Map_Hash, loc := #caller_location) -> int { - prev := m.entries.len - c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc) - if c != prev { - end := __dynamic_map_get_entry(h, c-1) - end.hash = hash.hash - mem_copy(rawptr(uintptr(end) + key_offset), hash.key_ptr, key_size) - end.next = -1 - } - return prev +// Utility procedure used by other runtime procedures +__map_find :: proc "contextless" (h: Map_Header, key_ptr: ^$K) -> Map_Find_Result #no_bounds_check { + hash := __get_map_key_hash(key_ptr) + return #force_inline __dynamic_map_find(h, hash, key_ptr) } -__dynamic_map_delete_key :: proc(using h: Map_Header, hash: Map_Hash) { - fr := __dynamic_map_find(h, hash) - if fr.entry_index >= 0 { - __dynamic_map_erase(h, fr) - } +__dynamic_map_get_entry :: #force_inline proc "contextless" (h: Map_Header, index: Map_Index) -> ^Map_Entry_Header { + return (^Map_Entry_Header)(uintptr(h.m.entries.data) + uintptr(index*Map_Index(h.entry_size))) } -__dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_Header { - // assert(0 <= index && index < m.entries.len) - return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size)) -} - -__dynamic_map_copy_entry :: proc(h: Map_Header, new, old: ^Map_Entry_Header) { - mem_copy(new, old, h.entry_size) -} - -__dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check { - if fr.entry_prev < 0 { - m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next - } else { +__dynamic_map_erase :: proc "contextless" (h: Map_Header, fr: Map_Find_Result) #no_bounds_check { + if fr.entry_prev != MAP_SENTINEL { prev := __dynamic_map_get_entry(h, fr.entry_prev) curr := __dynamic_map_get_entry(h, fr.entry_index) prev.next = curr.next - } - if fr.entry_index == m.entries.len-1 { - // NOTE(bill): No need to do anything else, just pop } else { + h.m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next + } + last_index := Map_Index(h.m.entries.len-1) + if fr.entry_index != last_index { old := __dynamic_map_get_entry(h, fr.entry_index) - end := __dynamic_map_get_entry(h, m.entries.len-1) - __dynamic_map_copy_entry(h, old, end) + end := __dynamic_map_get_entry(h, last_index) + mem_copy(old, end, h.entry_size) - old_hash := __get_map_hash_from_entry(h, old) - - if last := __dynamic_map_find(h, old_hash); last.entry_prev >= 0 { - last_entry := __dynamic_map_get_entry(h, last.entry_prev) - last_entry.next = fr.entry_index + last := __dynamic_map_find_from_entry(h, old) + if last.entry_prev != MAP_SENTINEL { + e := __dynamic_map_get_entry(h, last.entry_prev) + e.next = fr.entry_index } else { - m.hashes[last.hash_index] = fr.entry_index + h.m.hashes[last.hash_index] = fr.entry_index } } - m.entries.len -= 1 + h.m.entries.len -= 1 } diff --git a/core/slice/ptr.odin b/core/slice/ptr.odin index 9074932ae..214b745f7 100644 --- a/core/slice/ptr.odin +++ b/core/slice/ptr.odin @@ -4,10 +4,10 @@ import "core:builtin" import "core:mem" ptr_add :: proc(p: $P/^$T, x: int) -> ^T { - return (^T)(uintptr(p) + size_of(T)*x) + return ([^]T)(p)[x:] } ptr_sub :: proc(p: $P/^$T, x: int) -> ^T { - return #force_inline ptr_add(p, -x) + return ([^]T)(p)[-x:] } ptr_swap_non_overlapping :: proc(x, y: rawptr, len: int) { @@ -84,12 +84,14 @@ ptr_rotate :: proc(left: int, mid: ^$T, right: int) { } } } else { - ptr_swap_non_overlapping(ptr_sub(mid, left), mid, left) - mid = ptr_add(mid, left) + for { + ptr_swap_non_overlapping(ptr_sub(mid, left), mid, left) + mid = ptr_add(mid, left) - right -= left - if right < left { - break + right -= left + if right < left { + break + } } } } diff --git a/core/slice/slice.odin b/core/slice/slice.odin index e76a5599d..abc84787f 100644 --- a/core/slice/slice.odin +++ b/core/slice/slice.odin @@ -509,3 +509,10 @@ dot_product :: proc(a, b: $S/[]$T) -> (r: T, ok: bool) } return r, true } + + +// Convert a pointer to an enumerated array to a slice of the element type +enumerated_array :: proc(ptr: ^$T) -> []intrinsics.type_elem_type(T) + where intrinsics.type_is_enumerated_array(T) { + return ([^]intrinsics.type_elem_type(T))(ptr)[:len(T)] +} \ No newline at end of file diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index 15ffa2dd3..a9ed44d8a 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -820,3 +820,147 @@ foreign kernel32 { HandlerRoutine :: proc "stdcall" (dwCtrlType: DWORD) -> BOOL PHANDLER_ROUTINE :: HandlerRoutine + + + + +DCB_Config :: struct { + fParity: bool, + fOutxCtsFlow: bool, + fOutxDsrFlow: bool, + fDtrControl: DTR_Control, + fDsrSensitivity: bool, + fTXContinueOnXoff: bool, + fOutX: bool, + fInX: bool, + fErrorChar: bool, + fNull: bool, + fRtsControl: RTS_Control, + fAbortOnError: bool, + BaudRate: DWORD, + ByteSize: BYTE, + Parity: Parity, + StopBits: Stop_Bits, + XonChar: byte, + XoffChar: byte, + ErrorChar: byte, + EvtChar: byte, +} +DTR_Control :: enum byte { + Disable = 0, + Enable = 1, + Handshake = 2, +} +RTS_Control :: enum byte { + Disable = 0, + Enable = 1, + Handshake = 2, + Toggle = 3, +} +Parity :: enum byte { + None = 0, + Odd = 1, + Even = 2, + Mark = 3, + Space = 4, +} +Stop_Bits :: enum byte { + One = 0, + One_And_A_Half = 1, + Two = 2, +} + +// A helper procedure to set the values of a DCB structure. +init_dcb_with_config :: proc "contextless" (dcb: ^DCB, config: DCB_Config) { + out: u32 + + // NOTE(tetra, 2022-09-21): On both Clang 14 on Windows, and MSVC, the bits in the bitfield + // appear to be defined from LSB to MSB order. + // i.e: `fBinary` (the first bitfield in the C source) is the LSB in the `settings` u32. + + out |= u32(1) << 0 // fBinary must always be true on Windows. + + out |= u32(config.fParity) << 1 + out |= u32(config.fOutxCtsFlow) << 2 + out |= u32(config.fOutxDsrFlow) << 3 + + out |= u32(config.fDtrControl) << 4 + + out |= u32(config.fDsrSensitivity) << 6 + out |= u32(config.fTXContinueOnXoff) << 7 + out |= u32(config.fOutX) << 8 + out |= u32(config.fInX) << 9 + out |= u32(config.fErrorChar) << 10 + out |= u32(config.fNull) << 11 + + out |= u32(config.fRtsControl) << 12 + + out |= u32(config.fAbortOnError) << 14 + + dcb.settings = out + + dcb.BaudRate = config.BaudRate + dcb.ByteSize = config.ByteSize + dcb.Parity = config.Parity + dcb.StopBits = config.StopBits + dcb.XonChar = config.XonChar + dcb.XoffChar = config.XoffChar + dcb.ErrorChar = config.ErrorChar + dcb.EvtChar = config.EvtChar + + dcb.DCBlength = size_of(DCB) +} +get_dcb_config :: proc "contextless" (dcb: DCB) -> (config: DCB_Config) { + config.fParity = bool((dcb.settings >> 1) & 0x01) + config.fOutxCtsFlow = bool((dcb.settings >> 2) & 0x01) + config.fOutxDsrFlow = bool((dcb.settings >> 3) & 0x01) + + config.fDtrControl = DTR_Control((dcb.settings >> 4) & 0x02) + + config.fDsrSensitivity = bool((dcb.settings >> 6) & 0x01) + config.fTXContinueOnXoff = bool((dcb.settings >> 7) & 0x01) + config.fOutX = bool((dcb.settings >> 8) & 0x01) + config.fInX = bool((dcb.settings >> 9) & 0x01) + config.fErrorChar = bool((dcb.settings >> 10) & 0x01) + config.fNull = bool((dcb.settings >> 11) & 0x01) + + config.fRtsControl = RTS_Control((dcb.settings >> 12) & 0x02) + + config.fAbortOnError = bool((dcb.settings >> 14) & 0x01) + + config.BaudRate = dcb.BaudRate + config.ByteSize = dcb.ByteSize + config.Parity = dcb.Parity + config.StopBits = dcb.StopBits + config.XonChar = dcb.XonChar + config.XoffChar = dcb.XoffChar + config.ErrorChar = dcb.ErrorChar + config.EvtChar = dcb.EvtChar + + return +} + +// NOTE(tetra): See get_dcb_config() and init_dcb_with_config() for help with initializing this. +DCB :: struct { + DCBlength: DWORD, // NOTE(tetra): Must be set to size_of(DCB). + BaudRate: DWORD, + settings: u32, // NOTE(tetra): These are bitfields in the C struct. + wReserved: WORD, + XOnLim: WORD, + XOffLim: WORD, + ByteSize: BYTE, + Parity: Parity, + StopBits: Stop_Bits, + XonChar: byte, + XoffChar: byte, + ErrorChar: byte, + EofChar: byte, + EvtChar: byte, + wReserved1: WORD, +} + +@(default_calling_convention="stdcall") +foreign kernel32 { + GetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL --- + SetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL --- +} \ No newline at end of file diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index 1f56431c1..9f1865d46 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -3,22 +3,22 @@ package all // Imports every package // This is useful for knowing what exists and producing documentation with `odin doc` -import bufio "core:bufio" -import bytes "core:bytes" +import bufio "core:bufio" +import bytes "core:bytes" -import c "core:c" -import libc "core:c/libc" +import c "core:c" +import libc "core:c/libc" -import compress "core:compress" -import shoco "core:compress/shoco" -import gzip "core:compress/gzip" -import zlib "core:compress/zlib" +import compress "core:compress" +import shoco "core:compress/shoco" +import gzip "core:compress/gzip" +import zlib "core:compress/zlib" -import bit_array "core:container/bit_array" -import priority_queue "core:container/priority_queue" -import queue "core:container/queue" -import small_array "core:container/small_array" -import lru "core:container/lru" +import bit_array "core:container/bit_array" +import priority_queue "core:container/priority_queue" +import queue "core:container/queue" +import small_array "core:container/small_array" +import lru "core:container/lru" import crypto "core:crypto" import blake "core:crypto/blake" @@ -27,7 +27,7 @@ import blake2s "core:crypto/blake2s" import chacha20 "core:crypto/chacha20" import chacha20poly1305 "core:crypto/chacha20poly1305" import gost "core:crypto/gost" -import groestl "core:crypto/groestl" +import groestl "core:crypto/groestl" import haval "core:crypto/haval" import jh "core:crypto/jh" import keccak "core:crypto/keccak" @@ -48,73 +48,74 @@ import crypto_util "core:crypto/util" import whirlpool "core:crypto/whirlpool" import x25519 "core:crypto/x25519" -import dynlib "core:dynlib" +import dynlib "core:dynlib" -import base32 "core:encoding/base32" -import base64 "core:encoding/base64" -import csv "core:encoding/csv" -import hxa "core:encoding/hxa" -import json "core:encoding/json" -import varint "core:encoding/varint" -import xml "core:encoding/xml" +import base32 "core:encoding/base32" +import base64 "core:encoding/base64" +import csv "core:encoding/csv" +import hxa "core:encoding/hxa" +import json "core:encoding/json" +import varint "core:encoding/varint" +import xml "core:encoding/xml" -import fmt "core:fmt" -import hash "core:hash" +import fmt "core:fmt" +import hash "core:hash" -import image "core:image" -import netpbm "core:image/netpbm" -import png "core:image/png" -import qoi "core:image/qoi" -import tga "core:image/tga" +import image "core:image" +import netpbm "core:image/netpbm" +import png "core:image/png" +import qoi "core:image/qoi" +import tga "core:image/tga" -import io "core:io" -import log "core:log" +import io "core:io" +import log "core:log" -import math "core:math" -import big "core:math/big" -import bits "core:math/bits" -import fixed "core:math/fixed" -import linalg "core:math/linalg" -import glm "core:math/linalg/glsl" -import hlm "core:math/linalg/hlsl" -import rand "core:math/rand" +import math "core:math" +import big "core:math/big" +import bits "core:math/bits" +import fixed "core:math/fixed" +import linalg "core:math/linalg" +import glm "core:math/linalg/glsl" +import hlm "core:math/linalg/hlsl" +import noise "core:math/noise" +import rand "core:math/rand" -import mem "core:mem" +import mem "core:mem" // import virtual "core:mem/virtual" -import ast "core:odin/ast" -import doc_format "core:odin/doc-format" -import odin_format "core:odin/format" -import odin_parser "core:odin/parser" -import odin_printer "core:odin/printer" -import odin_tokenizer "core:odin/tokenizer" +import ast "core:odin/ast" +import doc_format "core:odin/doc-format" +import odin_format "core:odin/format" +import odin_parser "core:odin/parser" +import odin_printer "core:odin/printer" +import odin_tokenizer "core:odin/tokenizer" -import os "core:os" +import os "core:os" -import slashpath "core:path/slashpath" -import filepath "core:path/filepath" +import slashpath "core:path/slashpath" +import filepath "core:path/filepath" -import reflect "core:reflect" -import runtime "core:runtime" -import simd "core:simd" -import slice "core:slice" -import slice_heap "core:slice/heap" -import sort "core:sort" -import strconv "core:strconv" -import strings "core:strings" -import sync "core:sync" -import testing "core:testing" -import scanner "core:text/scanner" -import i18n "core:text/i18n" -import thread "core:thread" -import time "core:time" +import reflect "core:reflect" +import runtime "core:runtime" +import simd "core:simd" +import slice "core:slice" +import slice_heap "core:slice/heap" +import sort "core:sort" +import strconv "core:strconv" +import strings "core:strings" +import sync "core:sync" +import testing "core:testing" +import scanner "core:text/scanner" +import i18n "core:text/i18n" +import thread "core:thread" +import time "core:time" -import sysinfo "core:sys/info" +import sysinfo "core:sys/info" -import unicode "core:unicode" -import utf8 "core:unicode/utf8" -import utf8string "core:unicode/utf8/utf8string" -import utf16 "core:unicode/utf16" +import unicode "core:unicode" +import utf8 "core:unicode/utf8" +import utf8string "core:unicode/utf8/utf8string" +import utf16 "core:unicode/utf16" main :: proc(){} @@ -183,6 +184,7 @@ _ :: fixed _ :: linalg _ :: glm _ :: hlm +_ :: noise _ :: rand _ :: mem _ :: ast diff --git a/examples/all/all_vendor.odin b/examples/all/all_vendor.odin index 7da2e501b..22be12ef9 100644 --- a/examples/all/all_vendor.odin +++ b/examples/all/all_vendor.odin @@ -1,27 +1,27 @@ package all -import botan "vendor:botan" -import ENet "vendor:ENet" -import ggpo "vendor:ggpo" -import gl "vendor:OpenGL" -import glfw "vendor:glfw" -import microui "vendor:microui" -import miniaudio "vendor:miniaudio" -import PM "vendor:portmidi" -import rl "vendor:raylib" -import exr "vendor:OpenEXRCore" +import botan "vendor:botan" +import ENet "vendor:ENet" +import ggpo "vendor:ggpo" +import gl "vendor:OpenGL" +import glfw "vendor:glfw" +import microui "vendor:microui" +import miniaudio "vendor:miniaudio" +import PM "vendor:portmidi" +import rl "vendor:raylib" +import exr "vendor:OpenEXRCore" -import SDL "vendor:sdl2" -import SDLNet "vendor:sdl2/net" -import IMG "vendor:sdl2/image" -import MIX "vendor:sdl2/mixer" -import TTF "vendor:sdl2/ttf" +import SDL "vendor:sdl2" +import SDLNet "vendor:sdl2/net" +import IMG "vendor:sdl2/image" +import MIX "vendor:sdl2/mixer" +import TTF "vendor:sdl2/ttf" -import vk "vendor:vulkan" +import vk "vendor:vulkan" -import NS "vendor:darwin/Foundation" -import MTL "vendor:darwin/Metal" -import CA "vendor:darwin/QuartzCore" +import NS "vendor:darwin/Foundation" +import MTL "vendor:darwin/Metal" +import CA "vendor:darwin/QuartzCore" _ :: botan _ :: ENet @@ -33,12 +33,15 @@ _ :: miniaudio _ :: PM _ :: rl _ :: exr + _ :: SDL _ :: SDLNet _ :: IMG _ :: MIX _ :: TTF + _ :: vk + _ :: NS _ :: MTL -_ :: CA +_ :: CA \ No newline at end of file diff --git a/examples/all/all_vendor_cmark.odin b/examples/all/all_vendor_cmark.odin new file mode 100644 index 000000000..5faf47efc --- /dev/null +++ b/examples/all/all_vendor_cmark.odin @@ -0,0 +1,5 @@ +//+build windows, linux +package all + +import cm "vendor:commonmark" +_ :: cm diff --git a/examples/all/all_vendor_zlib.odin b/examples/all/all_vendor_zlib.odin new file mode 100644 index 000000000..6004bed50 --- /dev/null +++ b/examples/all/all_vendor_zlib.odin @@ -0,0 +1,5 @@ +//+build windows, linux +package all + +import zlib "vendor:zlib" +_ :: zlib diff --git a/src/build_settings.cpp b/src/build_settings.cpp index fc5a9d19f..02de22ec4 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -986,6 +986,15 @@ String get_fullpath_relative(gbAllocator a, String base_dir, String path) { gb_memmove(str+i, path.text, path.len); i += path.len; str[i] = 0; + // IMPORTANT NOTE(bill): Remove trailing path separators + // this is required to make sure there is a conventional + // notation for the path + for (/**/; i > 0; i--) { + u8 c = str[i-1]; + if (c != '/' && c != '\\') { + break; + } + } String res = make_string(str, i); res = string_trim_whitespace(res); @@ -1302,13 +1311,16 @@ void enable_target_feature(TokenPos pos, String const &target_feature_list) { defer (mutex_unlock(&bc->target_features_mutex)); auto items = split_by_comma(target_feature_list); - array_free(&items); for_array(i, items) { String const &item = items.data[i]; if (!check_target_feature_is_valid(pos, item)) { error(pos, "Target feature '%.*s' is not valid", LIT(item)); + continue; } + + string_set_add(&bc->target_features_set, item); } + array_free(&items); } @@ -1331,7 +1343,7 @@ char const *target_features_set_to_cstring(gbAllocator allocator, bool with_quot if (with_quotes) features[len++] = '"'; String feature = build_context.target_features_set.entries[i].value; - gb_memmove(features, feature.text, feature.len); + gb_memmove(features + len, feature.text, feature.len); len += feature.len; if (with_quotes) features[len++] = '"'; } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index f0db6afc4..09294c93a 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1614,6 +1614,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_type_info_of: case BuiltinProc_typeid_of: case BuiltinProc_len: + case BuiltinProc_cap: case BuiltinProc_min: case BuiltinProc_max: case BuiltinProc_type_is_subtype_of: @@ -1696,16 +1697,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 return check_builtin_procedure_directive(c, operand, call, type_hint); case BuiltinProc_len: - check_expr_or_type(c, operand, ce->args[0]); - if (operand->mode == Addressing_Invalid) { - return false; - } - /* fallthrough */ - case BuiltinProc_cap: { // len :: proc(Type) -> int // cap :: proc(Type) -> int + check_expr_or_type(c, operand, ce->args[0]); + if (operand->mode == Addressing_Invalid) { + return false; + } Type *op_type = type_deref(operand->type); Type *type = t_int; @@ -1749,11 +1748,17 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 mode = Addressing_Value; } else if (is_type_map(op_type)) { mode = Addressing_Value; - } else if (operand->mode == Addressing_Type && is_type_enum(op_type) && id == BuiltinProc_len) { + } else if (operand->mode == Addressing_Type && is_type_enum(op_type)) { Type *bt = base_type(op_type); - mode = Addressing_Constant; - value = exact_value_i64(bt->Enum.fields.count); - type = t_untyped_integer; + mode = Addressing_Constant; + type = t_untyped_integer; + if (id == BuiltinProc_len) { + value = exact_value_i64(bt->Enum.fields.count); + } else { + GB_ASSERT(id == BuiltinProc_cap); + value = exact_value_sub(*bt->Enum.max_value, *bt->Enum.min_value); + value = exact_value_increment_one(value); + } } else if (is_type_struct(op_type)) { Type *bt = base_type(op_type); if (bt->Struct.soa_kind == StructSoa_Fixed) { @@ -1899,6 +1904,21 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 gb_string_free(t); return false; } + + Type *bt = base_type(type); + if (bt->kind == Type_Struct && bt->Struct.scope != nullptr) { + if (is_type_polymorphic(bt)) { + gbString t = type_to_string(type); + error(field_arg, "Cannot use '%.*s' on an unspecialized polymorphic struct type, got '%s'", LIT(builtin_name), t); + gb_string_free(t); + return false; + } else if (bt->Struct.fields.count == 0 && bt->Struct.node == nullptr) { + gbString t = type_to_string(type); + error(field_arg, "Cannot use '%.*s' on incomplete struct declaration, got '%s'", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + } Selection sel = lookup_field(type, field_name, false); if (sel.entity == nullptr) { @@ -3665,8 +3685,92 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 gb_string_free(xts); } + Type *type = default_type(x.type); operand->mode = Addressing_Value; - operand->type = default_type(x.type); + operand->type = type; + + if (id == BuiltinProc_reverse_bits) { + // make runtime only for the time being + } else if (x.mode == Addressing_Constant && x.value.kind == ExactValue_Integer) { + convert_to_typed(c, &x, type); + if (x.mode == Addressing_Invalid) { + return false; + } + + ExactValue res = {}; + + i64 sz = type_size_of(x.type); + u64 bit_size = sz*8; + u64 rop64[4] = {}; // 2 u64 is the maximum we will ever need, so doubling it will ne fine + u8 *rop = cast(u8 *)rop64; + + size_t max_count = 0; + size_t written = 0; + size_t size = 1; + size_t nails = 0; + mp_endian endian = MP_LITTLE_ENDIAN; + + max_count = mp_pack_count(&x.value.value_integer, nails, size); + GB_ASSERT(sz >= cast(i64)max_count); + + mp_err err = mp_pack(rop, max_count, &written, MP_LSB_FIRST, size, endian, nails, &x.value.value_integer); + GB_ASSERT(err == MP_OKAY); + + if (id == BuiltinProc_reverse_bits) { + // TODO(bill): Should this even be allowed at compile time? + } else { + u64 v = 0; + switch (id) { + case BuiltinProc_count_ones: + case BuiltinProc_count_zeros: + switch (sz) { + case 1: v = bit_set_count(cast(u32)rop[0]); break; + case 2: v = bit_set_count(cast(u32)*(u16 *)rop); break; + case 4: v = bit_set_count(*(u32 *)rop); break; + case 8: v = bit_set_count(rop64[0]); break; + case 16: + v += bit_set_count(rop64[0]); + v += bit_set_count(rop64[1]); + break; + default: GB_PANIC("Unhandled sized"); + } + if (id == BuiltinProc_count_zeros) { + // flip the result + v = bit_size - v; + } + break; + case BuiltinProc_count_trailing_zeros: + for (u64 i = 0; i < bit_size; i++) { + u8 b = cast(u8)(i & 7); + u8 j = cast(u8)(i >> 3); + if (rop[j] & (1 << b)) { + break; + } + v += 1; + } + break; + case BuiltinProc_count_leading_zeros: + for (u64 i = bit_size-1; i < bit_size; i--) { + u8 b = cast(u8)(i & 7); + u8 j = cast(u8)(i >> 3); + if (rop[j] & (1 << b)) { + break; + } + v += 1; + } + break; + } + + + res = exact_value_u64(v); + } + + if (res.kind != ExactValue_Invalid) { + operand->mode = Addressing_Constant; + operand->value = res; + } + } + } break; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 38d17c131..0686d9cb2 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1060,6 +1060,8 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co type_extra = gb_string_append_fmt(type_extra, " (package %.*s)", LIT(type_pkg->name)); } } + + ERROR_BLOCK(); error(operand->expr, "Cannot assign value '%s' of type '%s%s' to '%s%s' in %.*s", expr_str, @@ -1143,6 +1145,12 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, return true; } return is_polymorphic_type_assignable(c, poly->Pointer.elem, source->Pointer.elem, true, modify_type); + } else if (source->kind == Type_MultiPointer) { + isize level = check_is_assignable_to_using_subtype(source->MultiPointer.elem, poly->Pointer.elem); + if (level > 0) { + return true; + } + return is_polymorphic_type_assignable(c, poly->Pointer.elem, source->MultiPointer.elem, true, modify_type); } return false; @@ -1153,6 +1161,12 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, return true; } return is_polymorphic_type_assignable(c, poly->MultiPointer.elem, source->MultiPointer.elem, true, modify_type); + } else if (source->kind == Type_Pointer) { + isize level = check_is_assignable_to_using_subtype(source->Pointer.elem, poly->MultiPointer.elem); + if (level > 0) { + return true; + } + return is_polymorphic_type_assignable(c, poly->MultiPointer.elem, source->Pointer.elem, true, modify_type); } return false; case Type_Array: @@ -1348,7 +1362,13 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, if (source->kind == Type_Map) { bool key = is_polymorphic_type_assignable(c, poly->Map.key, source->Map.key, true, modify_type); bool value = is_polymorphic_type_assignable(c, poly->Map.value, source->Map.value, true, modify_type); - return key || value; + if (key || value) { + poly->Map.entry_type = nullptr; + poly->Map.internal_type = nullptr; + poly->Map.lookup_result_type = nullptr; + init_map_internal_types(poly); + return true; + } } return false; @@ -1965,10 +1985,18 @@ void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type if (are_types_identical(s, d)) { error_line("\tSuggestion: the array expression may be sliced with %s[:]\n", a); } - } else if (are_types_identical(src, dst)) { + } else if (is_type_dynamic_array(src) && is_type_slice(dst)) { + Type *s = src->DynamicArray.elem; + Type *d = dst->Slice.elem; + if (are_types_identical(s, d)) { + error_line("\tSuggestion: the dynamic array expression may be sliced with %s[:]\n", a); + } + }else if (are_types_identical(src, dst) && !are_types_identical(o->type, type)) { error_line("\tSuggestion: the expression may be directly casted to type %s\n", b); } else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) { error_line("\tSuggestion: a string may be transmuted to %s\n", b); + error_line("\t This is an UNSAFE operation as string data is assumed to be immutable, \n"); + error_line("\t whereas slices in general are assumed to be mutable.\n"); } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) { error_line("\tSuggestion: the expression may be casted to %s\n", b); } @@ -2028,7 +2056,9 @@ bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) { gbString a = expr_to_string(o->expr); gbString b = type_to_string(type); gbString c = type_to_string(o->type); + gbString s = exact_value_to_string(o->value); defer( + gb_string_free(s); gb_string_free(c); gb_string_free(b); gb_string_free(a); @@ -2037,13 +2067,15 @@ bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) { if (is_type_numeric(o->type) && is_type_numeric(type)) { if (!is_type_integer(o->type) && is_type_integer(type)) { - error(o->expr, "'%s' truncated to '%s'", a, b); + error(o->expr, "'%s' truncated to '%s', got %s", a, b, s); } else { - error(o->expr, "Cannot convert numeric value '%s' to '%s' from '%s", a, b, c); + ERROR_BLOCK(); + error(o->expr, "Cannot convert numeric value '%s' to '%s' from '%s', got %s", a, b, c, s); check_assignment_error_suggestion(ctx, o, type); } } else { - error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c); + ERROR_BLOCK(); + error(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s", a, b, c, s); check_assignment_error_suggestion(ctx, o, type); } return false; @@ -3904,7 +3936,9 @@ bool check_index_value(CheckerContext *c, Type *main_type, bool open_range, Ast } } else if (!is_type_integer(operand.type) && !is_type_enum(operand.type)) { gbString expr_str = expr_to_string(operand.expr); - error(operand.expr, "Index '%s' must be an integer", expr_str); + gbString type_str = type_to_string(operand.type); + error(operand.expr, "Index '%s' must be an integer, got %s", expr_str, type_str); + gb_string_free(type_str); gb_string_free(expr_str); if (value) *value = 0; return false; @@ -3914,8 +3948,9 @@ bool check_index_value(CheckerContext *c, Type *main_type, bool open_range, Ast (c->state_flags & StateFlag_no_bounds_check) == 0) { BigInt i = exact_value_to_integer(operand.value).value_integer; if (i.sign && !is_type_enum(index_type) && !is_type_multi_pointer(main_type)) { + String idx_str = big_int_to_string(temporary_allocator(), &i); gbString expr_str = expr_to_string(operand.expr); - error(operand.expr, "Index '%s' cannot be a negative value", expr_str); + error(operand.expr, "Index '%s' cannot be a negative value, got %.*s", expr_str, LIT(idx_str)); gb_string_free(expr_str); if (value) *value = 0; return false; @@ -3946,7 +3981,7 @@ bool check_index_value(CheckerContext *c, Type *main_type, bool open_range, Ast if (out_of_bounds) { gbString expr_str = expr_to_string(operand.expr); if (lo_str.len > 0) { - error(operand.expr, "Index '%s' is out of bounds range %.*s .. %.*s", expr_str, LIT(lo_str), LIT(hi_str)); + error(operand.expr, "Index '%s' is out of bounds range %.*s ..= %.*s", expr_str, LIT(lo_str), LIT(hi_str)); } else { gbString index_type_str = type_to_string(index_type); error(operand.expr, "Index '%s' is out of bounds range of enum type %s", expr_str, index_type_str); @@ -3976,8 +4011,9 @@ bool check_index_value(CheckerContext *c, Type *main_type, bool open_range, Ast } if (out_of_bounds) { + String idx_str = big_int_to_string(temporary_allocator(), &i); gbString expr_str = expr_to_string(operand.expr); - error(operand.expr, "Index '%s' is out of bounds range 0..<%lld", expr_str, max_count); + error(operand.expr, "Index '%s' is out of bounds range 0..<%lld, got %.*s", expr_str, max_count, LIT(idx_str)); gb_string_free(expr_str); return false; } @@ -4022,6 +4058,7 @@ ExactValue get_constant_field_single(CheckerContext *c, ExactValue value, i32 in if (cl->elems[0]->kind == Ast_FieldValue) { if (is_type_struct(node->tav.type)) { + bool found = false; for_array(i, cl->elems) { Ast *elem = cl->elems[i]; if (elem->kind != Ast_FieldValue) { @@ -4033,9 +4070,14 @@ ExactValue get_constant_field_single(CheckerContext *c, ExactValue value, i32 in defer (array_free(&sub_sel.index)); if (sub_sel.index[0] == index) { value = fv->value->tav.value; + found = true; break; } } + if (!found) { + // Use the zero value if it is not found + value = {}; + } } else if (is_type_array(node->tav.type) || is_type_enumerated_array(node->tav.type)) { for_array(i, cl->elems) { Ast *elem = cl->elems[i]; @@ -4677,7 +4719,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ switch (entity->kind) { case Entity_Constant: - operand->value = entity->Constant.value; + operand->value = entity->Constant.value; operand->mode = Addressing_Constant; if (operand->value.kind == ExactValue_Procedure) { Entity *proc = strip_entity_wrapping(operand->value.value_procedure); @@ -9064,7 +9106,20 @@ ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_h o->type = t->RelativeSlice.slice_type; if (o->mode != Addressing_Variable) { gbString str = expr_to_string(node); - error(node, "Cannot relative slice '%s', value is not addressable", str); + error(node, "Cannot relative slice '%s', as value is not addressable", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + break; + + case Type_EnumeratedArray: + { + gbString str = expr_to_string(o->expr); + gbString type_str = type_to_string(o->type); + error(o->expr, "Cannot slice '%s' of type '%s', as enumerated arrays cannot be sliced", str, type_str); + gb_string_free(type_str); gb_string_free(str); o->mode = Addressing_Invalid; o->expr = node; diff --git a/src/check_type.cpp b/src/check_type.cpp index c024e0842..2ffe04342 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2211,7 +2211,6 @@ void init_map_internal_types(Type *type) { GB_ASSERT(type->kind == Type_Map); init_map_entry_type(type); if (type->Map.internal_type != nullptr) return; - if (type->Map.generated_struct_type != nullptr) return; Type *key = type->Map.key; Type *value = type->Map.value; @@ -2239,7 +2238,6 @@ void init_map_internal_types(Type *type) { generated_struct_type->Struct.fields = fields; type_set_offsets(generated_struct_type); - type->Map.generated_struct_type = generated_struct_type; type->Map.internal_type = generated_struct_type; type->Map.lookup_result_type = make_optional_ok_type(value); } diff --git a/src/checker.cpp b/src/checker.cpp index cdc0630bf..5bdc5b010 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1922,7 +1922,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) { init_map_internal_types(bt); add_type_info_type_internal(c, bt->Map.key); add_type_info_type_internal(c, bt->Map.value); - add_type_info_type_internal(c, bt->Map.generated_struct_type); + add_type_info_type_internal(c, bt->Map.internal_type); break; case Type_Tuple: @@ -2144,7 +2144,7 @@ void add_min_dep_type_info(Checker *c, Type *t) { init_map_internal_types(bt); add_min_dep_type_info(c, bt->Map.key); add_min_dep_type_info(c, bt->Map.value); - add_min_dep_type_info(c, bt->Map.generated_struct_type); + add_min_dep_type_info(c, bt->Map.internal_type); break; case Type_Tuple: @@ -2831,23 +2831,12 @@ void init_core_source_code_location(Checker *c) { } void init_core_map_type(Checker *c) { - if (t_map_hash == nullptr) { - Entity *e = find_core_entity(c, str_lit("Map_Hash")); - if (e->state == EntityState_Unresolved) { - check_entity_decl(&c->builtin_ctx, e, nullptr, nullptr); - } - t_map_hash = e->type; - GB_ASSERT(t_map_hash != nullptr); - } - - if (t_map_header == nullptr) { - Entity *e = find_core_entity(c, str_lit("Map_Header")); - if (e->state == EntityState_Unresolved) { - check_entity_decl(&c->builtin_ctx, e, nullptr, nullptr); - } - t_map_header = e->type; - GB_ASSERT(t_map_header != nullptr); + if (t_map_hash != nullptr) { + return; } + t_map_hash = find_core_type(c, str_lit("Map_Hash")); + t_map_header = find_core_type(c, str_lit("Map_Header")); + t_map_header_table = find_core_type(c, str_lit("Map_Header_Table")); } void init_preload(Checker *c) { diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 175cb61f6..1572b564f 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -1,8 +1,5 @@ #include -// TODO(bill): Big numbers -// IMPORTANT TODO(bill): This needs to be completely fixed!!!!!!!! - gb_global BlockingMutex hash_exact_value_mutex; struct Ast; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 4eb343fa7..5502aaa58 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -500,52 +500,51 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A return value; } -lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type) { - GB_ASSERT_MSG(is_type_pointer(map_val_ptr.type), "%s", type_to_string(map_val_ptr.type)); - lbAddr h = lb_add_local_generated(p, t_map_header, false); // all the values will be initialzed later +lbValue lb_gen_map_header_table_internal(lbProcedure *p, Type *map_type) { + lbModule *m = p->module; + map_type = base_type(map_type); GB_ASSERT(map_type->kind == Type_Map); - Type *key_type = map_type->Map.key; - Type *val_type = map_type->Map.value; - gb_unused(val_type); + lbAddr *found = map_get(&m->map_header_table_map, map_type); + if (found) { + return lb_addr_load(p, *found); + } GB_ASSERT(map_type->Map.entry_type->kind == Type_Struct); - map_type->Map.entry_type->cached_size = -1; - map_type->Map.entry_type->Struct.are_offsets_set = false; - i64 entry_size = type_size_of (map_type->Map.entry_type); i64 entry_align = type_align_of (map_type->Map.entry_type); - + i64 key_offset = type_offset_of(map_type->Map.entry_type, 2); i64 key_size = type_size_of (map_type->Map.key); i64 value_offset = type_offset_of(map_type->Map.entry_type, 3); i64 value_size = type_size_of (map_type->Map.value); - - - Type *map_header_base = base_type(t_map_header); - GB_ASSERT(map_header_base->Struct.fields.count == 8); - Type *raw_map_ptr_type = map_header_base->Struct.fields[0]->type; - LLVMValueRef const_values[8] = {}; - const_values[0] = LLVMConstNull(lb_type(p->module, raw_map_ptr_type)); - const_values[1] = lb_get_equal_proc_for_type(p->module, key_type) .value; - const_values[2] = lb_const_int(p->module, t_int, entry_size) .value; - const_values[3] = lb_const_int(p->module, t_int, entry_align) .value; - const_values[4] = lb_const_int(p->module, t_uintptr, key_offset) .value; - const_values[5] = lb_const_int(p->module, t_int, key_size) .value; - const_values[6] = lb_const_int(p->module, t_uintptr, value_offset).value; - const_values[7] = lb_const_int(p->module, t_int, value_size) .value; - - LLVMValueRef const_value = llvm_const_named_struct(p->module, t_map_header, const_values, gb_count_of(const_values)); - LLVMBuildStore(p->builder, const_value, h.addr.value); - - // NOTE(bill): Removes unnecessary allocation if split gep - lbValue gep0 = lb_emit_struct_ep(p, h.addr, 0); - lbValue m = lb_emit_conv(p, map_val_ptr, type_deref(gep0.type)); - lb_emit_store(p, gep0, m); - return lb_addr_load(p, h); + Type *key_type = map_type->Map.key; + Type *val_type = map_type->Map.value; + gb_unused(val_type); + + Type *st = base_type(t_map_header_table); + GB_ASSERT(st->Struct.fields.count == 7); + + LLVMValueRef const_values[7] = {}; + const_values[0] = lb_get_equal_proc_for_type(m, key_type) .value; + const_values[1] = lb_const_int(m, t_int, entry_size) .value; + const_values[2] = lb_const_int(m, t_int, entry_align) .value; + const_values[3] = lb_const_int(m, t_uintptr, key_offset) .value; + const_values[4] = lb_const_int(m, t_int, key_size) .value; + const_values[5] = lb_const_int(m, t_uintptr, value_offset).value; + const_values[6] = lb_const_int(m, t_int, value_size) .value; + + LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_header_table, const_values, gb_count_of(const_values)); + lbValue res = {llvm_res, t_map_header_table}; + + lbAddr addr = lb_add_global_generated(m, t_map_header_table, res, nullptr); + lb_make_global_private_const(addr); + + map_set(&m->map_header_table_map, map_type, addr); + return lb_addr_load(p, addr); } lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) { @@ -595,14 +594,12 @@ lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) { return hashed_key; } -lbValue lb_gen_map_hash(lbProcedure *p, lbValue key, Type *key_type) { - lbAddr v = lb_add_local_generated(p, t_map_hash, true); - lbValue vp = lb_addr_get_ptr(p, v); - key = lb_emit_conv(p, key, key_type); - +lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue *key_ptr_) { lbValue key_ptr = lb_address_from_load_or_generate_local(p, key); key_ptr = lb_emit_conv(p, key_ptr, t_rawptr); + if (key_ptr_) *key_ptr_ = key_ptr; + lbValue hashed_key = lb_const_hash(p->module, key, key_type); if (hashed_key.value == nullptr) { lbValue hasher = lb_get_hasher_proc_for_type(p->module, key_type); @@ -613,32 +610,62 @@ lbValue lb_gen_map_hash(lbProcedure *p, lbValue key, Type *key_type) { hashed_key = lb_emit_call(p, hasher, args); } - lb_emit_store(p, lb_emit_struct_ep(p, vp, 0), hashed_key); - lb_emit_store(p, lb_emit_struct_ep(p, vp, 1), key_ptr); - - return lb_addr_load(p, v); + return hashed_key; } -void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *map_type, - lbValue map_key, lbValue map_value, Ast *node) { +lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue const &key) { + Type *map_type = base_type(type_deref(map_ptr.type)); + + lbValue key_ptr = {}; + auto args = array_make(permanent_allocator(), 4); + args[0] = lb_emit_conv(p, map_ptr, t_rawptr); + args[1] = lb_gen_map_header_table_internal(p, map_type); + args[2] = lb_gen_map_key_hash(p, key, map_type->Map.key, &key_ptr); + args[3] = key_ptr; + + lbValue ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args); + + return lb_emit_conv(p, ptr, alloc_type_pointer(map_type->Map.value)); +} + +void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbValue const &map_ptr, Type *map_type, + lbValue const &map_key, lbValue const &map_value, Ast *node) { map_type = base_type(map_type); GB_ASSERT(map_type->kind == Type_Map); - lbValue h = lb_gen_map_header(p, addr.addr, map_type); - lbValue key = lb_gen_map_hash(p, map_key, map_type->Map.key); + lbValue key_ptr = {}; + lbValue key_hash = lb_gen_map_key_hash(p, map_key, map_type->Map.key, &key_ptr); lbValue v = lb_emit_conv(p, map_value, map_type->Map.value); lbAddr value_addr = lb_add_local_generated(p, v.type, false); lb_addr_store(p, value_addr, v); - auto args = array_make(permanent_allocator(), 4); - args[0] = h; - args[1] = key; - args[2] = lb_emit_conv(p, value_addr.addr, t_rawptr); - args[3] = lb_emit_source_code_location(p, node); + auto args = array_make(permanent_allocator(), 6); + args[0] = lb_emit_conv(p, map_ptr, t_rawptr); + args[1] = lb_gen_map_header_table_internal(p, map_type); + args[2] = key_hash; + args[3] = key_ptr; + args[4] = lb_emit_conv(p, value_addr.addr, t_rawptr); + args[5] = lb_emit_source_code_location(p, node); lb_emit_runtime_call(p, "__dynamic_map_set", args); } +void lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos) { + GB_ASSERT(!build_context.no_dynamic_literals); + + String proc_name = {}; + if (p->entity) { + proc_name = p->entity->token.string; + } + + auto args = array_make(permanent_allocator(), 4); + args[0] = lb_emit_conv(p, map_ptr, t_rawptr); + args[1] = lb_gen_map_header_table_internal(p, type_deref(map_ptr.type)); + args[2] = lb_const_int(p->module, t_int, capacity); + args[3] = lb_emit_source_code_location(p, proc_name, pos); + lb_emit_runtime_call(p, "__dynamic_map_reserve", args); +} + struct lbGlobalVariable { lbValue var; @@ -780,6 +807,9 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start var->init = init; } else if (lb_is_const_or_global(init)) { if (!var->is_initialized) { + if (is_type_proc(init.type)) { + init.value = LLVMConstPointerCast(init.value, lb_type(p->module, init.type)); + } LLVMSetInitializer(var->var.value, init.value); var->is_initialized = true; continue; diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 79f0f37e7..e69d3a6ed 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -159,6 +159,8 @@ struct lbModule { StringMap objc_classes; StringMap objc_selectors; + + PtrMap map_header_table_map; }; struct lbGenerator { @@ -443,9 +445,11 @@ String lb_get_const_string(lbModule *m, lbValue value); lbValue lb_generate_local_array(lbProcedure *p, Type *elem_type, i64 count, bool zero_init=true); lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String prefix, i64 id); -lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type); -lbValue lb_gen_map_hash(lbProcedure *p, lbValue key, Type *key_type); -void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *map_type, lbValue map_key, lbValue map_value, Ast *node); +lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue *key_ptr_); + +lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue const &key); +void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbValue const &map_ptr, Type *map_type, lbValue const &map_key, lbValue const &map_value, Ast *node); +void lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos); lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e); lbValue lb_find_value_from_entity(lbModule *m, Entity *e); diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 7c92c517c..6c046fa4a 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1423,15 +1423,9 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { switch (rt->kind) { case Type_Map: { - lbValue addr = lb_address_from_load_or_generate_local(p, right); - lbValue h = lb_gen_map_header(p, addr, rt); - lbValue key = lb_gen_map_hash(p, left, rt->Map.key); - - auto args = array_make(permanent_allocator(), 2); - args[0] = h; - args[1] = key; - - lbValue ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args); + lbValue map_ptr = lb_address_from_load_or_generate_local(p, right); + lbValue key = left; + lbValue ptr = lb_internal_dynamic_map_get_ptr(p, map_ptr, key); if (be->op.kind == Token_in) { return lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_NotEq, ptr), t_bool); } else { @@ -3676,16 +3670,14 @@ lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) { if (is_type_map(t)) { lbAddr map_addr = lb_build_addr(p, ie->expr); - lbValue map_val = lb_addr_load(p, map_addr); - if (deref) { - map_val = lb_emit_load(p, map_val); - } - lbValue key = lb_build_expr(p, ie->index); key = lb_emit_conv(p, key, t->Map.key); Type *result_type = type_of_expr(expr); - lbValue map_ptr = lb_address_from_load_or_generate_local(p, map_val); + lbValue map_ptr = lb_addr_get_ptr(p, map_addr); + if (is_type_pointer(type_deref(map_ptr.type))) { + map_ptr = lb_emit_load(p, map_ptr); + } return lb_addr_map(map_ptr, key, t, result_type); } @@ -3725,8 +3717,11 @@ lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) { ExactValue idx = exact_value_sub(index_tv.value, *t->EnumeratedArray.min_value); index = lb_const_value(p->module, index_type, idx); } else { - index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int); - index = lb_emit_arith(p, Token_Sub, index, lb_const_value(p->module, index_type, *t->EnumeratedArray.min_value), index_type); + index = lb_emit_arith(p, Token_Sub, + lb_build_expr(p, ie->index), + lb_const_value(p->module, index_type, *t->EnumeratedArray.min_value), + index_type); + index = lb_emit_conv(p, index, t_int); } } else { index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int); @@ -4136,20 +4131,16 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { break; } GB_ASSERT(!build_context.no_dynamic_literals); - { - auto args = array_make(permanent_allocator(), 3); - args[0] = lb_gen_map_header(p, v.addr, type); - args[1] = lb_const_int(p->module, t_int, 2*cl->elems.count); - args[2] = lb_emit_source_code_location(p, proc_name, pos); - lb_emit_runtime_call(p, "__dynamic_map_reserve", args); - } + + lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos); + for_array(field_index, cl->elems) { Ast *elem = cl->elems[field_index]; ast_node(fv, FieldValue, elem); lbValue key = lb_build_expr(p, fv->field); lbValue value = lb_build_expr(p, fv->value); - lb_insert_dynamic_map_key_and_value(p, v, type, key, value, elem); + lb_insert_dynamic_map_key_and_value(p, v.addr, type, key, value, elem); } break; } diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 1f8fccdcb..0dabee076 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -74,6 +74,9 @@ void lb_init_module(lbModule *m, Checker *c) { string_map_init(&m->objc_classes, a); string_map_init(&m->objc_selectors, a); + + map_init(&m->map_header_table_map, a, 0); + } bool lb_init_generator(lbGenerator *gen, Checker *c) { @@ -213,6 +216,17 @@ void lb_loop_end(lbProcedure *p, lbLoopData const &data) { } +void lb_make_global_private_const(LLVMValueRef global_data) { + LLVMSetLinkage(global_data, LLVMPrivateLinkage); + LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr); + LLVMSetGlobalConstant(global_data, true); +} +void lb_make_global_private_const(lbAddr const &addr) { + lb_make_global_private_const(addr.addr.value); +} + + + // This emits a GEP at 0, index lbValue lb_emit_epi(lbProcedure *p, lbValue const &value, isize index) { GB_ASSERT(is_type_pointer(value.type)); @@ -390,19 +404,8 @@ lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) { } switch (addr.kind) { - case lbAddr_Map: { - Type *map_type = base_type(addr.map.type); - lbValue h = lb_gen_map_header(p, addr.addr, map_type); - lbValue key = lb_gen_map_hash(p, addr.map.key, map_type->Map.key); - - auto args = array_make(permanent_allocator(), 2); - args[0] = h; - args[1] = key; - - lbValue ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args); - - return lb_emit_conv(p, ptr, alloc_type_pointer(map_type->Map.value)); - } + case lbAddr_Map: + return lb_internal_dynamic_map_get_ptr(p, addr.addr, addr.map.key); case lbAddr_RelativePointer: { Type *rel_ptr = base_type(lb_addr_type(addr)); @@ -711,7 +714,7 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) { return; } else if (addr.kind == lbAddr_Map) { - lb_insert_dynamic_map_key_and_value(p, addr, addr.map.type, addr.map.key, value, p->curr_stmt); + lb_insert_dynamic_map_key_and_value(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt); return; } else if (addr.kind == lbAddr_Context) { lbAddr old_addr = lb_find_or_generate_context_ptr(p); @@ -926,19 +929,15 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) { return; } else if (LLVMIsConstant(value.value)) { lbAddr addr = lb_add_global_generated(p->module, value.type, value, nullptr); - LLVMValueRef global_data = addr.addr.value; - // make it truly private data - LLVMSetLinkage(global_data, LLVMPrivateLinkage); - LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr); - LLVMSetGlobalConstant(global_data, true); + lb_make_global_private_const(addr); LLVMValueRef dst_ptr = ptr.value; - LLVMValueRef src_ptr = global_data; + LLVMValueRef src_ptr = addr.addr.value; src_ptr = LLVMBuildPointerCast(p->builder, src_ptr, LLVMTypeOf(dst_ptr), ""); LLVMBuildMemMove(p->builder, dst_ptr, lb_try_get_alignment(dst_ptr, 1), - src_ptr, lb_try_get_alignment(global_data, 1), + src_ptr, lb_try_get_alignment(src_ptr, 1), LLVMConstInt(LLVMInt64TypeInContext(p->module->ctx), lb_sizeof(LLVMTypeOf(value.value)), false)); return; } @@ -1059,16 +1058,11 @@ lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) { } else if (addr.kind == lbAddr_Map) { - Type *map_type = base_type(addr.map.type); + Type *map_type = base_type(type_deref(addr.addr.type)); + GB_ASSERT(map_type->kind == Type_Map); lbAddr v = lb_add_local_generated(p, map_type->Map.lookup_result_type, true); - lbValue h = lb_gen_map_header(p, addr.addr, map_type); - lbValue key = lb_gen_map_hash(p, addr.map.key, map_type->Map.key); - auto args = array_make(permanent_allocator(), 2); - args[0] = h; - args[1] = key; - - lbValue ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args); + lbValue ptr = lb_internal_dynamic_map_get_ptr(p, addr.addr, addr.map.key); lbValue ok = lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_NotEq, ptr), t_bool); lb_emit_store(p, lb_emit_struct_ep(p, v.addr, 1), ok); @@ -1513,6 +1507,7 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) { LLVMTypeRef ret = nullptr; LLVMTypeRef *params = gb_alloc_array(permanent_allocator(), LLVMTypeRef, param_count); + bool *params_by_ptr = gb_alloc_array(permanent_allocator(), bool, param_count); if (type->Proc.result_count != 0) { Type *single_ret = reduce_tuple_to_single_type(type->Proc.results); ret = lb_type(m, single_ret); @@ -1538,9 +1533,12 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) { } Type *e_type = reduce_tuple_to_single_type(e->type); + bool param_is_by_ptr = false; LLVMTypeRef param_type = nullptr; if (e->flags & EntityFlag_ByPtr) { - param_type = lb_type(m, alloc_type_pointer(e_type)); + // it will become a pointer afterwards by making it indirect + param_type = lb_type(m, e_type); + param_is_by_ptr = true; } else if (is_type_boolean(e_type) && type_size_of(e_type) <= 1) { param_type = LLVMInt1TypeInContext(m->ctx); @@ -1552,6 +1550,7 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) { } } + params_by_ptr[param_index] = param_is_by_ptr; params[param_index++] = param_type; } } @@ -1577,6 +1576,12 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) { LLVMPrintTypeToString(ft->ret.type), LLVMGetTypeContext(ft->ret.type), ft->ctx, LLVMGetGlobalContext()); } + for_array(j, ft->args) { + if (params_by_ptr[j]) { + // NOTE(bill): The parameter needs to be passed "indirectly", override it + ft->args[j].kind = lbArg_Indirect; + } + } map_set(&m->function_type_map, type, ft); LLVMTypeRef new_abi_fn_type = lb_function_type_to_llvm_raw(ft, type->Proc.c_vararg); @@ -2473,10 +2478,8 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) { LLVMTypeRef type = LLVMTypeOf(data); LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name); LLVMSetInitializer(global_data, data); - LLVMSetLinkage(global_data, LLVMPrivateLinkage); - LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr); + lb_make_global_private_const(global_data); LLVMSetAlignment(global_data, 1); - LLVMSetGlobalConstant(global_data, true); LLVMValueRef ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2); string_map_set(&m->const_strings, key, ptr); @@ -2519,10 +2522,8 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str) LLVMTypeRef type = LLVMTypeOf(data); LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name); LLVMSetInitializer(global_data, data); - LLVMSetLinkage(global_data, LLVMPrivateLinkage); - LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr); + lb_make_global_private_const(global_data); LLVMSetAlignment(global_data, 1); - LLVMSetGlobalConstant(global_data, true); LLVMValueRef ptr = nullptr; if (str.len != 0) { @@ -2558,10 +2559,8 @@ lbValue lb_find_or_add_entity_string_byte_slice_with_type(lbModule *m, String co LLVMTypeRef type = LLVMTypeOf(data); LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name); LLVMSetInitializer(global_data, data); - LLVMSetLinkage(global_data, LLVMPrivateLinkage); - LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr); + lb_make_global_private_const(global_data); LLVMSetAlignment(global_data, 1); - LLVMSetGlobalConstant(global_data, true); i64 data_len = str.len; LLVMValueRef ptr = nullptr; @@ -2669,6 +2668,7 @@ lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) { return {}; } + lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value, Entity **entity_) { GB_ASSERT(type != nullptr); type = default_type(type); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 82e577032..56ffe3fe9 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -121,8 +121,8 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) p->branch_blocks.allocator = a; p->context_stack.allocator = a; p->scope_stack.allocator = a; - map_init(&p->selector_values, a, 0); - map_init(&p->selector_addr, a, 0); + map_init(&p->selector_values, a, 0); + map_init(&p->selector_addr, a, 0); if (p->is_foreign) { lb_add_foreign_library_path(p->module, entity->Procedure.foreign_library); @@ -379,7 +379,6 @@ lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type lb_add_proc_attribute_at_index(p, offset+parameter_index, "nonnull"); lb_add_proc_attribute_at_index(p, offset+parameter_index, "nocapture"); } - return p; } @@ -577,20 +576,13 @@ void lb_begin_procedure_body(lbProcedure *p) { if (e->token.string != "") { GB_ASSERT(!is_blank_ident(e->token)); - lbAddr res = {}; - if (return_ptr_value.value != nullptr) { - lbValue ptr = return_ptr_value; - if (results->variables.count != 1) { - ptr = lb_emit_struct_ep(p, ptr, cast(i32)i); - } - - res = lb_addr(ptr); - lb_add_entity(p->module, e, ptr); - lb_add_debug_local_variable(p, ptr.value, e->type, e->token); - } else { - res = lb_add_local(p, e->type, e); - } - + // NOTE(bill): Don't even bother trying to optimize this with the return ptr value + // This will violate the defer rules if you do: + // foo :: proc() -> (x, y: T) { + // defer x = ... // defer is executed after the `defer` + // return // the values returned should be zeroed + // } + lbAddr res = lb_add_local(p, e->type, e); if (e->Variable.param_value.kind != ParameterValue_Invalid) { lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos); lb_addr_store(p, res, c); @@ -893,7 +885,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, GB_ASSERT(param_count-1 <= args.count); param_count -= 1; } else { - GB_ASSERT_MSG(param_count == args.count, "%td == %td", param_count, args.count); + GB_ASSERT_MSG(param_count == args.count, "%td == %td (%s)", param_count, args.count, LLVMPrintValueToString(value.value)); } lbValue result = {}; diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index a080fc09d..bd622d411 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -1273,6 +1273,7 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) { lbValue parent = lb_build_expr(p, as->rhs[0]); bool is_parent_ptr = is_type_pointer(parent.type); + Type *parent_base_type = type_deref(parent.type); TypeSwitchKind switch_kind = check_valid_type_switch_type(parent.type); GB_ASSERT(switch_kind != TypeSwitch_Invalid); @@ -1288,8 +1289,11 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) { lbValue union_data = {}; if (switch_kind == TypeSwitch_Union) { union_data = lb_emit_conv(p, parent_ptr, t_rawptr); - if (is_type_union_maybe_pointer(type_deref(parent_ptr.type))) { + Type *union_type = type_deref(parent_ptr.type); + if (is_type_union_maybe_pointer(union_type)) { tag = lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_NotEq, union_data), t_int); + } else if (union_tag_size(union_type) == 0) { + tag = {}; // there is no tag for a zero sized union } else { lbValue tag_ptr = lb_emit_union_tag_ptr(p, parent_ptr); tag = lb_emit_load(p, tag_ptr); @@ -1318,8 +1322,15 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) { } } - GB_ASSERT(tag.value != nullptr); - LLVMValueRef switch_instr = LLVMBuildSwitch(p->builder, tag.value, else_block->block, cast(unsigned)num_cases); + + LLVMValueRef switch_instr = nullptr; + if (type_size_of(parent_base_type) == 0) { + GB_ASSERT(tag.value == nullptr); + switch_instr = LLVMBuildSwitch(p->builder, lb_const_bool(p->module, t_llvm_bool, false).value, else_block->block, cast(unsigned)num_cases); + } else { + GB_ASSERT(tag.value != nullptr); + switch_instr = LLVMBuildSwitch(p->builder, tag.value, else_block->block, cast(unsigned)num_cases); + } for_array(i, body->stmts) { Ast *clause = body->stmts[i]; diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index d424fa5b2..8a0b794bc 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -612,6 +612,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da LLVMValueRef value_init = llvm_const_array(lb_type(m, t_type_info_enum_value), value_values, cast(unsigned)fields.count); LLVMSetInitializer(name_array.value, name_init); LLVMSetInitializer(value_array.value, value_init); + LLVMSetGlobalConstant(name_array.value, true); + LLVMSetGlobalConstant(value_array.value, true); lbValue v_count = lb_const_int(m, t_int, fields.count); @@ -787,7 +789,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_map_ptr); init_map_internal_types(t); - lbValue gst = lb_type_info(m, t->Map.generated_struct_type); + lbValue gst = lb_type_info(m, t->Map.internal_type); LLVMValueRef vals[5] = { lb_type_info(m, t->Map.key).value, diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 14592f29a..a54171b51 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1130,7 +1130,7 @@ lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) { case Type_Map: { init_map_internal_types(t); - Type *gst = t->Map.generated_struct_type; + Type *gst = t->Map.internal_type; switch (index) { case 0: result_type = get_struct_field_type(gst, 0); break; case 1: result_type = get_struct_field_type(gst, 1); break; diff --git a/src/parser.cpp b/src/parser.cpp index 2109945ec..286f6d7a2 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -41,7 +41,6 @@ gbString get_file_line_as_string(TokenPos const &pos, i32 *offset_) { while (line_end < end) { if (*line_end == '\n') { - line_end -= 1; break; } line_end += 1; diff --git a/src/types.cpp b/src/types.cpp index e917d30fa..fec324bf4 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -227,7 +227,6 @@ struct TypeProc { Type *key; \ Type *value; \ Type *entry_type; \ - Type *generated_struct_type; \ Type *internal_type; \ Type *lookup_result_type; \ }) \ @@ -688,6 +687,7 @@ gb_global Type *t_source_code_location_ptr = nullptr; gb_global Type *t_map_hash = nullptr; gb_global Type *t_map_header = nullptr; +gb_global Type *t_map_header_table = nullptr; gb_global Type *t_equal_proc = nullptr; @@ -2107,6 +2107,9 @@ bool is_type_polymorphic(Type *t, bool or_specialized=false) { case Type_Pointer: return is_type_polymorphic(t->Pointer.elem, or_specialized); + case Type_MultiPointer: + return is_type_polymorphic(t->MultiPointer.elem, or_specialized); + case Type_SoaPointer: return is_type_polymorphic(t->SoaPointer.elem, or_specialized); @@ -2130,6 +2133,15 @@ bool is_type_polymorphic(Type *t, bool or_specialized=false) { case Type_Slice: return is_type_polymorphic(t->Slice.elem, or_specialized); + case Type_Matrix: + if (t->Matrix.generic_row_count != nullptr) { + return true; + } + if (t->Matrix.generic_column_count != nullptr) { + return true; + } + return is_type_polymorphic(t->Matrix.elem, or_specialized); + case Type_Tuple: for_array(i, t->Tuple.variables) { if (is_type_polymorphic(t->Tuple.variables[i]->type, or_specialized)) { @@ -2196,6 +2208,34 @@ bool is_type_polymorphic(Type *t, bool or_specialized=false) { } break; + case Type_BitSet: + if (is_type_polymorphic(t->BitSet.elem, or_specialized)) { + return true; + } + if (t->BitSet.underlying != nullptr && + is_type_polymorphic(t->BitSet.underlying, or_specialized)) { + return true; + } + break; + + case Type_RelativeSlice: + if (is_type_polymorphic(t->RelativeSlice.slice_type, or_specialized)) { + return true; + } + if (t->RelativeSlice.base_integer != nullptr && + is_type_polymorphic(t->RelativeSlice.base_integer, or_specialized)) { + return true; + } + break; + case Type_RelativePointer: + if (is_type_polymorphic(t->RelativePointer.pointer_type, or_specialized)) { + return true; + } + if (t->RelativePointer.base_integer != nullptr && + is_type_polymorphic(t->RelativePointer.base_integer, or_specialized)) { + return true; + } + break; } return false; diff --git a/tests/core/Makefile b/tests/core/Makefile index 5c2918e30..92f12cbe7 100644 --- a/tests/core/Makefile +++ b/tests/core/Makefile @@ -2,7 +2,7 @@ ODIN=../../odin PYTHON=$(shell which python3) all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test encoding_test \ - math_test linalg_glsl_math_test filepath_test reflect_test os_exit_test i18n_test + math_test linalg_glsl_math_test filepath_test reflect_test os_exit_test i18n_test c_libc_test download_test_assets: $(PYTHON) download_assets.py @@ -47,4 +47,7 @@ os_exit_test: $(ODIN) run os/test_core_os_exit.odin -file -out:test_core_os_exit && exit 1 || exit 0 i18n_test: - $(ODIN) run text/i18n -out:test_core_i18n \ No newline at end of file + $(ODIN) run text/i18n -out:test_core_i18n + +c_libc_test: + $(ODIN) run c/libc -out:test_core_libc \ No newline at end of file diff --git a/tests/core/c/libc/test_core_libc.odin b/tests/core/c/libc/test_core_libc.odin new file mode 100644 index 000000000..6ad37ac6d --- /dev/null +++ b/tests/core/c/libc/test_core_libc.odin @@ -0,0 +1,37 @@ +package test_core_libc + +import "core:fmt" +import "core:os" +import "core:strings" +import "core:testing" + +TEST_count := 0 +TEST_fail := 0 + +when ODIN_TEST { + expect :: testing.expect + log :: testing.log +} else { + expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { + TEST_count += 1 + if !condition { + TEST_fail += 1 + fmt.printf("[%v] %v\n", loc, message) + return + } + } + log :: proc(t: ^testing.T, v: any, loc := #caller_location) { + fmt.printf("[%v] ", loc) + fmt.printf("log: %v\n", v) + } +} + +main :: proc() { + t := testing.T{} + test_libc_complex(&t) + + fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + if TEST_fail > 0 { + os.exit(1) + } +} diff --git a/tests/core/c/libc/test_core_libc_complex_pow.odin b/tests/core/c/libc/test_core_libc_complex_pow.odin new file mode 100644 index 000000000..90928794c --- /dev/null +++ b/tests/core/c/libc/test_core_libc_complex_pow.odin @@ -0,0 +1,91 @@ +package test_core_libc + +import "core:testing" +import "core:fmt" +import "core:c/libc" + +reldiff :: proc(lhs, rhs: $T) -> f64 { + if lhs == rhs { + return 0. + } + amean := f64((abs(lhs)+abs(rhs)) / 2.) + adiff := f64(abs(lhs - rhs)) + out := adiff / amean + return out +} + +isclose :: proc(lhs, rhs: $T, rtol:f64 = 1e-12, atol:f64 = 1e-12) -> bool { + adiff := f64(abs(lhs - rhs)) + if adiff < atol { + return true + } + rdiff := reldiff(lhs, rhs) + if rdiff < rtol { + return true + } + fmt.printf("not close -- lhs:%v rhs:%v -- adiff:%e rdiff:%e\n",lhs, rhs, adiff, rdiff) + return false +} + +// declaring here so they can be used as function pointers + +libc_pow :: proc(x, y: libc.complex_double) -> libc.complex_double { + return libc.pow(x,y) +} + +libc_powf :: proc(x, y: libc.complex_float) -> libc.complex_float { + return libc.pow(x,y) +} + +@test +test_libc_complex :: proc(t: ^testing.T) { + test_libc_pow_binding(t, libc.complex_double, f64, libc_pow, 1e-12, 1e-12) + // f32 needs more atol for comparing values close to zero + test_libc_pow_binding(t, libc.complex_float, f32, libc_powf, 1e-12, 1e-5) +} + +@test +test_libc_pow_binding :: proc(t: ^testing.T, $LIBC_COMPLEX:typeid, $F:typeid, pow: proc(LIBC_COMPLEX, LIBC_COMPLEX) -> LIBC_COMPLEX, + rtol: f64, atol: f64) { + // Tests that c/libc/pow(f) functions have two arguments and that the function works as expected for simple inputs + { + // tests 2^n + expected_real : F = 1./16. + expected_imag : F = 0. + complex_base := LIBC_COMPLEX(complex(F(2.), F(0.))) + for n in -4..=4 { + complex_power := LIBC_COMPLEX(complex(F(n), F(0.))) + result := pow(complex_base, complex_power) + expect(t, isclose(expected_real, F(real(result)), rtol, atol), fmt.tprintf("ftype:%T, n:%v reldiff(%v, re(%v)) is greater than specified rtol:%e", F{}, n, expected_real, result, rtol)) + expect(t, isclose(expected_imag, F(imag(result)), rtol, atol), fmt.tprintf("ftype:%T, n:%v reldiff(%v, im(%v)) is greater than specified rtol:%e", F{}, n, expected_imag, result, rtol)) + expected_real *= 2 + } + } + { + // tests (2i)^n + value : F = 1/16. + expected_real, expected_imag : F + complex_base := LIBC_COMPLEX(complex(F(0.), F(2.))) + for n in -4..=4 { + complex_power := LIBC_COMPLEX(complex(F(n), F(0.))) + result := pow(complex_base, complex_power) + switch n%%4 { + case 0: + expected_real = value + expected_imag = 0. + case 1: + expected_real = 0. + expected_imag = value + case 2: + expected_real = -value + expected_imag = 0. + case 3: + expected_real = 0. + expected_imag = -value + } + expect(t, isclose(expected_real, F(real(result)), rtol, atol), fmt.tprintf("ftype:%T, n:%v reldiff(%v, re(%v)) is greater than specified rtol:%e", F{}, n, expected_real, result, rtol)) + expect(t, isclose(expected_imag, F(imag(result)), rtol, atol), fmt.tprintf("ftype:%T, n:%v reldiff(%v, im(%v)) is greater than specified rtol:%e", F{}, n, expected_imag, result, rtol)) + value *= 2 + } + } +} \ No newline at end of file diff --git a/vendor/OpenGL/constants.odin b/vendor/OpenGL/constants.odin index c7d8b9542..798cf2cde 100644 --- a/vendor/OpenGL/constants.odin +++ b/vendor/OpenGL/constants.odin @@ -1,4 +1,4 @@ -package odin_gl +package vendor_gl GL_DEBUG :: #config(GL_DEBUG, ODIN_DEBUG) diff --git a/vendor/OpenGL/enums.odin b/vendor/OpenGL/enums.odin index 3802b7e76..8583ce4fb 100644 --- a/vendor/OpenGL/enums.odin +++ b/vendor/OpenGL/enums.odin @@ -1,4 +1,4 @@ -package odin_gl +package vendor_gl GL_Enum :: enum u64 { FALSE = 0, diff --git a/vendor/OpenGL/helpers.odin b/vendor/OpenGL/helpers.odin index 61c68ace5..900710f3d 100644 --- a/vendor/OpenGL/helpers.odin +++ b/vendor/OpenGL/helpers.odin @@ -1,4 +1,4 @@ -package odin_gl +package vendor_gl // Helper for loading shaders into a program diff --git a/vendor/OpenGL/impl.odin b/vendor/OpenGL/impl.odin index e9adda4bd..783b509a0 100644 --- a/vendor/OpenGL/impl.odin +++ b/vendor/OpenGL/impl.odin @@ -1,4 +1,4 @@ -package odin_gl +package vendor_gl loaded_up_to: [2]int loaded_up_to_major := 0 diff --git a/vendor/OpenGL/wrappers.odin b/vendor/OpenGL/wrappers.odin index ba6ff369c..46511c7d7 100644 --- a/vendor/OpenGL/wrappers.odin +++ b/vendor/OpenGL/wrappers.odin @@ -1,4 +1,4 @@ -package odin_gl +package vendor_gl #assert(size_of(bool) == size_of(u8)) diff --git a/vendor/README.md b/vendor/README.md index 1e24b4d0a..1aedd799a 100644 --- a/vendor/README.md +++ b/vendor/README.md @@ -141,4 +141,11 @@ Includes full bindings as well as wrappers to match the `core:crypto` API. [CMark](https://github.com/commonmark/cmark) CommonMark parsing library. See also LICENSE in the `commonmark` directory itself. -Includes full bindings and Windows `.lib` and `.dll`. \ No newline at end of file +Includes full bindings and Windows `.lib` and `.dll`. + +## CommonMark + +[zlib](https://github.com/madler/zlib) data compression library + +See also LICENSE in the `zlib` directory itself. +Includes full bindings. \ No newline at end of file diff --git a/vendor/botan/bindings/botan.odin b/vendor/botan/bindings/botan.odin index a12706e95..2217eda5a 100644 --- a/vendor/botan/bindings/botan.odin +++ b/vendor/botan/bindings/botan.odin @@ -1,4 +1,4 @@ -package botan_bindings +package vendor_botan /* Copyright 2021 zhibog diff --git a/vendor/botan/blake2b/blake2b.odin b/vendor/botan/blake2b/blake2b.odin index 67238ec74..18fd89bd8 100644 --- a/vendor/botan/blake2b/blake2b.odin +++ b/vendor/botan/blake2b/blake2b.odin @@ -1,4 +1,4 @@ -package botan_blake2b +package vendor_botan_blake2b /* Copyright 2021 zhibog diff --git a/vendor/botan/gost/gost.odin b/vendor/botan/gost/gost.odin index 6bf1c5b97..bccc4d463 100644 --- a/vendor/botan/gost/gost.odin +++ b/vendor/botan/gost/gost.odin @@ -1,4 +1,4 @@ -package gost +package vendor_gost /* Copyright 2021 zhibog diff --git a/vendor/botan/keccak/keccak.odin b/vendor/botan/keccak/keccak.odin index 28e7374ba..4c82edc92 100644 --- a/vendor/botan/keccak/keccak.odin +++ b/vendor/botan/keccak/keccak.odin @@ -1,4 +1,4 @@ -package keccak +package vendor_keccak /* Copyright 2021 zhibog diff --git a/vendor/botan/md4/md4.odin b/vendor/botan/md4/md4.odin index 174676a82..ddb7d5940 100644 --- a/vendor/botan/md4/md4.odin +++ b/vendor/botan/md4/md4.odin @@ -1,4 +1,4 @@ -package md4 +package vendor_md4 /* Copyright 2021 zhibog diff --git a/vendor/botan/md5/md5.odin b/vendor/botan/md5/md5.odin index 01e099062..9ea489669 100644 --- a/vendor/botan/md5/md5.odin +++ b/vendor/botan/md5/md5.odin @@ -1,4 +1,4 @@ -package md5 +package vendor_md5 /* Copyright 2021 zhibog diff --git a/vendor/botan/ripemd/ripemd.odin b/vendor/botan/ripemd/ripemd.odin index 230e4c0d9..33f0ba692 100644 --- a/vendor/botan/ripemd/ripemd.odin +++ b/vendor/botan/ripemd/ripemd.odin @@ -1,4 +1,4 @@ -package ripemd +package vendor_ripemd /* Copyright 2021 zhibog diff --git a/vendor/botan/sha1/sha1.odin b/vendor/botan/sha1/sha1.odin index 1f74c8fc2..96520f09e 100644 --- a/vendor/botan/sha1/sha1.odin +++ b/vendor/botan/sha1/sha1.odin @@ -1,4 +1,4 @@ -package sha1 +package vendor_sha1 /* Copyright 2021 zhibog diff --git a/vendor/botan/sha2/sha2.odin b/vendor/botan/sha2/sha2.odin index 4c201cc26..d583298ee 100644 --- a/vendor/botan/sha2/sha2.odin +++ b/vendor/botan/sha2/sha2.odin @@ -1,4 +1,4 @@ -package sha2 +package vendor_sha2 /* Copyright 2021 zhibog diff --git a/vendor/botan/sha3/sha3.odin b/vendor/botan/sha3/sha3.odin index 4c1b87dda..5f82be49c 100644 --- a/vendor/botan/sha3/sha3.odin +++ b/vendor/botan/sha3/sha3.odin @@ -1,4 +1,4 @@ -package sha3 +package vendor_sha3 /* Copyright 2021 zhibog diff --git a/vendor/botan/shake/shake.odin b/vendor/botan/shake/shake.odin index f1023b90e..b973fee24 100644 --- a/vendor/botan/shake/shake.odin +++ b/vendor/botan/shake/shake.odin @@ -1,4 +1,4 @@ -package shake +package vendor_shake /* Copyright 2021 zhibog diff --git a/vendor/botan/siphash/siphash.odin b/vendor/botan/siphash/siphash.odin index c2b7b64f4..81ac71cd5 100644 --- a/vendor/botan/siphash/siphash.odin +++ b/vendor/botan/siphash/siphash.odin @@ -1,4 +1,4 @@ -package siphash +package vendor_siphash /* Copyright 2022 zhibog diff --git a/vendor/botan/skein512/skein512.odin b/vendor/botan/skein512/skein512.odin index 4fed07853..41ffaefff 100644 --- a/vendor/botan/skein512/skein512.odin +++ b/vendor/botan/skein512/skein512.odin @@ -1,4 +1,4 @@ -package skein512 +package vendor_skein512 /* Copyright 2021 zhibog diff --git a/vendor/botan/sm3/sm3.odin b/vendor/botan/sm3/sm3.odin index 75cf40679..52fe9a488 100644 --- a/vendor/botan/sm3/sm3.odin +++ b/vendor/botan/sm3/sm3.odin @@ -1,4 +1,4 @@ -package sm3 +package vendor_sm3 /* Copyright 2021 zhibog diff --git a/vendor/botan/streebog/streebog.odin b/vendor/botan/streebog/streebog.odin index 20b4e6adb..fdc07923f 100644 --- a/vendor/botan/streebog/streebog.odin +++ b/vendor/botan/streebog/streebog.odin @@ -1,4 +1,4 @@ -package streebog +package vendor_streebog /* Copyright 2021 zhibog diff --git a/vendor/botan/tiger/tiger.odin b/vendor/botan/tiger/tiger.odin index f24dc7019..3d7e064d0 100644 --- a/vendor/botan/tiger/tiger.odin +++ b/vendor/botan/tiger/tiger.odin @@ -1,4 +1,4 @@ -package tiger +package vendor_tiger /* Copyright 2021 zhibog diff --git a/vendor/botan/whirlpool/whirlpool.odin b/vendor/botan/whirlpool/whirlpool.odin index a7c1abbe8..c32ff20c0 100644 --- a/vendor/botan/whirlpool/whirlpool.odin +++ b/vendor/botan/whirlpool/whirlpool.odin @@ -1,4 +1,4 @@ -package whirlpool +package vendor_whirlpool /* Copyright 2021 zhibog diff --git a/vendor/commonmark/cmark.odin b/vendor/commonmark/cmark.odin index 313435870..fba12436d 100644 --- a/vendor/commonmark/cmark.odin +++ b/vendor/commonmark/cmark.odin @@ -4,7 +4,7 @@ Original authors: John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer. See LICENSE for license details. */ -package commonmark +package vendor_commonmark import "core:c" import "core:c/libc" diff --git a/vendor/commonmark/doc.odin b/vendor/commonmark/doc.odin index 24f6780f3..f37dc8153 100644 --- a/vendor/commonmark/doc.odin +++ b/vendor/commonmark/doc.odin @@ -5,7 +5,7 @@ Original authors: John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer. See LICENSE for license details. */ -package commonmark +package vendor_commonmark /* Parsing - Simple interface: diff --git a/vendor/ggpo/ggpo.odin b/vendor/ggpo/ggpo.odin index c727e4a67..d17c33638 100644 --- a/vendor/ggpo/ggpo.odin +++ b/vendor/ggpo/ggpo.odin @@ -1,4 +1,4 @@ -package ggpo +package vendor_ggpo foreign import lib "GGPO.lib" diff --git a/vendor/stb/image/stb_image.odin b/vendor/stb/image/stb_image.odin index eedce5f04..c1e31c7b2 100644 --- a/vendor/stb/image/stb_image.odin +++ b/vendor/stb/image/stb_image.odin @@ -6,7 +6,7 @@ import c "core:c/libc" when ODIN_OS == .Windows { foreign import stbi "../lib/stb_image.lib" } when ODIN_OS == .Linux { foreign import stbi "../lib/stb_image.a" } -when ODIN_OS == .Darwin { foreign import stbi "../lib/stb_image.a" } +when ODIN_OS == .Darwin { foreign import stbi "../lib/darwin/stb_image.a" } #assert(size_of(b32) == size_of(c.int)) diff --git a/vendor/stb/image/stb_image_resize.odin b/vendor/stb/image/stb_image_resize.odin index 5763e142a..509f79fb8 100644 --- a/vendor/stb/image/stb_image_resize.odin +++ b/vendor/stb/image/stb_image_resize.odin @@ -4,7 +4,7 @@ import c "core:c/libc" when ODIN_OS == .Windows { foreign import lib "../lib/stb_image_resize.lib" } when ODIN_OS == .Linux { foreign import lib "../lib/stb_image_resize.a" } -when ODIN_OS == .Darwin { foreign import lib "../lib/stb_image_resize.a" } +when ODIN_OS == .Darwin { foreign import lib "../lib/darwin/stb_image_resize.a" } ////////////////////////////////////////////////////////////////////////////// // diff --git a/vendor/stb/image/stb_image_write.odin b/vendor/stb/image/stb_image_write.odin index b9433e821..f830050a8 100644 --- a/vendor/stb/image/stb_image_write.odin +++ b/vendor/stb/image/stb_image_write.odin @@ -4,7 +4,7 @@ import c "core:c/libc" when ODIN_OS == .Windows { foreign import stbiw "../lib/stb_image_write.lib" } when ODIN_OS == .Linux { foreign import stbiw "../lib/stb_image_write.a" } -when ODIN_OS == .Darwin { foreign import stbiw "../lib/stb_image_write.a" } +when ODIN_OS == .Darwin { foreign import stbiw "../lib/darwin/stb_image_write.a" } write_func :: proc "c" (ctx: rawptr, data: rawptr, size: c.int) diff --git a/vendor/zlib/LICENSE b/vendor/zlib/LICENSE new file mode 100644 index 000000000..1769520f9 --- /dev/null +++ b/vendor/zlib/LICENSE @@ -0,0 +1,20 @@ + (C) 1995-2022 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu \ No newline at end of file diff --git a/vendor/zlib/libz.lib b/vendor/zlib/libz.lib new file mode 100644 index 000000000..1cc2943ce Binary files /dev/null and b/vendor/zlib/libz.lib differ diff --git a/vendor/zlib/zlib.odin b/vendor/zlib/zlib.odin new file mode 100644 index 000000000..8a046a401 --- /dev/null +++ b/vendor/zlib/zlib.odin @@ -0,0 +1,262 @@ +package vendor_zlib + +import "core:c" + +when ODIN_OS == .Windows { foreign import zlib "libz.lib" } +when ODIN_OS == .Linux { foreign import zlib "system:z" } + +VERSION :: "1.2.12" +VERNUM :: 0x12c0 +VER_MAJOR :: 1 +VER_MINOR :: 2 +VER_REVISION :: 12 +VER_SUBREVISION :: 0 + +voidp :: rawptr +voidpf :: rawptr +voidpc :: rawptr +Byte :: c.uchar +Bytef :: c.uchar +uInt :: c.uint +uIntf :: c.uint +uLong :: c.ulong +uLongf :: c.ulong +size_t :: c.size_t +off_t :: c.long +off64_t :: i64 +crc_t :: u32 + +alloc_func :: proc "c" (opaque: voidp, items: uInt, size: uInt) -> voidpf +free_func :: proc "c" (opaque: voidp, address: voidpf) + +in_func :: proc "c" (rawptr, [^][^]c.uchar) -> c.uint +out_func :: proc "c" (rawptr, [^]c.uchar, c.uint) -> c.int + +gzFile_s :: struct { + have: c.uint, + next: [^]c.uchar, + pos: off64_t, +} + +gzFile :: ^gzFile_s + +z_stream_s :: struct { + next_in: ^Bytef, + avail_in: uInt, + total_in: uLong, + next_out: ^Bytef, + avail_out: uInt, + total_out: uLong, + msg: [^]c.char, + state: rawptr, + zalloc: alloc_func, + zfree: free_func, + opaque: voidpf, + data_type: c.int, + adler: uLong, + reserved: uLong, +} + +z_stream :: z_stream_s +z_streamp :: ^z_stream + +gz_header_s :: struct { + text: c.int, + time: uLong, + xflags: c.int, + os: c.int, + extra: [^]Bytef, + extra_len: uInt, + extra_max: uInt, + name: [^]Bytef, + name_max: uInt, + comment: [^]Bytef, + comm_max: uInt, + hcrc: c.int, + done: c.int, +} + +gz_header :: gz_header_s +gz_headerp :: ^gz_header + +// Allowed flush values; see deflate() and inflate() below for details +NO_FLUSH :: 0 +PARTIAL_FLUSH :: 1 +SYNC_FLUSH :: 2 +FULL_FLUSH :: 3 +FINISH :: 4 +BLOCK :: 5 +TREES :: 6 + +// Return codes for the compression/decompression functions. Negative values are +// errors, positive values are used for special but normal events. +OK :: 0 +STREAM_END :: 1 +NEED_DICT :: 2 +ERRNO :: -1 +STREAM_ERROR :: -2 +DATA_ERROR :: -3 +MEM_ERROR :: -4 +BUF_ERROR :: -5 +VERSION_ERROR :: -6 + +// compression levels +NO_COMPRESSION :: 0 +BEST_SPEED :: 1 +BEST_COMPRESSION :: 9 +DEFAULT_COMPRESSION :: -1 + +// compression strategy; see deflateInit2() below for details +FILTERED :: 1 +HUFFMAN_ONLY :: 2 +RLE :: 3 +FIXED :: 4 +DEFAULT_STRATEGY :: 0 + +// Possible values of the data_type field for deflate() +BINARY :: 0 +TEXT :: 1 +ASCII :: TEXT // for compatibility with 1.2.2 and earlier +UNKNOWN :: 2 + +// The deflate compression method (the only one supported in this version) +DEFLATED :: 8 + +NULL :: 0 // for initializing zalloc, zfree, opaque + +version :: Version // for compatibility with versions < 1.0.2 + +@(default_calling_convention="c") +foreign zlib { + // becase zlib.zlibVersion would be silly to write + @(link_prefix="zlib") + Version :: proc() -> cstring --- + + deflate :: proc(strm: z_streamp, flush: c.int) -> c.int --- + deflateEnd :: proc(strm: z_streamp) -> c.int --- + inflate :: proc(strm: z_streamp, flush: c.int) -> c.int --- + inflateEnd :: proc(strm: z_streamp) -> c.int --- + deflateSetDictionary :: proc(strm: z_streamp, dictionary: [^]Bytef, dictLength: uInt) -> c.int --- + deflateGetDictionary :: proc(strm: z_streamp, dictionary: [^]Bytef, dictLength: ^uInt) -> c.int --- + deflateCopy :: proc(dest, source: z_streamp) -> c.int --- + deflateReset :: proc(strm: z_streamp) -> c.int --- + deflateParams :: proc(strm: z_streamp, level, strategy: c.int) -> c.int --- + deflateTune :: proc(strm: z_streamp, good_length, max_lazy, nice_length, max_chain: c.int) -> c.int --- + deflateBound :: proc(strm: z_streamp, sourceLen: uLong) -> uLong --- + deflatePending :: proc(strm: z_streamp, pending: [^]c.uint, bits: [^]c.int) -> c.int --- + deflatePrime :: proc(strm: z_streamp, bits, value: c.int) -> c.int --- + deflateSetHeader :: proc(strm: z_streamp, head: gz_headerp) -> c.int --- + inflateSetDictionary :: proc(strm: z_streamp, dictionary: [^]Bytef, dictLength: uInt) -> c.int --- + inflateGetDictionary :: proc(strm: z_streamp, dictionary: [^]Bytef, dictLength: ^uInt) -> c.int --- + inflateSync :: proc(strm: z_streamp) -> c.int --- + inflateCopy :: proc(dest, source: z_streamp) -> c.int --- + inflateReset :: proc(strm: z_streamp) -> c.int --- + inflateReset2 :: proc(strm: z_streamp, windowBits: c.int) -> c.int --- + inflatePrime :: proc(strm: z_streamp, bits, value: c.int) -> c.int --- + inflateMark :: proc(strm: z_streamp) -> c.long --- + inflateGetHeader :: proc(strm: z_streamp, head: gz_headerp) -> c.int --- + inflateBack :: proc(strm: z_streamp, _in: in_func, in_desc: rawptr, out: out_func, out_desc: rawptr) -> c.int --- + inflateBackEnd :: proc(strm: z_streamp) -> c.int --- + zlibCompileFlags :: proc() -> uLong --- + compress :: proc(dest: [^]Bytef, destLen: ^uLongf, source: [^]Bytef, sourceLen: uLong) -> c.int --- + compress2 :: proc(dest: [^]Bytef, destLen: ^uLongf, source: [^]Bytef, sourceLen: uLong, level: c.int) -> c.int --- + compressBound :: proc(sourceLen: uLong) -> uLong --- + uncompress :: proc(dest: [^]Bytef, destLen: ^uLongf, source: [^]Bytef, sourceLen: uLong) -> c.int --- + uncompress2 :: proc(dest: [^]Bytef, destLen: ^uLongf, source: [^]Bytef, sourceLen: ^uLong) -> c.int --- + gzdopen :: proc(fd: c.int, mode: cstring) -> gzFile --- + gzbuffer :: proc(file: gzFile, size: c.uint) -> c.int --- + gzsetparams :: proc(file: gzFile, level, strategy: c.int) -> c.int --- + gzread :: proc(file: gzFile, buf: voidp, len: c.uint) -> c.int --- + gzfread :: proc(buf: voidp, size, nitems: size_t, file: gzFile) -> size_t --- + gzwrite :: proc(file: gzFile, buf: voidpc, len: c.uint) -> c.int --- + gzfwrite :: proc(buf: voidpc, size, nitems: size_t, file: gzFile) -> size_t --- + gzprintf :: proc(file: gzFile, format: cstring, #c_vararg args: ..any) -> c.int --- + gzputs :: proc(file: gzFile, s: cstring) -> c.int --- + gzgets :: proc(file: gzFile, buf: [^]c.char, len: c.int) -> [^]c.char --- + gzputc :: proc(file: gzFile, ch: c.int) -> c.int --- + gzgetc_ :: proc(file: gzFile) -> c.int --- // backwards compat, not the same as gzget + gzungetc :: proc(ch: c.int, file: gzFile) -> c.int --- + gzflush :: proc(file: gzFile, flush: c.int) -> c.int --- + gzrewind :: proc(file: gzFile) -> c.int --- + gzeof :: proc(file: gzFile) -> c.int --- + gzdirect :: proc(file: gzFile) -> c.int --- + gzclose :: proc(file: gzFile) -> c.int --- + gzclose_r :: proc(file: gzFile) -> c.int --- + gzclose_w :: proc(file: gzFile) -> c.int --- + gzerror :: proc(file: gzFile, errnum: ^c.int) -> cstring --- + gzclearerr :: proc(file: gzFile) --- + adler32 :: proc(adler: uLong, buf: [^]Bytef, len: uInt) -> uLong --- + adler32_z :: proc(adler: uLong, buf: [^]Bytef, len: size_t) -> uLong --- + crc32 :: proc(crc: uLong, buf: [^]Bytef, len: uInt) -> uLong --- + crc32_z :: proc(crc: uLong, buf: [^]Bytef, len: size_t) -> uLong --- + crc32_combine_op :: proc(crc1, crc2, op: uLong) -> uLong --- + gzopen64 :: proc(cstring, cstring) -> gzFile --- + gzseek64 :: proc(gzFile, off64_t, c.int) -> off64_t --- + gztell64 :: proc(gzFile) -> off64_t --- + gzoffset64 :: proc(gzFile) -> off64_t --- + adler32_combine64 :: proc(uLong, uLong, off64_t) -> uLong --- + crc32_combine64 :: proc(uLong, uLong, off64_t) -> uLong --- + crc32_combine_gen64 :: proc(off64_t) -> uLong --- + adler32_combine :: proc(uLong, uLong, off_t) -> uLong --- + crc32_combine :: proc(uLong, uLong, off_t) -> uLong --- + crc32_combine_gen :: proc(off_t) -> uLong --- + zError :: proc(c.int) -> cstring --- + inflateSyncPoint :: proc(z_streamp) -> c.int --- + get_crc_table :: proc() -> [^]crc_t --- + inflateUndermine :: proc(z_streamp, c.int) -> c.int --- + inflateValidate :: proc(z_streamp, c.int) -> c.int --- + inflateCodesUsed :: proc(z_streamp) -> c.ulong --- + inflateResetKeep :: proc(z_streamp) -> c.int --- + deflateResetKeep :: proc(z_streamp) -> c.int --- +} + +// Make these private since we create wrappers below passing in version and size +// of the stream structure like zlib.h does +@(private) +@(default_calling_convention="c") +foreign zlib { + deflateInit_ :: proc(strm: z_streamp, level: c.int, version: cstring, stream_size: c.int) -> c.int --- + inflateInit_ :: proc(strm: z_streamp, level: c.int, version: cstring, stream_size: c.int) -> c.int --- + deflateInit2_ :: proc(strm: z_streamp, level, method, windowBits, memLevel, strategy: c.int, version: cstring, stream_size: c.int) -> c.int --- + inflateInit2_ :: proc(strm: z_streamp, windowBits: c.int, version: cstring, stream_size: c.int) -> c.int --- + inflateBackInit_ :: proc(strm: z_streamp, windowBits: c.int, window: [^]c.uchar, version: cstring, stream_size: c.int) -> c.int --- + + // see below for explanation + @(link_name="gzgetc") + gzgetc_unique :: proc(file: gzFile) -> c.int --- +} + +deflateInit :: #force_inline proc "c" (strm: z_streamp, level: c.int) -> c.int { + return deflateInit_(strm, level, VERSION, c.int(size_of(z_stream))) +} + +inflateInit :: #force_inline proc "c" (strm: z_streamp, level: c.int) -> c.int { + return inflateInit_(strm, level, VERSION, c.int(size_of(z_stream))) +} + +deflateInit2 :: #force_inline proc "c" (strm: z_streamp, level, method, windowBits, memLevel, strategy: c.int) -> c.int { + return deflateInit2_(strm, level, method, windowBits, memLevel, strategy, VERSION, c.int(size_of(z_stream))) +} + +inflateInit2 :: #force_inline proc "c" (strm: z_streamp, windowBits: c.int) -> c.int { + return inflateInit2_(strm, windowBits, VERSION, c.int(size_of(z_stream))) +} + +inflateBackInit :: #force_inline proc "c" (strm: z_streamp, windowBits: c.int, window: [^]c.uchar) -> c.int { + return inflateBackInit_(strm, windowBits, window, VERSION, c.int(size_of(z_stream))) +} + +// zlib.h redefines gzgetc with a macro and uses (gzgetc)(g) to invoke it from +// inside the same macro (preventing macro expansion), in Odin we give that a +// unique name using link_prefix then implement the body of the macro in our own +// procedure calling the unique named gzgetc instead. +gzgetc :: #force_inline proc(file: gzFile) -> c.int { + if file.have != 0 { + file.have -= 1 + file.pos += 1 + ch := c.int(file.next[0]) + file.next = &file.next[1] + return ch + } + return gzgetc_unique(file) +}