From 0ff5ff6ff2f22ff61d56d1b647e71d3c73419426 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 12 Sep 2022 21:22:45 +0100 Subject: [PATCH 01/67] Use pow of two capacity for hash maps to allow for `& (n-1)` instead of `% n` --- core/runtime/dynamic_map_internal.odin | 33 +++++++++++++++++++++----- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 35b42d488..30db8d380 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -231,6 +231,9 @@ __dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller c.allocator = m.entries.allocator } context = c + + cap := cap + cap = next_pow2(cap) __dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc) @@ -267,7 +270,6 @@ __dynamic_map_get :: proc(h: Map_Header, hash: Map_Hash) -> rawptr { __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) @@ -300,17 +302,36 @@ __dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := # if __dynamic_map_full(h) { __dynamic_map_grow(h, loc) - // index = __dynamic_map_find(h, hash).entry_index - // assert(index >= 0) } return __dynamic_map_get_entry(h, index) } +@(private="file") +next_pow2 :: proc "contextless" (n: int) -> int { + n := n + if n <= 0 { + return 0 + } else if n <= 2 { + return 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_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) + new_count := max(m.entries.cap * 2, INITIAL_MAP_CAP) __dynamic_map_rehash(h, new_count, loc) } @@ -325,8 +346,8 @@ __dynamic_map_hash_equal :: proc "contextless" (h: Map_Header, a, b: Map_Hash) - __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) + if n := uintptr(len(m.hashes)); n != 0 { + fr.hash_index = int(hash.hash & (n-1)) fr.entry_index = m.hashes[fr.hash_index] for fr.entry_index >= 0 { entry := __dynamic_map_get_entry(h, fr.entry_index) From 8b82bcef7daec0a4390256ab2bfc444703b7b749 Mon Sep 17 00:00:00 2001 From: Dale Weiler Date: Wed, 14 Sep 2022 16:09:13 -0400 Subject: [PATCH 02/67] vendor zlib --- vendor/zlib/zlib.odin | 262 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 vendor/zlib/zlib.odin diff --git a/vendor/zlib/zlib.odin b/vendor/zlib/zlib.odin new file mode 100644 index 000000000..c052429f5 --- /dev/null +++ b/vendor/zlib/zlib.odin @@ -0,0 +1,262 @@ +package zlib + +import "core:c" + +when ODIN_OS == .Windows do foreign import zlib "libz.lib" +when ODIN_OS == .Linux do 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) +} \ No newline at end of file From 59f3e10f0aaf22e20cfd2301f25aca35c23a8a1b Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 14 Sep 2022 23:08:52 +0200 Subject: [PATCH 03/67] [zlib] Add LICENSE, update README --- vendor/README.md | 9 ++++++++- vendor/zlib/LICENSE | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 vendor/zlib/LICENSE 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/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 From 8aba92da9b621e8df7eb4c534cd36924c7532644 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 14 Sep 2022 23:27:28 +0200 Subject: [PATCH 04/67] [zlib] Add statically linked x64 library. --- vendor/zlib/libz.lib | Bin 0 -> 687622 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 vendor/zlib/libz.lib diff --git a/vendor/zlib/libz.lib b/vendor/zlib/libz.lib new file mode 100644 index 0000000000000000000000000000000000000000..1cc2943ce0860aaf8ddb35f951049dd263fcc7e3 GIT binary patch literal 687622 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 z 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�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 lJOt{mi=m!_n{~zi&-g5u| literal 0 HcmV?d00001 From 4767311a222bbbe68461ad103702c19c046ed76e Mon Sep 17 00:00:00 2001 From: Zac Nowicki Date: Thu, 15 Sep 2022 07:07:55 -0400 Subject: [PATCH 05/67] Fix -verbose-error source lines from having last char cut off Fixes #1226 --- src/parser.cpp | 1 - 1 file changed, 1 deletion(-) 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; From 98420192051a5805c20328c7d2002883c1f47fde Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 15 Sep 2022 18:01:15 +0200 Subject: [PATCH 06/67] [examples] Add math/noise, align imports. --- examples/all/all_main.odin | 138 +++++++++++++++++++------------------ 1 file changed, 70 insertions(+), 68 deletions(-) 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 From 98eaf5c6c014be7cf159ec11175c15dc170253ce Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 10:20:04 +0100 Subject: [PATCH 07/67] =?UTF-8?q?Fix=20#2054=20Differing=20behaviours=20wi?= =?UTF-8?q?th=20defer=20statements=20for=20single=20vs=20multiple=20return?= =?UTF-8?q?=20values=20caused=20by=20na=C3=AFve=20ABI=20optimization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/llvm_backend_proc.cpp | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 82e577032..8bbbb0c56 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -577,20 +577,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); From 320b84df4f7f2af868775951f89d19d353ef654e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 10:20:52 +0100 Subject: [PATCH 08/67] Fix #2052 typo in linalg.max_single --- core/math/linalg/extended.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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.. Date: Sat, 17 Sep 2022 10:26:57 +0100 Subject: [PATCH 09/67] Enforce constant pointer cast on global procedure variable initialization `x := proc() {}` --- src/llvm_backend.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 4eb343fa7..aa901d22f 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -780,6 +780,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; From 1bc0e66ed15bba0cdc1b27fec8a1f06a4c9144be Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 10:36:49 +0100 Subject: [PATCH 10/67] Improve error message for using `offset_of` within a struct itself of that struct --- src/check_builtin.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index f0db6afc4..48cf73b58 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1899,6 +1899,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) { From 9640b493191e2ec0ff1338751a053a5ef559728d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 10:42:54 +0100 Subject: [PATCH 11/67] Fix #1435 type switch statements of empty union types --- src/llvm_backend_stmt.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) 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]; From 99a1a102864689bd8904f10e1a7fe0f79363399f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 11:01:56 +0100 Subject: [PATCH 12/67] Fixed #2044 Uninitialised constant struct member values can cause crash Foo :: struct { x: i32, data: sa.Small_Array(10, i32), } defaultFoo :: Foo{ x = 1, // The 'data' value is not set! } fmt.println(defaultFoo.data) // caused the bug --- src/check_expr.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 38d17c131..891a9ebcb 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4022,6 +4022,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 +4034,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 +4683,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); From a903e5024c6e430690b48b8e3d614706e619942d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 11:18:26 +0100 Subject: [PATCH 13/67] Chnage `next_pow2` to `ceil_to_pow2` --- core/runtime/dynamic_map_internal.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 30db8d380..d2aaa49f4 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -233,7 +233,7 @@ __dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller context = c cap := cap - cap = next_pow2(cap) + cap = ceil_to_pow2(cap) __dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc) @@ -309,7 +309,7 @@ __dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := # @(private="file") -next_pow2 :: proc "contextless" (n: int) -> int { +ceil_to_pow2 :: proc "contextless" (n: int) -> int { n := n if n <= 0 { return 0 From b967ae2739735128db983e965bfd13a8d3233d9f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 12:21:23 +0100 Subject: [PATCH 14/67] Change internal map indices to use a distinct `uint` rather than just `int` --- core/runtime/core.odin | 2 +- core/runtime/core_builtin.odin | 2 +- core/runtime/dynamic_map_internal.odin | 50 +++++++++++++------------- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 0310aff6d..81cce8caf 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -394,7 +394,7 @@ Raw_Dynamic_Array :: struct { } Raw_Map :: struct { - hashes: []int, + hashes: []Map_Index, entries: Raw_Dynamic_Array, } diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index 4c736b6f6..92b3e9677 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -289,7 +289,7 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) { entries := (^Raw_Dynamic_Array)(&raw_map.entries) entries.len = 0 for _, i in raw_map.hashes { - raw_map.hashes[i] = -1 + raw_map.hashes[i] = MAP_SENTINEL } } diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index d2aaa49f4..ebefe7813 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -24,17 +24,18 @@ __get_map_hash_from_entry :: proc "contextless" (h: Map_Header, entry: ^Map_Entr return } - +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, @@ -207,16 +208,16 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l __dynamic_map_reset_entries :: proc(using header: Map_Header, loc := #caller_location) { for i in 0.. rawptr { } __dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := #caller_location) -> ^Map_Entry_Header #no_bounds_check { - index: int + index: Map_Index if len(h.m.hashes) == 0 { __dynamic_map_reserve(h, INITIAL_MAP_CAP, loc) @@ -345,11 +346,11 @@ __dynamic_map_hash_equal :: proc "contextless" (h: Map_Header, a, b: Map_Hash) - } __dynamic_map_find :: proc(using h: Map_Header, hash: Map_Hash) -> Map_Find_Result #no_bounds_check { - fr := Map_Find_Result{-1, -1, -1} + fr := Map_Find_Result{MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL} if n := uintptr(len(m.hashes)); n != 0 { - fr.hash_index = int(hash.hash & (n-1)) + fr.hash_index = Map_Index(hash.hash & (n-1)) fr.entry_index = m.hashes[fr.hash_index] - for fr.entry_index >= 0 { + 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) { @@ -364,28 +365,28 @@ __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) +__dynamic_map_add_entry :: proc(using h: Map_Header, hash: Map_Hash, loc := #caller_location) -> Map_Index { + prev := Map_Index(m.entries.len) + c := Map_Index(__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 + end.next = MAP_SENTINEL } return prev } __dynamic_map_delete_key :: proc(using h: Map_Header, hash: Map_Hash) { fr := __dynamic_map_find(h, hash) - if fr.entry_index >= 0 { + if fr.entry_index != MAP_SENTINEL { __dynamic_map_erase(h, fr) } } -__dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_Header { +__dynamic_map_get_entry :: proc(using h: Map_Header, index: Map_Index) -> ^Map_Entry_Header { // assert(0 <= index && index < m.entries.len) - return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size)) + return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*Map_Index(entry_size))) } __dynamic_map_copy_entry :: proc(h: Map_Header, new, old: ^Map_Entry_Header) { @@ -393,23 +394,24 @@ __dynamic_map_copy_entry :: proc(h: Map_Header, new, old: ^Map_Entry_Header) { } __dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check { - if fr.entry_prev < 0 { + if fr.entry_prev == MAP_SENTINEL { m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next } else { 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 { + last_index := Map_Index(m.entries.len-1) + if fr.entry_index == last_index { // NOTE(bill): No need to do anything else, just pop } else { old := __dynamic_map_get_entry(h, fr.entry_index) - end := __dynamic_map_get_entry(h, m.entries.len-1) + end := __dynamic_map_get_entry(h, last_index) __dynamic_map_copy_entry(h, old, end) old_hash := __get_map_hash_from_entry(h, old) - if last := __dynamic_map_find(h, old_hash); last.entry_prev >= 0 { + if last := __dynamic_map_find(h, old_hash); last.entry_prev != MAP_SENTINEL { last_entry := __dynamic_map_get_entry(h, last.entry_prev) last_entry.next = fr.entry_index } else { From 0428d5ae2e8fc3e814a968698a6550e86aea9d5c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 12:27:34 +0100 Subject: [PATCH 15/67] Catch missing areas of `Map_Index` usage --- core/runtime/dynamic_map_internal.odin | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index ebefe7813..4f1311dff 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -262,7 +262,7 @@ __dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #c __dynamic_map_get :: proc(h: Map_Header, hash: Map_Hash) -> rawptr { index := __dynamic_map_find(h, hash).entry_index - if index >= 0 { + if index != MAP_SENTINEL { data := uintptr(__dynamic_map_get_entry(h, index)) return rawptr(data + h.value_offset) } @@ -270,7 +270,7 @@ __dynamic_map_get :: proc(h: Map_Header, hash: Map_Hash) -> rawptr { } __dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := #caller_location) -> ^Map_Entry_Header #no_bounds_check { - index: Map_Index + index := MAP_SENTINEL if len(h.m.hashes) == 0 { __dynamic_map_reserve(h, INITIAL_MAP_CAP, loc) @@ -278,14 +278,14 @@ __dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := # } fr := __dynamic_map_find(h, hash) - if fr.entry_index >= 0 { + if fr.entry_index != MAP_SENTINEL { index = fr.entry_index } else { index = __dynamic_map_add_entry(h, hash, loc) - if fr.entry_prev >= 0 { + if fr.entry_prev != MAP_SENTINEL { entry := __dynamic_map_get_entry(h, fr.entry_prev) entry.next = index - } else if fr.hash_index >= 0 { + } else if fr.hash_index != MAP_SENTINEL { h.m.hashes[fr.hash_index] = index } else { return nil From 7840c1b89f3e6a7b3d962e6f5e96aab77a3ddc3b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 12:48:12 +0100 Subject: [PATCH 16/67] Change `__dynamic_map_get` and `__dynamic_map_set` to use separate parameters rather than take a singular struct --- core/runtime/dynamic_map_internal.odin | 21 ++++++++----- src/llvm_backend.cpp | 25 +++++++--------- src/llvm_backend.hpp | 2 +- src/llvm_backend_expr.cpp | 12 ++------ src/llvm_backend_general.cpp | 41 +++++++++++++------------- 5 files changed, 48 insertions(+), 53 deletions(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 4f1311dff..3c4b60938 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -18,10 +18,9 @@ __get_map_hash :: proc "contextless" (k: ^$K) -> (map_hash: Map_Hash) { return } -__get_map_hash_from_entry :: proc "contextless" (h: Map_Header, entry: ^Map_Entry_Header) -> (hash: Map_Hash) { +__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 } Map_Index :: distinct uint @@ -213,7 +212,8 @@ __dynamic_map_reset_entries :: proc(using header: Map_Header, loc := #caller_loc for i in 0.. rawptr { - index := __dynamic_map_find(h, hash).entry_index +// USED INTERNALLY BY THE COMPILER +__dynamic_map_get :: proc(h: Map_Header, key_hash: uintptr, key_ptr: rawptr) -> rawptr { + 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) @@ -269,7 +270,9 @@ __dynamic_map_get :: proc(h: Map_Header, hash: Map_Hash) -> rawptr { return nil } -__dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := #caller_location) -> ^Map_Entry_Header #no_bounds_check { +// USED INTERNALLY BY THE COMPILER +__dynamic_map_set :: proc(h: Map_Header, key_hash: uintptr, key_ptr: rawptr, value: rawptr, loc := #caller_location) -> ^Map_Entry_Header #no_bounds_check { + hash := Map_Hash{key_hash, key_ptr} index := MAP_SENTINEL if len(h.m.hashes) == 0 { @@ -352,7 +355,8 @@ __dynamic_map_find :: proc(using h: Map_Header, hash: Map_Hash) -> Map_Find_Resu fr.entry_index = 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) + entry_hash: Map_Hash + __get_map_hash_from_entry(h, entry, &entry_hash) if __dynamic_map_hash_equal(h, entry_hash, hash) { return fr } @@ -409,7 +413,8 @@ __dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds end := __dynamic_map_get_entry(h, last_index) __dynamic_map_copy_entry(h, old, end) - old_hash := __get_map_hash_from_entry(h, old) + old_hash: Map_Hash + __get_map_hash_from_entry(h, old, &old_hash) if last := __dynamic_map_find(h, old_hash); last.entry_prev != MAP_SENTINEL { last_entry := __dynamic_map_get_entry(h, last.entry_prev) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index aa901d22f..126bcef11 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -595,14 +595,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,10 +611,7 @@ 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, @@ -625,17 +620,19 @@ void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *map_ 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); + auto args = array_make(permanent_allocator(), 5); 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); + args[1] = key_hash; + args[2] = key_ptr; + args[3] = lb_emit_conv(p, value_addr.addr, t_rawptr); + args[4] = lb_emit_source_code_location(p, node); lb_emit_runtime_call(p, "__dynamic_map_set", args); } diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 79f0f37e7..e5bb9455f 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -444,7 +444,7 @@ 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); +lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue *key_ptr_); 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_find_procedure_value_from_entity(lbModule *m, Entity *e); diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 7c92c517c..3ab73a27b 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 { diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 1f8fccdcb..55b09cbfc 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -383,6 +383,21 @@ Type *lb_addr_type(lbAddr const &addr) { return type_deref(addr.addr.type); } +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 h = lb_gen_map_header(p, map_ptr, map_type); + + lbValue key_ptr = {}; + auto args = array_make(permanent_allocator(), 3); + args[0] = h; + args[1] = lb_gen_map_key_hash(p, key, map_type->Map.key, &key_ptr); + args[2] = 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)); +} + lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) { if (addr.addr.value == nullptr) { GB_PANIC("Illegal addr -> nullptr"); @@ -390,19 +405,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)); @@ -1059,16 +1063,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); From 0ebc2add030d224d59c0b8961dfa75a1f9d13b91 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 12:56:03 +0100 Subject: [PATCH 17/67] Use a cache when generating the map header to minimize stack wastage --- src/llvm_backend.cpp | 84 ++++++++++++++++++++++----------------- src/llvm_backend.hpp | 1 + src/llvm_backend_proc.cpp | 5 ++- 3 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 126bcef11..142ecc348 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -502,48 +502,58 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A 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 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 h = {}; + lbAddr *found = map_get(&p->map_header_cache, map_val_ptr.value); + if (found != nullptr) { + h = *found; + } else { + h = lb_add_local_generated(p, t_map_header, false); // all the values will be initialzed later - 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); + Type *key_type = map_type->Map.key; + Type *val_type = map_type->Map.value; + gb_unused(val_type); - 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); + 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); + + + map_set(&p->map_header_cache, map_val_ptr.value, h); + } return lb_addr_load(p, h); } diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index e5bb9455f..d622f3661 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -308,6 +308,7 @@ struct lbProcedure { PtrMap selector_values; PtrMap selector_addr; + PtrMap map_header_cache; }; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 8bbbb0c56..9e8158a88 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -121,8 +121,9 @@ 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); + map_init(&p->map_header_cache, a, 0); if (p->is_foreign) { lb_add_foreign_library_path(p->module, entity->Procedure.foreign_library); From 8ee6bb5d4b102158c6f4771a3104610bb8e6fd7f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 13:00:19 +0100 Subject: [PATCH 18/67] Add `contextless` where possible in dynamic_map_internal.odin --- core/runtime/dynamic_map_internal.odin | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 3c4b60938..d330d4808 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -205,7 +205,7 @@ __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) { +__dynamic_map_reset_entries :: proc "contextless" (using header: Map_Header, loc := #caller_location) { for i in 0.. rawptr { +__dynamic_map_get :: proc "contextless" (h: Map_Header, key_hash: uintptr, key_ptr: rawptr) -> rawptr { index := __dynamic_map_find(h, {key_hash, key_ptr}).entry_index if index != MAP_SENTINEL { data := uintptr(__dynamic_map_get_entry(h, index)) @@ -348,7 +348,7 @@ __dynamic_map_hash_equal :: proc "contextless" (h: Map_Header, a, b: Map_Hash) - return a.hash == b.hash && h.equal(a.key_ptr, b.key_ptr) } -__dynamic_map_find :: proc(using h: Map_Header, hash: Map_Hash) -> Map_Find_Result #no_bounds_check { +__dynamic_map_find :: proc "contextless" (using h: Map_Header, hash: Map_Hash) -> Map_Find_Result #no_bounds_check { fr := Map_Find_Result{MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL} if n := uintptr(len(m.hashes)); n != 0 { fr.hash_index = Map_Index(hash.hash & (n-1)) @@ -381,23 +381,22 @@ __dynamic_map_add_entry :: proc(using h: Map_Header, hash: Map_Hash, loc := #cal return prev } -__dynamic_map_delete_key :: proc(using h: Map_Header, hash: Map_Hash) { +__dynamic_map_delete_key :: proc "contextless" (using h: Map_Header, hash: Map_Hash) { fr := __dynamic_map_find(h, hash) if fr.entry_index != MAP_SENTINEL { __dynamic_map_erase(h, fr) } } -__dynamic_map_get_entry :: proc(using h: Map_Header, index: Map_Index) -> ^Map_Entry_Header { - // assert(0 <= index && index < m.entries.len) +__dynamic_map_get_entry :: proc "contextless" (using h: Map_Header, index: Map_Index) -> ^Map_Entry_Header { return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*Map_Index(entry_size))) } -__dynamic_map_copy_entry :: proc(h: Map_Header, new, old: ^Map_Entry_Header) { +__dynamic_map_copy_entry :: proc "contextless" (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 { +__dynamic_map_erase :: proc "contextless" (using h: Map_Header, fr: Map_Find_Result) #no_bounds_check { if fr.entry_prev == MAP_SENTINEL { m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next } else { From bfe0ffd6e6e7940d189b12df1697591f98564d60 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 13:02:06 +0100 Subject: [PATCH 19/67] Minor clean up --- core/runtime/dynamic_map_internal.odin | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index d330d4808..7326d761d 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -334,7 +334,6 @@ ceil_to_pow2 :: proc "contextless" (n: int) -> int { } __dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) { - // TODO(bill): Determine an efficient growing rate new_count := max(m.entries.cap * 2, INITIAL_MAP_CAP) __dynamic_map_rehash(h, new_count, loc) } @@ -344,7 +343,7 @@ __dynamic_map_full :: #force_inline proc "contextless" (using h: Map_Header) -> } -__dynamic_map_hash_equal :: proc "contextless" (h: Map_Header, a, b: Map_Hash) -> bool { +__dynamic_map_hash_equal :: #force_inline proc "contextless" (h: Map_Header, a, b: Map_Hash) -> bool { return a.hash == b.hash && h.equal(a.key_ptr, b.key_ptr) } @@ -388,7 +387,7 @@ __dynamic_map_delete_key :: proc "contextless" (using h: Map_Header, hash: Map_H } } -__dynamic_map_get_entry :: proc "contextless" (using h: Map_Header, index: Map_Index) -> ^Map_Entry_Header { +__dynamic_map_get_entry :: #force_inline proc "contextless" (using h: Map_Header, index: Map_Index) -> ^Map_Entry_Header { return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*Map_Index(entry_size))) } From 40bcfc7c8df2e4e76a75f543c43f97cf9d050925 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 13:05:14 +0100 Subject: [PATCH 20/67] Update json/unmarshal.odin for the new `runtime.__dynamic_map_set` --- core/encoding/json/unmarshal.odin | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index 792b1edc6..85d3303c2 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -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(header, key_hash, key_ptr, map_backing_value.data) if set_ptr == nil { delete(key, p.allocator) } From fbf036a654f54b4104f6252cac8fce6c9375daaf Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 13:11:29 +0100 Subject: [PATCH 21/67] Wrap `__dynamic_map_find` for certain cases --- core/runtime/core_builtin.odin | 8 +++----- core/runtime/dynamic_map_internal.odin | 13 +++++++++---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index 92b3e9677..558a04bf9 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -325,8 +325,7 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (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) + fr := __map_find(h, &key) if fr.entry_index >= 0 { entry := __dynamic_map_get_entry(h, fr.entry_index) deleted_key = (^K)(uintptr(entry)+h.key_offset)^ @@ -674,9 +673,8 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call 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)) + + data := uintptr(__dynamic_map_set(h, __get_map_key_hash(&key), &key, &value, loc)) return (^V)(data + h.value_offset) } diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 7326d761d..be7f73339 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -11,11 +11,9 @@ 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 :: 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) { @@ -347,6 +345,13 @@ __dynamic_map_hash_equal :: #force_inline proc "contextless" (h: Map_Header, a, return a.hash == b.hash && h.equal(a.key_ptr, b.key_ptr) } + +__map_find :: proc "contextless" (h: Map_Header, key_ptr: ^$K) -> Map_Find_Result #no_bounds_check { + hash := __get_map_key_hash(key_ptr) + return __dynamic_map_find(h, {hash, key_ptr}) +} + + __dynamic_map_find :: proc "contextless" (using h: Map_Header, hash: Map_Hash) -> Map_Find_Result #no_bounds_check { fr := Map_Find_Result{MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL} if n := uintptr(len(m.hashes)); n != 0 { From 81f10f53ad08e641e2fa800757c802d8eee24f30 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 13:22:23 +0100 Subject: [PATCH 22/67] Correct `delete_key` --- core/runtime/core_builtin.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index 558a04bf9..f65fd37d2 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -326,7 +326,7 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: key := key h := __get_map_header(m) fr := __map_find(h, &key) - if fr.entry_index >= 0 { + 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)^ From 4d512c2cf647bdd46f30746cd3544632bfaa8273 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 13:40:29 +0100 Subject: [PATCH 23/67] Correct `lb_gen_map_header` initialization --- src/llvm_backend_proc.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 9e8158a88..17ed9c2a6 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -381,6 +381,8 @@ lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type lb_add_proc_attribute_at_index(p, offset+parameter_index, "nocapture"); } + map_init(&p->map_header_cache, heap_allocator(), 0); + return p; } From c37de9459e4bf782e86e4c0687bba9aef92c58d8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 14:46:52 +0100 Subject: [PATCH 24/67] Minor refactor of the dynamic_map_internal.odin stuff --- core/runtime/dynamic_array_internal.odin | 2 + core/runtime/dynamic_map_internal.odin | 124 +++++++++++------------ 2 files changed, 60 insertions(+), 66 deletions(-) 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 be7f73339..dd18f6272 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -181,7 +181,7 @@ __get_map_header_runtime :: proc "contextless" (m: ^Raw_Map, ti: Type_Info_Map) } -__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 { @@ -203,59 +203,55 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l return false } -__dynamic_map_reset_entries :: proc "contextless" (using header: Map_Header, loc := #caller_location) { - for i in 0.. (did_shrink: bool) { +__dynamic_map_shrink :: proc "odin" (h: Map_Header, cap: int, loc := #caller_location) -> (did_shrink: bool) { c := context - if m.entries.allocator.procedure != nil { - c.allocator = m.entries.allocator + if h.m.entries.allocator.procedure != nil { + c.allocator = h.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) + return __dynamic_array_shrink(&h.m.entries, h.entry_size, h.entry_align, cap, loc) } // USED INTERNALLY BY THE COMPILER @@ -269,8 +265,19 @@ __dynamic_map_get :: proc "contextless" (h: Map_Header, key_hash: uintptr, key_p } // USED INTERNALLY BY THE COMPILER -__dynamic_map_set :: proc(h: Map_Header, key_hash: uintptr, key_ptr: rawptr, value: rawptr, loc := #caller_location) -> ^Map_Entry_Header #no_bounds_check { - hash := Map_Hash{key_hash, key_ptr} +__dynamic_map_set :: proc "odin" (h: Map_Header, 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 + } + index := MAP_SENTINEL if len(h.m.hashes) == 0 { @@ -278,11 +285,11 @@ __dynamic_map_set :: proc(h: Map_Header, key_hash: uintptr, key_ptr: rawptr, val __dynamic_map_grow(h, loc) } - fr := __dynamic_map_find(h, hash) + fr := __dynamic_map_find(h, {key_hash, key_ptr}) if fr.entry_index != MAP_SENTINEL { index = fr.entry_index } else { - index = __dynamic_map_add_entry(h, hash, loc) + 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 @@ -294,10 +301,10 @@ __dynamic_map_set :: proc(h: Map_Header, key_hash: uintptr, key_ptr: rawptr, val } e := __dynamic_map_get_entry(h, index) - e.hash = hash.hash + e.hash = key_hash key := rawptr(uintptr(e) + h.key_offset) - mem_copy(key, hash.key_ptr, h.key_size) + mem_copy(key, key_ptr, h.key_size) val := rawptr(uintptr(e) + h.value_offset) mem_copy(val, value, h.value_size) @@ -331,18 +338,14 @@ ceil_to_pow2 :: proc "contextless" (n: int) -> int { return n } -__dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) { - new_count := max(m.entries.cap * 2, INITIAL_MAP_CAP) - __dynamic_map_rehash(h, new_count, loc) +__dynamic_map_grow :: proc "odin" (h: Map_Header, loc := #caller_location) { + new_count := max(h.m.entries.cap * 2, INITIAL_MAP_CAP) + // Rehash through Reserve + __dynamic_map_reserve(h, new_count, loc) } -__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_hash_equal :: #force_inline 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 } @@ -352,16 +355,17 @@ __map_find :: proc "contextless" (h: Map_Header, key_ptr: ^$K) -> Map_Find_Resul } -__dynamic_map_find :: proc "contextless" (using h: Map_Header, hash: Map_Hash) -> Map_Find_Result #no_bounds_check { +__dynamic_map_find :: proc "contextless" (h: Map_Header, hash: Map_Hash) -> Map_Find_Result #no_bounds_check { fr := Map_Find_Result{MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL} - if n := uintptr(len(m.hashes)); n != 0 { + if n := uintptr(len(h.m.hashes)); n != 0 { fr.hash_index = Map_Index(hash.hash & (n-1)) - fr.entry_index = m.hashes[fr.hash_index] + 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: Map_Hash __get_map_hash_from_entry(h, entry, &entry_hash) - if __dynamic_map_hash_equal(h, entry_hash, hash) { + + if entry_hash.hash == hash.hash && h.equal(entry_hash.key_ptr, hash.key_ptr) { return fr } // assert(entry.next < m.entries.len) @@ -373,48 +377,36 @@ __dynamic_map_find :: proc "contextless" (using h: Map_Header, hash: Map_Hash) - return fr } -__dynamic_map_add_entry :: proc(using h: Map_Header, hash: Map_Hash, loc := #caller_location) -> Map_Index { - prev := Map_Index(m.entries.len) - c := Map_Index(__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 = MAP_SENTINEL - } - return prev -} - -__dynamic_map_delete_key :: proc "contextless" (using h: Map_Header, hash: Map_Hash) { +__dynamic_map_delete_key :: proc "contextless" (h: Map_Header, hash: Map_Hash) { fr := __dynamic_map_find(h, hash) if fr.entry_index != MAP_SENTINEL { __dynamic_map_erase(h, fr) } } -__dynamic_map_get_entry :: #force_inline proc "contextless" (using h: Map_Header, index: Map_Index) -> ^Map_Entry_Header { - return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*Map_Index(entry_size))) +__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_copy_entry :: proc "contextless" (h: Map_Header, new, old: ^Map_Entry_Header) { mem_copy(new, old, h.entry_size) } -__dynamic_map_erase :: proc "contextless" (using h: Map_Header, fr: Map_Find_Result) #no_bounds_check { +__dynamic_map_erase :: proc "contextless" (h: Map_Header, fr: Map_Find_Result) #no_bounds_check { if fr.entry_prev == MAP_SENTINEL { - m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next + h.m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next } else { prev := __dynamic_map_get_entry(h, fr.entry_prev) curr := __dynamic_map_get_entry(h, fr.entry_index) prev.next = curr.next } - last_index := Map_Index(m.entries.len-1) + last_index := Map_Index(h.m.entries.len-1) if fr.entry_index == last_index { // NOTE(bill): No need to do anything else, just pop } else { old := __dynamic_map_get_entry(h, fr.entry_index) end := __dynamic_map_get_entry(h, last_index) - __dynamic_map_copy_entry(h, old, end) + mem_copy(old, end, h.entry_size) old_hash: Map_Hash __get_map_hash_from_entry(h, old, &old_hash) @@ -423,9 +415,9 @@ __dynamic_map_erase :: proc "contextless" (using h: Map_Header, fr: Map_Find_Res last_entry := __dynamic_map_get_entry(h, last.entry_prev) last_entry.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 } From 9e3ea92829fe0f377220f38119e55c18420b58d0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 14:59:16 +0100 Subject: [PATCH 25/67] Inline many calls and delete unused procedures --- core/runtime/core_builtin.odin | 3 +- core/runtime/dynamic_map_internal.odin | 51 ++++++++++---------------- 2 files changed, 20 insertions(+), 34 deletions(-) diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index f65fd37d2..f8e39b8b2 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -296,7 +296,7 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) { @builtin reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) { if m != nil { - __dynamic_map_reserve(__get_map_header(m), capacity, loc) + __dynamic_map_reserve(__get_map_header(m), uint(capacity), loc) } } @@ -334,7 +334,6 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: __dynamic_map_erase(h, fr) } } - return } diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index dd18f6272..5dd3df001 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -214,7 +214,7 @@ __dynamic_map_reset_entries :: proc "contextless" (h: Map_Header, loc := #caller __get_map_hash_from_entry(h, entry_header, &entry_hash) entry_header.next = MAP_SENTINEL - fr := __dynamic_map_find(h, entry_hash) + fr := __dynamic_map_find(h, entry_hash.hash, entry_hash.key_ptr) if fr.entry_prev == MAP_SENTINEL { h.m.hashes[fr.hash_index] = i } else { @@ -224,7 +224,7 @@ __dynamic_map_reset_entries :: proc "contextless" (h: Map_Header, loc := #caller } } -__dynamic_map_reserve :: proc "odin" (h: Map_Header, cap: int, loc := #caller_location) { +__dynamic_map_reserve :: proc "odin" (h: Map_Header, cap: uint, loc := #caller_location) { c := context if h.m.entries.allocator.procedure != nil { c.allocator = h.m.entries.allocator @@ -234,12 +234,12 @@ __dynamic_map_reserve :: proc "odin" (h: Map_Header, cap: int, loc := #caller_lo cap := cap cap = ceil_to_pow2(cap) - __dynamic_array_reserve(&h.m.entries, h.entry_size, h.entry_align, cap, loc) + __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, cap*2, h.m.entries.allocator, loc) { + if __slice_resize(&h.m.hashes, int(cap*2), h.m.entries.allocator, loc) { __dynamic_map_reset_entries(h, loc) } } @@ -256,7 +256,7 @@ __dynamic_map_shrink :: proc "odin" (h: Map_Header, cap: int, loc := #caller_loc // USED INTERNALLY BY THE COMPILER __dynamic_map_get :: proc "contextless" (h: Map_Header, key_hash: uintptr, key_ptr: rawptr) -> rawptr { - index := __dynamic_map_find(h, {key_hash, key_ptr}).entry_index + 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) @@ -285,7 +285,7 @@ __dynamic_map_set :: proc "odin" (h: Map_Header, key_hash: uintptr, key_ptr: raw __dynamic_map_grow(h, loc) } - fr := __dynamic_map_find(h, {key_hash, key_ptr}) + fr := __dynamic_map_find(h, key_hash, key_ptr) if fr.entry_index != MAP_SENTINEL { index = fr.entry_index } else { @@ -318,13 +318,11 @@ __dynamic_map_set :: proc "odin" (h: Map_Header, key_hash: uintptr, key_ptr: raw @(private="file") -ceil_to_pow2 :: proc "contextless" (n: int) -> int { - n := n - if n <= 0 { - return 0 - } else if n <= 2 { +ceil_to_pow2 :: proc "contextless" (n: uint) -> uint { + if n <= 2 { return n } + n := n n -= 1 n |= n >> 1 n |= n >> 2 @@ -339,7 +337,7 @@ ceil_to_pow2 :: proc "contextless" (n: int) -> int { } __dynamic_map_grow :: proc "odin" (h: Map_Header, loc := #caller_location) { - new_count := max(h.m.entries.cap * 2, INITIAL_MAP_CAP) + new_count := max(uint(h.m.entries.cap) * 2, INITIAL_MAP_CAP) // Rehash through Reserve __dynamic_map_reserve(h, new_count, loc) } @@ -348,27 +346,19 @@ __dynamic_map_full :: #force_inline proc "contextless" (h: Map_Header) -> bool { return int(0.75 * f64(len(h.m.hashes))) <= h.m.entries.len } - -__map_find :: proc "contextless" (h: Map_Header, key_ptr: ^$K) -> Map_Find_Result #no_bounds_check { - hash := __get_map_key_hash(key_ptr) - return __dynamic_map_find(h, {hash, key_ptr}) -} - - -__dynamic_map_find :: proc "contextless" (h: Map_Header, hash: Map_Hash) -> Map_Find_Result #no_bounds_check { +__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(hash.hash & (n-1)) + 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: Map_Hash __get_map_hash_from_entry(h, entry, &entry_hash) - if entry_hash.hash == hash.hash && h.equal(entry_hash.key_ptr, hash.key_ptr) { + if entry_hash.hash == key_hash && h.equal(entry_hash.key_ptr, key_ptr) { return fr } - // assert(entry.next < m.entries.len) fr.entry_prev = fr.entry_index fr.entry_index = entry.next @@ -377,11 +367,10 @@ __dynamic_map_find :: proc "contextless" (h: Map_Header, hash: Map_Hash) -> Map_ return fr } -__dynamic_map_delete_key :: proc "contextless" (h: Map_Header, hash: Map_Hash) { - fr := __dynamic_map_find(h, hash) - if fr.entry_index != MAP_SENTINEL { - __dynamic_map_erase(h, fr) - } +// 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 __dynamic_map_find(h, hash, key_ptr) } __dynamic_map_get_entry :: #force_inline proc "contextless" (h: Map_Header, index: Map_Index) -> ^Map_Entry_Header { @@ -401,9 +390,7 @@ __dynamic_map_erase :: proc "contextless" (h: Map_Header, fr: Map_Find_Result) # prev.next = curr.next } last_index := Map_Index(h.m.entries.len-1) - if fr.entry_index == last_index { - // NOTE(bill): No need to do anything else, just pop - } else { + if fr.entry_index != last_index { old := __dynamic_map_get_entry(h, fr.entry_index) end := __dynamic_map_get_entry(h, last_index) mem_copy(old, end, h.entry_size) @@ -411,7 +398,7 @@ __dynamic_map_erase :: proc "contextless" (h: Map_Header, fr: Map_Find_Result) # old_hash: Map_Hash __get_map_hash_from_entry(h, old, &old_hash) - if last := __dynamic_map_find(h, old_hash); last.entry_prev != MAP_SENTINEL { + if last := __dynamic_map_find(h, old_hash.hash, old_hash.key_ptr); last.entry_prev != MAP_SENTINEL { last_entry := __dynamic_map_get_entry(h, last.entry_prev) last_entry.next = fr.entry_index } else { From cd484979a840a093967dcd7076e7cc39cb900096 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 15:09:39 +0100 Subject: [PATCH 26/67] General clean up minor with rearrangements and removing unneeded procedures --- core/runtime/dynamic_map_internal.odin | 57 ++++++++++++-------------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 5dd3df001..6ca9455ef 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -11,14 +11,13 @@ Map_Hash :: struct { key_ptr: rawptr, // address of Map_Entry_Header.key } -__get_map_key_hash :: proc "contextless" (k: ^$K) -> uintptr { +__get_map_key_hash :: #force_inline proc "contextless" (k: ^$K) -> uintptr { hasher := intrinsics.type_hasher_proc(K) 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) +__get_map_entry_key_ptr :: #force_inline proc "contextless" (h: Map_Header, entry: ^Map_Entry_Header) -> rawptr { + return rawptr(uintptr(entry) + h.key_offset) } Map_Index :: distinct uint @@ -210,16 +209,14 @@ __dynamic_map_reset_entries :: proc "contextless" (h: Map_Header, loc := #caller for i in 0.. bool { return int(0.75 * f64(len(h.m.hashes))) <= h.m.entries.len } +__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 { @@ -353,10 +356,8 @@ __dynamic_map_find :: proc "contextless" (h: Map_Header, key_hash: uintptr, key_ 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: Map_Hash - __get_map_hash_from_entry(h, entry, &entry_hash) - - if entry_hash.hash == key_hash && h.equal(entry_hash.key_ptr, key_ptr) { + entry_key_ptr := __get_map_entry_key_ptr(h, entry) + if entry.hash == key_hash && h.equal(entry_key_ptr, key_ptr) { return fr } @@ -370,24 +371,20 @@ __dynamic_map_find :: proc "contextless" (h: Map_Header, key_hash: uintptr, key_ // 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 __dynamic_map_find(h, hash, key_ptr) + return #force_inline __dynamic_map_find(h, hash, key_ptr) } __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_copy_entry :: proc "contextless" (h: Map_Header, new, old: ^Map_Entry_Header) { - mem_copy(new, old, h.entry_size) -} - __dynamic_map_erase :: proc "contextless" (h: Map_Header, fr: Map_Find_Result) #no_bounds_check { - if fr.entry_prev == MAP_SENTINEL { - h.m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next - } else { + 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 + } 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 { @@ -395,12 +392,10 @@ __dynamic_map_erase :: proc "contextless" (h: Map_Header, fr: Map_Find_Result) # end := __dynamic_map_get_entry(h, last_index) mem_copy(old, end, h.entry_size) - old_hash: Map_Hash - __get_map_hash_from_entry(h, old, &old_hash) - - if last := __dynamic_map_find(h, old_hash.hash, old_hash.key_ptr); last.entry_prev != MAP_SENTINEL { - 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 { h.m.hashes[last.hash_index] = fr.entry_index } From 756c1b7bcb8c881076594bf0ed73f64971e77f1b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 15:12:32 +0100 Subject: [PATCH 27/67] Correct slice/ptr.odin calls --- core/slice/ptr.odin | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) 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 + } } } } From 3fb69d59bb5d2ae5e74e6d8a8a2eb00059f4e0d7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 17:48:38 +0100 Subject: [PATCH 28/67] Minor correction to `__get_map_header` --- core/runtime/dynamic_map_internal.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 6ca9455ef..e63109f46 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -139,7 +139,7 @@ __get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { header := Map_Header{m = (^Raw_Map)(m)} Entry :: struct { hash: uintptr, - next: int, + next: Map_Index, key: K, value: V, } From 0092995f9d0a23ae9402a334706e2491a46d2ad9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 17:54:12 +0100 Subject: [PATCH 29/67] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4c69a3f43..7df38525b 100644 --- a/.gitignore +++ b/.gitignore @@ -283,3 +283,4 @@ shared/ *.sublime-workspace examples/bug/ build.sh +!core/debug/ \ No newline at end of file From 729ffeee0946a5b8124c7266ed7cd4696de22989 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 17 Sep 2022 18:45:33 +0100 Subject: [PATCH 30/67] Begin work on `core:debug/pe` --- core/debug/pe/pe.odin | 221 +++++++++++++++++++++++++++++++++++++ core/debug/pe/section.odin | 131 ++++++++++++++++++++++ core/debug/pe/symbol.odin | 108 ++++++++++++++++++ 3 files changed, 460 insertions(+) create mode 100644 core/debug/pe/pe.odin create mode 100644 core/debug/pe/section.odin create mode 100644 core/debug/pe/symbol.odin 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, +} From d6f84887ffea2ef0bb331a8e2f24c8ee15f774b4 Mon Sep 17 00:00:00 2001 From: "ray.garner" Date: Sun, 18 Sep 2022 21:11:07 +0900 Subject: [PATCH 31/67] Fix darwin libs for vendor stbi --- vendor/stb/image/stb_image.odin | 2 +- vendor/stb/image/stb_image_resize.odin | 2 +- vendor/stb/image/stb_image_write.odin | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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) From eb7a9c55b03efe0245eb4ccd1abd552edc99224d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 20 Sep 2022 22:47:53 +0100 Subject: [PATCH 32/67] Improve parapoly support for `^T` to `[^]$V` and vice versa --- src/check_expr.cpp | 12 ++++++++++++ src/types.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 891a9ebcb..196982084 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1143,6 +1143,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 +1159,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: diff --git a/src/types.cpp b/src/types.cpp index e917d30fa..5ebc8689f 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -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; From 5e9ff85fa89bbc3763cdbdb2fac17e8430852b46 Mon Sep 17 00:00:00 2001 From: Felipe Lavratti Date: Wed, 21 Sep 2022 00:50:34 +0100 Subject: [PATCH 33/67] Changed nightly build for linux to include the llvm library file --- .github/workflows/nightly.yml | 1 + .gitignore | 1 + build_odin.sh | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) 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 7df38525b..deccccbbd 100644 --- a/.gitignore +++ b/.gitignore @@ -271,6 +271,7 @@ odin odin.dSYM *.bin demo.bin +libLLVM*.so # shared collection shared/ 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() { From 3ff56e44050afdc1794ab34864682f76723682f9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Sep 2022 11:31:52 +0100 Subject: [PATCH 34/67] Correct `get_fullpath_relative` to remove all trailing path separators (`/` and `\`) --- src/build_settings.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index fc5a9d19f..f640bcced 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); From 5337413c5605655847767105f609d74a357984da Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Sep 2022 11:36:14 +0100 Subject: [PATCH 35/67] Temporary patch for `lb_gen_map_header` --- src/llvm_backend.cpp | 110 ++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 48 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 142ecc348..9594e9d86 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -500,59 +500,73 @@ 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 lb_gen_map_header_internal(lbProcedure *p, lbValue map_val_ptr, Type *map_type) { map_type = base_type(map_type); GB_ASSERT(map_type->kind == Type_Map); + lbAddr h = lb_add_local_generated(p, t_map_header, true); // all the values will be initialzed later + + Type *key_type = map_type->Map.key; + Type *val_type = map_type->Map.value; + gb_unused(val_type); + + 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 h; +} + + +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)); + GB_ASSERT(is_type_map(map_type)); + + + // TODO(bill): this is a temporary fix since this caching is not working other platforms + bool allow_caching = build_context.metrics.os == TargetOs_windows || is_arch_wasm(); + lbAddr h = {}; - lbAddr *found = map_get(&p->map_header_cache, map_val_ptr.value); - if (found != nullptr) { - h = *found; + if (!allow_caching) { + h = lb_gen_map_header_internal(p, map_val_ptr, map_type); } else { - h = lb_add_local_generated(p, t_map_header, false); // all the values will be initialzed later - - Type *key_type = map_type->Map.key; - Type *val_type = map_type->Map.value; - gb_unused(val_type); - - 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); - - - map_set(&p->map_header_cache, map_val_ptr.value, h); + lbAddr *found = map_get(&p->map_header_cache, map_val_ptr.value); + if (found != nullptr) { + h = *found; + } else { + h = lb_gen_map_header_internal(p, map_val_ptr, map_type); + map_set(&p->map_header_cache, map_val_ptr.value, h); + } } return lb_addr_load(p, h); From 1d793ea33810036d1c93f9cae666e91fa15ee22a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Sep 2022 12:09:05 +0100 Subject: [PATCH 36/67] Split header table data and the map pointer --- core/runtime/dynamic_map_internal.odin | 174 +++++++++++++------------ src/checker.cpp | 21 +-- src/llvm_backend.cpp | 53 +++++--- src/types.cpp | 1 + 4 files changed, 130 insertions(+), 119 deletions(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index e63109f46..35c005071 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -38,8 +38,7 @@ Map_Entry_Header :: struct { */ } -Map_Header :: struct { - m: ^Raw_Map, +Map_Header_Table :: struct { equal: Equal_Proc, entry_size: int, @@ -52,6 +51,95 @@ Map_Header :: struct { value_size: int, } +Map_Header :: struct { + m: ^Raw_Map, + using header_table: Map_Header_Table, +} + +// USED INTERNALLY BY THE COMPILER +__dynamic_map_get :: proc "contextless" (h: Map_Header, key_hash: uintptr, key_ptr: rawptr) -> rawptr { + 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" (h: Map_Header, 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 + } + + index := MAP_SENTINEL + + if len(h.m.hashes) == 0 { + __dynamic_map_reserve(h, 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" (h: Map_Header, cap: uint, loc := #caller_location) { + 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 { @@ -221,26 +309,6 @@ __dynamic_map_reset_entries :: proc "contextless" (h: Map_Header, loc := #caller } } -__dynamic_map_reserve :: proc "odin" (h: Map_Header, cap: uint, loc := #caller_location) { - 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) - } -} - __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 { @@ -251,68 +319,6 @@ __dynamic_map_shrink :: proc "odin" (h: Map_Header, cap: int, loc := #caller_loc return __dynamic_array_shrink(&h.m.entries, h.entry_size, h.entry_align, cap, loc) } -// USED INTERNALLY BY THE COMPILER -__dynamic_map_get :: proc "contextless" (h: Map_Header, key_hash: uintptr, key_ptr: rawptr) -> rawptr { - 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" (h: Map_Header, 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 - } - - index := MAP_SENTINEL - - if len(h.m.hashes) == 0 { - __dynamic_map_reserve(h, 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) -} - @(private="file") ceil_to_pow2 :: proc "contextless" (n: uint) -> uint { diff --git a/src/checker.cpp b/src/checker.cpp index cdc0630bf..e30dea6e1 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -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/llvm_backend.cpp b/src/llvm_backend.cpp index 9594e9d86..65e6f3eed 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -500,20 +500,11 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A return value; } -lbAddr lb_gen_map_header_internal(lbProcedure *p, lbValue map_val_ptr, Type *map_type) { +LLVMValueRef lb_gen_map_header_table_internal(lbModule *m, Type *map_type) { map_type = base_type(map_type); GB_ASSERT(map_type->kind == Type_Map); - lbAddr h = lb_add_local_generated(p, t_map_header, true); // all the values will be initialzed later - - Type *key_type = map_type->Map.key; - Type *val_type = map_type->Map.value; - gb_unused(val_type); - 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); @@ -524,18 +515,42 @@ lbAddr lb_gen_map_header_internal(lbProcedure *p, lbValue map_val_ptr, Type *map i64 value_size = type_size_of (map_type->Map.value); + 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; + + return llvm_const_named_struct(m, t_map_header_table, const_values, gb_count_of(const_values)); +} + + +lbAddr lb_gen_map_header_internal(lbProcedure *p, lbValue map_val_ptr, Type *map_type) { + map_type = base_type(map_type); + GB_ASSERT(map_type->kind == Type_Map); + + lbAddr h = lb_add_local_generated(p, t_map_header, true); // all the values will be initialzed later + + 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; + Type *map_header_base = base_type(t_map_header); - GB_ASSERT(map_header_base->Struct.fields.count == 8); + GB_ASSERT(map_header_base->Struct.fields.count == 2); Type *raw_map_ptr_type = map_header_base->Struct.fields[0]->type; - LLVMValueRef const_values[8] = {}; + LLVMValueRef const_values[2] = {}; 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; + const_values[1] = lb_gen_map_header_table_internal(p->module, map_type); 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); diff --git a/src/types.cpp b/src/types.cpp index 5ebc8689f..321f42aa0 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -688,6 +688,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; From 769d8dd038601d6e7ad0ddb445593e65c4596074 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Sep 2022 12:13:05 +0100 Subject: [PATCH 37/67] Simplify `__get_map_header` stuff --- core/runtime/dynamic_map_internal.odin | 32 +++++++++++++++++--------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 35c005071..5a683f234 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -16,7 +16,7 @@ __get_map_key_hash :: #force_inline proc "contextless" (k: ^$K) -> uintptr { return hasher(k, 0) } -__get_map_entry_key_ptr :: #force_inline proc "contextless" (h: Map_Header, entry: ^Map_Entry_Header) -> rawptr { +__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) } @@ -53,7 +53,7 @@ Map_Header_Table :: struct { Map_Header :: struct { m: ^Raw_Map, - using header_table: Map_Header_Table, + using table: Map_Header_Table, } // USED INTERNALLY BY THE COMPILER @@ -223,8 +223,19 @@ 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: Map_Index, @@ -243,18 +254,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 @@ -264,10 +273,11 @@ __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 "odin" (array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool { array := (^Raw_Slice)(array_) From ff97a731521dbeb9a6457a889f30f4fa69e080f7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Sep 2022 13:03:13 +0100 Subject: [PATCH 38/67] Reduce unnecessary map gets --- core/runtime/core_builtin.odin | 3 +- core/runtime/dynamic_map_internal.odin | 28 ++++++---- src/llvm_backend.cpp | 75 +++++++++++++++++++++----- src/llvm_backend.hpp | 7 ++- src/llvm_backend_expr.cpp | 22 +++----- src/llvm_backend_general.cpp | 20 ++----- src/llvm_backend_proc.cpp | 2 +- 7 files changed, 103 insertions(+), 54 deletions(-) diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index f8e39b8b2..568deed3b 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -296,7 +296,8 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) { @builtin reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) { if m != nil { - __dynamic_map_reserve(__get_map_header(m), uint(capacity), loc) + h := __get_map_header_table(T) + __dynamic_map_reserve(m, h, uint(capacity), loc) } } diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 5a683f234..011513162 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -57,17 +57,20 @@ Map_Header :: struct { } // USED INTERNALLY BY THE COMPILER -__dynamic_map_get :: proc "contextless" (h: Map_Header, key_hash: uintptr, key_ptr: rawptr) -> rawptr { - 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) +__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" (h: Map_Header, key_hash: uintptr, key_ptr: rawptr, value: rawptr, loc := #caller_location) -> ^Map_Entry_Header #no_bounds_check { +__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)) @@ -79,11 +82,14 @@ __dynamic_map_set :: proc "odin" (h: Map_Header, key_hash: uintptr, key_ptr: raw } return prev } + assert(condition = m != nil) + + h := Map_Header{(^Raw_Map)(m), table} index := MAP_SENTINEL if len(h.m.hashes) == 0 { - __dynamic_map_reserve(h, INITIAL_MAP_CAP, loc) + __dynamic_map_reserve(m, table, INITIAL_MAP_CAP, loc) __dynamic_map_grow(h, loc) } @@ -119,7 +125,11 @@ __dynamic_map_set :: proc "odin" (h: Map_Header, key_hash: uintptr, key_ptr: raw } // USED INTERNALLY BY THE COMPILER -__dynamic_map_reserve :: proc "odin" (h: Map_Header, cap: uint, loc := #caller_location) { +__dynamic_map_reserve :: proc "odin" (m: rawptr, table: Map_Header_Table, cap: uint, loc := #caller_location) { + assert(condition = m != nil) + + h := Map_Header{(^Raw_Map)(m), table} + c := context if h.m.entries.allocator.procedure != nil { c.allocator = h.m.entries.allocator @@ -352,7 +362,7 @@ ceil_to_pow2 :: proc "contextless" (n: uint) -> uint { __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, new_count, loc) + __dynamic_map_reserve(h.m, h.table, new_count, loc) } __dynamic_map_full :: #force_inline proc "contextless" (h: Map_Header) -> bool { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 65e6f3eed..6dbfb7331 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -500,10 +500,15 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A return value; } -LLVMValueRef lb_gen_map_header_table_internal(lbModule *m, Type *map_type) { +lbAddr lb_gen_map_header_table_internal(lbModule *m, Type *map_type) { map_type = base_type(map_type); GB_ASSERT(map_type->kind == Type_Map); + lbAddr *found = map_get(&m->map_header_table_map, map_type); + if (found) { + return *found; + } + GB_ASSERT(map_type->Map.entry_type->kind == Type_Struct); i64 entry_size = type_size_of (map_type->Map.entry_type); i64 entry_align = type_align_of (map_type->Map.entry_type); @@ -531,7 +536,18 @@ LLVMValueRef lb_gen_map_header_table_internal(lbModule *m, Type *map_type) { const_values[5] = lb_const_int(m, t_uintptr, value_offset).value; const_values[6] = lb_const_int(m, t_int, value_size) .value; - return llvm_const_named_struct(m, t_map_header_table, const_values, gb_count_of(const_values)); + 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); + LLVMValueRef global_data = addr.addr.value; + + LLVMSetLinkage(global_data, LLVMPrivateLinkage); + LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr); + LLVMSetGlobalConstant(global_data, true); + + map_set(&m->map_header_table_map, map_type, addr); + return addr; } @@ -550,7 +566,7 @@ lbAddr lb_gen_map_header_internal(lbProcedure *p, lbValue map_val_ptr, Type *map Type *raw_map_ptr_type = map_header_base->Struct.fields[0]->type; LLVMValueRef const_values[2] = {}; const_values[0] = LLVMConstNull(lb_type(p->module, raw_map_ptr_type)); - const_values[1] = lb_gen_map_header_table_internal(p->module, map_type); + const_values[1] = lb_addr_load(p, lb_gen_map_header_table_internal(p->module, map_type)).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); @@ -653,12 +669,30 @@ lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue 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) { + if (p->name == "main.map_type") { + gb_printf_err("HERE!\n"); + } + + 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_addr_load(p, lb_gen_map_header_table_internal(p->module, 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_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); @@ -666,15 +700,32 @@ void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *map_ lbAddr value_addr = lb_add_local_generated(p, v.type, false); lb_addr_store(p, value_addr, v); - auto args = array_make(permanent_allocator(), 5); - args[0] = h; - args[1] = key_hash; - args[2] = key_ptr; - args[3] = lb_emit_conv(p, value_addr.addr, t_rawptr); - args[4] = 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_addr_load(p, lb_gen_map_header_table_internal(p->module, 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_addr_load(p, lb_gen_map_header_table_internal(p->module, 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; diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index d622f3661..4447f9a0b 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 { @@ -446,7 +448,10 @@ lbValue lb_generate_local_array(lbProcedure *p, Type *elem_type, i64 count, bool 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_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue *key_ptr_); -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); +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 3ab73a27b..e8138d0a2 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -3670,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); } @@ -4130,20 +4128,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 55b09cbfc..ba1c501ad 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) { @@ -383,21 +386,6 @@ Type *lb_addr_type(lbAddr const &addr) { return type_deref(addr.addr.type); } -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 h = lb_gen_map_header(p, map_ptr, map_type); - - lbValue key_ptr = {}; - auto args = array_make(permanent_allocator(), 3); - args[0] = h; - args[1] = lb_gen_map_key_hash(p, key, map_type->Map.key, &key_ptr); - args[2] = 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)); -} - lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) { if (addr.addr.value == nullptr) { GB_PANIC("Illegal addr -> nullptr"); @@ -715,7 +703,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); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 17ed9c2a6..f2cbef5e3 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -889,7 +889,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 = {}; From 4f50988799d48aaf78c75130daca59a295efab19 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Sep 2022 13:03:30 +0100 Subject: [PATCH 39/67] Remove debug code --- src/llvm_backend.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 6dbfb7331..b340b9b7a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -670,10 +670,6 @@ lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue } lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue const &key) { - if (p->name == "main.map_type") { - gb_printf_err("HERE!\n"); - } - Type *map_type = base_type(type_deref(map_ptr.type)); lbValue key_ptr = {}; From 831620bfc4f1210900669ce501723a64f04c1807 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Sep 2022 13:06:02 +0100 Subject: [PATCH 40/67] Remove header cache code --- core/runtime/dynamic_map_internal.odin | 3 --- src/llvm_backend.cpp | 25 ------------------------- src/llvm_backend.hpp | 2 -- src/llvm_backend_proc.cpp | 4 ---- 4 files changed, 34 deletions(-) diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index 011513162..abe58fc5a 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -82,7 +82,6 @@ __dynamic_map_set :: proc "odin" (m: rawptr, table: Map_Header_Table, key_hash: } return prev } - assert(condition = m != nil) h := Map_Header{(^Raw_Map)(m), table} @@ -126,8 +125,6 @@ __dynamic_map_set :: proc "odin" (m: rawptr, table: Map_Header_Table, key_hash: // USED INTERNALLY BY THE COMPILER __dynamic_map_reserve :: proc "odin" (m: rawptr, table: Map_Header_Table, cap: uint, loc := #caller_location) { - assert(condition = m != nil) - h := Map_Header{(^Raw_Map)(m), table} c := context diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index b340b9b7a..adccb2869 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -578,31 +578,6 @@ lbAddr lb_gen_map_header_internal(lbProcedure *p, lbValue map_val_ptr, Type *map return h; } - -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)); - GB_ASSERT(is_type_map(map_type)); - - - // TODO(bill): this is a temporary fix since this caching is not working other platforms - bool allow_caching = build_context.metrics.os == TargetOs_windows || is_arch_wasm(); - - lbAddr h = {}; - if (!allow_caching) { - h = lb_gen_map_header_internal(p, map_val_ptr, map_type); - } else { - lbAddr *found = map_get(&p->map_header_cache, map_val_ptr.value); - if (found != nullptr) { - h = *found; - } else { - h = lb_gen_map_header_internal(p, map_val_ptr, map_type); - map_set(&p->map_header_cache, map_val_ptr.value, h); - } - } - - return lb_addr_load(p, h); -} - lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) { if (true) { return {}; diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 4447f9a0b..e69d3a6ed 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -310,7 +310,6 @@ struct lbProcedure { PtrMap selector_values; PtrMap selector_addr; - PtrMap map_header_cache; }; @@ -446,7 +445,6 @@ 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_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); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index f2cbef5e3..56ffe3fe9 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -123,7 +123,6 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) p->scope_stack.allocator = a; map_init(&p->selector_values, a, 0); map_init(&p->selector_addr, a, 0); - map_init(&p->map_header_cache, a, 0); if (p->is_foreign) { lb_add_foreign_library_path(p->module, entity->Procedure.foreign_library); @@ -380,9 +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"); } - - map_init(&p->map_header_cache, heap_allocator(), 0); - return p; } From 9b9aa9c3536924c53a350fa121ca921ec8dd4fdd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Sep 2022 13:08:40 +0100 Subject: [PATCH 41/67] Remove more dead code for map header stuff --- src/llvm_backend.cpp | 42 ++++++++---------------------------------- 1 file changed, 8 insertions(+), 34 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index adccb2869..8bee5c9e5 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -500,13 +500,15 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A return value; } -lbAddr lb_gen_map_header_table_internal(lbModule *m, Type *map_type) { +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); lbAddr *found = map_get(&m->map_header_table_map, map_type); if (found) { - return *found; + return lb_addr_load(p, *found); } GB_ASSERT(map_type->Map.entry_type->kind == Type_Struct); @@ -547,35 +549,7 @@ lbAddr lb_gen_map_header_table_internal(lbModule *m, Type *map_type) { LLVMSetGlobalConstant(global_data, true); map_set(&m->map_header_table_map, map_type, addr); - return addr; -} - - -lbAddr lb_gen_map_header_internal(lbProcedure *p, lbValue map_val_ptr, Type *map_type) { - map_type = base_type(map_type); - GB_ASSERT(map_type->kind == Type_Map); - - lbAddr h = lb_add_local_generated(p, t_map_header, true); // all the values will be initialzed later - - 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; - - Type *map_header_base = base_type(t_map_header); - GB_ASSERT(map_header_base->Struct.fields.count == 2); - Type *raw_map_ptr_type = map_header_base->Struct.fields[0]->type; - LLVMValueRef const_values[2] = {}; - const_values[0] = LLVMConstNull(lb_type(p->module, raw_map_ptr_type)); - const_values[1] = lb_addr_load(p, lb_gen_map_header_table_internal(p->module, map_type)).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 h; + return lb_addr_load(p, addr); } lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) { @@ -650,7 +624,7 @@ lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue key_ptr = {}; auto args = array_make(permanent_allocator(), 4); args[0] = lb_emit_conv(p, map_ptr, t_rawptr); - args[1] = lb_addr_load(p, lb_gen_map_header_table_internal(p->module, map_type)); + 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; @@ -673,7 +647,7 @@ void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbValue const &map_ptr, auto args = array_make(permanent_allocator(), 6); args[0] = lb_emit_conv(p, map_ptr, t_rawptr); - args[1] = lb_addr_load(p, lb_gen_map_header_table_internal(p->module, map_type)); + 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); @@ -691,7 +665,7 @@ void lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const auto args = array_make(permanent_allocator(), 4); args[0] = lb_emit_conv(p, map_ptr, t_rawptr); - args[1] = lb_addr_load(p, lb_gen_map_header_table_internal(p->module, type_deref(map_ptr.type))); + 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); From a6d3cbe824b3ab6849af3f1c4abb94d7a72ed801 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Sep 2022 13:10:06 +0100 Subject: [PATCH 42/67] Correct json.unmarshal for `map`s --- core/encoding/json/unmarshal.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index 85d3303c2..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) @@ -432,7 +432,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm key_ptr = &key_cstr } - set_ptr := runtime.__dynamic_map_set(header, key_hash, key_ptr, 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) } From 8ce1ce85ad33b9b37f0ba5c49448e188077c64eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20=28counter=29?= <49562770+awwdev@users.noreply.github.com> Date: Wed, 21 Sep 2022 16:03:52 +0200 Subject: [PATCH 43/67] removed do --- vendor/zlib/zlib.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/zlib/zlib.odin b/vendor/zlib/zlib.odin index c052429f5..f5357d28e 100644 --- a/vendor/zlib/zlib.odin +++ b/vendor/zlib/zlib.odin @@ -2,8 +2,8 @@ package zlib import "core:c" -when ODIN_OS == .Windows do foreign import zlib "libz.lib" -when ODIN_OS == .Linux do foreign import zlib "system:z" +when ODIN_OS == .Windows { foreign import zlib "libz.lib" } +when ODIN_OS == .Linux { foreign import zlib "system:z" } VERSION :: "1.2.12" VERNUM :: 0x12c0 @@ -259,4 +259,4 @@ gzgetc :: #force_inline proc(file: gzFile) -> c.int { return ch } return gzgetc_unique(file) -} \ No newline at end of file +} From 0ca8a5e1868b1d1b25beb828c380d71f583c6678 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 21 Sep 2022 16:45:01 +0200 Subject: [PATCH 44/67] Add vendor:zlib to examples/all; prefix vendor packages. --- examples/all/all_vendor.odin | 47 +++++++++++++++------------ vendor/OpenGL/constants.odin | 2 +- vendor/OpenGL/enums.odin | 2 +- vendor/OpenGL/helpers.odin | 2 +- vendor/OpenGL/impl.odin | 2 +- vendor/OpenGL/wrappers.odin | 2 +- vendor/botan/bindings/botan.odin | 2 +- vendor/botan/blake2b/blake2b.odin | 2 +- vendor/botan/gost/gost.odin | 2 +- vendor/botan/keccak/keccak.odin | 2 +- vendor/botan/md4/md4.odin | 2 +- vendor/botan/md5/md5.odin | 2 +- vendor/botan/ripemd/ripemd.odin | 2 +- vendor/botan/sha1/sha1.odin | 2 +- vendor/botan/sha2/sha2.odin | 2 +- vendor/botan/sha3/sha3.odin | 2 +- vendor/botan/shake/shake.odin | 2 +- vendor/botan/siphash/siphash.odin | 2 +- vendor/botan/skein512/skein512.odin | 2 +- vendor/botan/sm3/sm3.odin | 2 +- vendor/botan/streebog/streebog.odin | 2 +- vendor/botan/tiger/tiger.odin | 2 +- vendor/botan/whirlpool/whirlpool.odin | 2 +- vendor/commonmark/cmark.odin | 2 +- vendor/commonmark/doc.odin | 2 +- vendor/ggpo/ggpo.odin | 2 +- vendor/zlib/zlib.odin | 2 +- 27 files changed, 53 insertions(+), 46 deletions(-) diff --git a/examples/all/all_vendor.odin b/examples/all/all_vendor.odin index 7da2e501b..11fcfeac6 100644 --- a/examples/all/all_vendor.odin +++ b/examples/all/all_vendor.odin @@ -1,29 +1,32 @@ 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 cm "vendor:commonmark" +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 zlib "vendor:zlib" -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 +_ :: cm _ :: ENet _ :: ggpo _ :: gl @@ -33,12 +36,16 @@ _ :: miniaudio _ :: PM _ :: rl _ :: exr +_ :: zlib + _ :: SDL _ :: SDLNet _ :: IMG _ :: MIX _ :: TTF + _ :: vk + _ :: NS _ :: MTL -_ :: CA +_ :: CA \ 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/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/zlib/zlib.odin b/vendor/zlib/zlib.odin index f5357d28e..8a046a401 100644 --- a/vendor/zlib/zlib.odin +++ b/vendor/zlib/zlib.odin @@ -1,4 +1,4 @@ -package zlib +package vendor_zlib import "core:c" From 3455e5690ce8d09eda37bff8478dadae73160af1 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 21 Sep 2022 16:54:21 +0200 Subject: [PATCH 45/67] [examples/all] Make OS-specific for zlib, cmark --- examples/all/all_vendor.odin | 4 ---- examples/all/all_vendor_cmark.odin | 5 +++++ examples/all/all_vendor_zlib.odin | 5 +++++ 3 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 examples/all/all_vendor_cmark.odin create mode 100644 examples/all/all_vendor_zlib.odin diff --git a/examples/all/all_vendor.odin b/examples/all/all_vendor.odin index 11fcfeac6..22be12ef9 100644 --- a/examples/all/all_vendor.odin +++ b/examples/all/all_vendor.odin @@ -1,7 +1,6 @@ package all import botan "vendor:botan" -import cm "vendor:commonmark" import ENet "vendor:ENet" import ggpo "vendor:ggpo" import gl "vendor:OpenGL" @@ -11,7 +10,6 @@ import miniaudio "vendor:miniaudio" import PM "vendor:portmidi" import rl "vendor:raylib" import exr "vendor:OpenEXRCore" -import zlib "vendor:zlib" import SDL "vendor:sdl2" import SDLNet "vendor:sdl2/net" @@ -26,7 +24,6 @@ import MTL "vendor:darwin/Metal" import CA "vendor:darwin/QuartzCore" _ :: botan -_ :: cm _ :: ENet _ :: ggpo _ :: gl @@ -36,7 +33,6 @@ _ :: miniaudio _ :: PM _ :: rl _ :: exr -_ :: zlib _ :: SDL _ :: SDLNet 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 From d469c2da48594d1dfea99fea2a4bb1391809c055 Mon Sep 17 00:00:00 2001 From: Vitaly Kravchenko Date: Wed, 21 Sep 2022 17:31:37 +0100 Subject: [PATCH 46/67] Fix indent --- core/os/os.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/os/os.odin b/core/os/os.odin index 5e71e720e..1d8845da7 100644 --- a/core/os/os.odin +++ b/core/os/os.odin @@ -120,7 +120,7 @@ read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator) data = make([]byte, int(length), allocator) if data == nil { - return nil, false + return nil, false } bytes_read, read_err := read_full(fd, data) From 4b4c2a2abd71dff2dc865df815f25318700e0929 Mon Sep 17 00:00:00 2001 From: 13419596 <13419596@hotmail.com> Date: Wed, 21 Sep 2022 17:15:28 -0500 Subject: [PATCH 47/67] Correcting libc pow bindings Adding tests that libc pow(f) functions - have two arguments - behave as expected for simple inputs. --- core/c/libc/complex.odin | 4 +- tests/core/c/libc/test_core_libc.odin | 37 ++++++++ .../c/libc/test_core_libc_complex_pow.odin | 91 +++++++++++++++++++ 3 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 tests/core/c/libc/test_core_libc.odin create mode 100644 tests/core/c/libc/test_core_libc_complex_pow.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/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 From 0380601bb489d6488bbc0a3322a06889dca9bd02 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Sep 2022 23:47:33 +0100 Subject: [PATCH 48/67] Fix `map_insert` --- core/runtime/core_builtin.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index 568deed3b..574023a1e 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -672,10 +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) + h := __get_map_header(T) - data := uintptr(__dynamic_map_set(h, __get_map_key_hash(&key), &key, &value, loc)) - return (^V)(data + h.value_offset) + e := __dynamic_map_set(m, h, __get_map_key_hash(&key), &key, &value, loc) + return (^V)(uintptr(e) + h.value_offset) } From b7abacfa7ef6bbeb9434673b62f47a136ba34737 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 22 Sep 2022 01:01:40 +0200 Subject: [PATCH 49/67] Enable libc complex test. --- tests/core/Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 From 8c3f01fbfa73f707e9150b0dbe2629d7d5ac2f8d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Sep 2022 00:05:11 +0100 Subject: [PATCH 50/67] Correct parapoly determination of generated internal type of a `map` --- src/check_expr.cpp | 8 +++++++- src/check_type.cpp | 2 -- src/checker.cpp | 4 ++-- src/llvm_backend.cpp | 1 - src/llvm_backend_type.cpp | 2 +- src/llvm_backend_utility.cpp | 2 +- src/types.cpp | 1 - 7 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 196982084..3f4f11813 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1360,7 +1360,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; 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 e30dea6e1..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: diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 8bee5c9e5..e563fabb7 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -521,7 +521,6 @@ lbValue lb_gen_map_header_table_internal(lbProcedure *p, Type *map_type) { i64 value_offset = type_offset_of(map_type->Map.entry_type, 3); i64 value_size = type_size_of (map_type->Map.value); - Type *key_type = map_type->Map.key; Type *val_type = map_type->Map.value; gb_unused(val_type); diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index d424fa5b2..61955d934 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -787,7 +787,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/types.cpp b/src/types.cpp index 321f42aa0..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; \ }) \ From 0dce7769f4ff9d854f4429ac5e5f3c1ad00c50f6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Sep 2022 00:18:03 +0100 Subject: [PATCH 51/67] Clean up private internal constant global handling --- src/llvm_backend.cpp | 6 +----- src/llvm_backend_general.cpp | 34 ++++++++++++++++++---------------- src/llvm_backend_type.cpp | 2 ++ 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index e563fabb7..5502aaa58 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -541,11 +541,7 @@ lbValue lb_gen_map_header_table_internal(lbProcedure *p, Type *map_type) { lbValue res = {llvm_res, t_map_header_table}; lbAddr addr = lb_add_global_generated(m, t_map_header_table, res, nullptr); - LLVMValueRef global_data = addr.addr.value; - - LLVMSetLinkage(global_data, LLVMPrivateLinkage); - LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr); - LLVMSetGlobalConstant(global_data, true); + lb_make_global_private_const(addr); map_set(&m->map_header_table_map, map_type, addr); return lb_addr_load(p, addr); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index ba1c501ad..f05ae81ca 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -216,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)); @@ -918,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; } @@ -2460,10 +2467,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); @@ -2506,10 +2511,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) { @@ -2545,10 +2548,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; @@ -2656,6 +2657,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_type.cpp b/src/llvm_backend_type.cpp index 61955d934..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); From 3aea9a7c206f225af3bc327738f2df30d7fe0a58 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Sep 2022 00:30:10 +0100 Subject: [PATCH 52/67] Improve error messages for compile time known bounds checking --- src/check_expr.cpp | 12 ++++++++---- src/exact_value.cpp | 3 --- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 3f4f11813..1168a9ba3 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3922,7 +3922,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; @@ -3932,8 +3934,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; @@ -3964,7 +3967,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); @@ -3994,8 +3997,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; } 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; From dade5b5ef2ee5a35cbe2c5a3d524cbc3e84402f0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Sep 2022 00:34:36 +0100 Subject: [PATCH 53/67] Improve error message for `check_is_expressible` (Cannot convert X to Y from Z) --- src/check_expr.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 1168a9ba3..0a42c6618 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2053,15 +2053,18 @@ bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) { o->mode = Addressing_Invalid; ); + gbString s = exact_value_to_string(o->value); + defer (gb_string_free(s)); + 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(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(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s", a, b, c, s); check_assignment_error_suggestion(ctx, o, type); } return false; From 0ddf1bf6603b2ea647f8c71b1bb5e26faf3e0807 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Sep 2022 00:36:31 +0100 Subject: [PATCH 54/67] Minor style change --- src/check_expr.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 0a42c6618..83df0fa4e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2046,16 +2046,15 @@ 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); o->mode = Addressing_Invalid; ); - gbString s = exact_value_to_string(o->value); - defer (gb_string_free(s)); - 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', got %s", a, b, s); From b15968f14061ba41fe5e800e191901c28c270bbe Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Sep 2022 00:42:03 +0100 Subject: [PATCH 55/67] Improve suggestions for certain assignments --- src/check_expr.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 83df0fa4e..dea8574da 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, @@ -1983,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); } @@ -2059,10 +2069,12 @@ bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) { if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s', got %s", a, b, s); } else { + 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_BLOCK(); error(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s", a, b, c, s); check_assignment_error_suggestion(ctx, o, type); } From 6fe1825db9350813a41627273268dc41983eb1f7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Sep 2022 00:47:23 +0100 Subject: [PATCH 56/67] Improve error message for slicing an enumerated array --- src/check_expr.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index dea8574da..0686d9cb2 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -9106,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; From c056a0d108d70932f33c75fd7f9dd5cb82808e1f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Sep 2022 00:52:37 +0100 Subject: [PATCH 57/67] Add `slice.enumerated_array` --- core/slice/slice.odin | 7 +++++++ 1 file changed, 7 insertions(+) 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 From 532133d6485321f075db7bbcd5e26d5fd3ca3770 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Sep 2022 00:55:28 +0100 Subject: [PATCH 58/67] Minor technical improvement --- src/llvm_backend_expr.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index e8138d0a2..6c046fa4a 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -3717,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); From b426e8577bb04d365a031e198216d12ad99b3e0b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Sep 2022 01:09:18 +0100 Subject: [PATCH 59/67] `cap(Enum)` (equivalent to `max(Enum)-min(Enum)+1`) --- src/check_builtin.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 48cf73b58..c835801ac 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) { From 07d798c61a3d19c9b3c4ae66c6fa612a4d9de6bb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Sep 2022 01:17:58 +0100 Subject: [PATCH 60/67] Fix `libc.aligned_alloc` on Windows --- core/c/libc/stdlib.odin | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/c/libc/stdlib.odin b/core/c/libc/stdlib.odin index a278db0ef..68fb4c4dc 100644 --- a/core/c/libc/stdlib.odin +++ b/core/c/libc/stdlib.odin @@ -88,6 +88,7 @@ foreign libc { srand :: proc(seed: uint) --- // 7.22.3 Memory management functions + @(link_name="_aligned_malloc" when ODIN_OS == .Windows else "aligned_alloc") aligned_alloc :: proc(aligment, size: size_t) -> rawptr --- calloc :: proc(nmemb, size: size_t) -> rawptr --- free :: proc(ptr: rawptr) --- @@ -125,3 +126,15 @@ 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_free :: 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 From eb0d7465e2aa4d6119c9219930f9403951df16f0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Sep 2022 01:22:48 +0100 Subject: [PATCH 61/67] Fix `libc.aligned_alloc` for Windows (thanks Microsoft(!)) --- core/c/libc/stdlib.odin | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/core/c/libc/stdlib.odin b/core/c/libc/stdlib.odin index 68fb4c4dc..d797b8746 100644 --- a/core/c/libc/stdlib.odin +++ b/core/c/libc/stdlib.odin @@ -88,8 +88,6 @@ foreign libc { srand :: proc(seed: uint) --- // 7.22.3 Memory management functions - @(link_name="_aligned_malloc" when ODIN_OS == .Windows else "aligned_alloc") - 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 --- @@ -128,7 +126,22 @@ foreign libc { } -aligned_free :: proc "c" (ptr: rawptr) { +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) --- From 2ff61bdfc7315ad5cda0d049970cf3a5baf1d72f Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Wed, 21 Sep 2022 18:35:56 -0700 Subject: [PATCH 62/67] fix target features to make wasm intrinsics happy --- src/build_settings.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index f640bcced..02de22ec4 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1311,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); } @@ -1340,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++] = '"'; } From 6c8aad0afbfab55ed959712e1db4b6c368b4bead Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Sep 2022 15:17:36 +0100 Subject: [PATCH 63/67] Make `intrinsics.{count_ones, count_zeros, count_trailing_zeros, count_leading_zeros}` work at compile time --- src/check_builtin.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index c835801ac..09294c93a 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -3685,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; From 37a2356485d66bc777105e0520a1206713cbb86f Mon Sep 17 00:00:00 2001 From: Tetralux Date: Wed, 21 Sep 2022 19:01:33 +0000 Subject: [PATCH 64/67] [sys/windows] Add DCB structure, SetCommState, GetCommState These are the procedures for configuring a serial port. You simply open the port with CreateFile (os.open), followed by a call to GetCommState, setting the DCB as desired, followed by a SetCommState call. The DCB structure uses C bitfields, so a configuration struct is provided along with a helper procedure to make it easier to initialize in Odin code. This makes it possible to initialize a DCB structure with one call to the helper: ``` dcb: DCB windows.init_dcb_with_config(&dcb, { BaudRate = 115200, ByteSize = 8, Parity = .None, StopBits = .One, }) ``` (The parity and the stopbits are actually optional in this example, as their zero-values are None and One, respectively.) --- core/sys/windows/kernel32.odin | 144 +++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) 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 From 83ffb68bb7025752639b746597cdcfa463cc7074 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 23 Sep 2022 12:09:46 +0100 Subject: [PATCH 65/67] Fix typo in `map_insert` --- core/runtime/core_builtin.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index 574023a1e..b5b003122 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -672,7 +672,7 @@ 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(T) + 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) From 4d208dc0922b713bc7351bfe2e846e3ac957aff7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 27 Sep 2022 00:10:05 +0100 Subject: [PATCH 66/67] Override lbArgKind to be indirect for `#by_ptr` parameters --- src/llvm_backend_general.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index f05ae81ca..ed77eb975 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1507,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); @@ -1532,9 +1533,11 @@ 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)); + param_is_by_ptr = true; } else if (is_type_boolean(e_type) && type_size_of(e_type) <= 1) { param_type = LLVMInt1TypeInContext(m->ctx); @@ -1546,6 +1549,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; } } @@ -1571,6 +1575,13 @@ 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 + GB_ASSERT(ft->args[j].kind == lbArg_Direct); + 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); From 0fe006157e6e6f07722bcf4eb7c51a8db07d008c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 27 Sep 2022 00:18:19 +0100 Subject: [PATCH 67/67] Remove extra pointer indirection --- src/llvm_backend_general.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index ed77eb975..0dabee076 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1536,7 +1536,8 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *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) { @@ -1578,7 +1579,6 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) { for_array(j, ft->args) { if (params_by_ptr[j]) { // NOTE(bill): The parameter needs to be passed "indirectly", override it - GB_ASSERT(ft->args[j].kind == lbArg_Direct); ft->args[j].kind = lbArg_Indirect; } }